]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/uxagentc.c
Unify GET_32BIT()/PUT_32BIT() et al from numerous source files into misc.h.
[PuTTY.git] / unix / uxagentc.c
1 /*
2  * SSH agent client code.
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <assert.h>
8 #include <unistd.h>
9 #include <sys/socket.h>
10 #include <sys/un.h>
11
12 #include "putty.h"
13 #include "misc.h"
14 #include "tree234.h"
15 #include "puttymem.h"
16
17 int agent_exists(void)
18 {
19     if (getenv("SSH_AUTH_SOCK") != NULL)
20         return TRUE;
21     return FALSE;
22 }
23
24 static tree234 *agent_connections;
25 struct agent_connection {
26     int fd;
27     char *retbuf;
28     char sizebuf[4];
29     int retsize, retlen;
30     void (*callback)(void *, void *, int);
31     void *callback_ctx;
32 };
33 static int agent_conncmp(void *av, void *bv)
34 {
35     struct agent_connection *a = (struct agent_connection *) av;
36     struct agent_connection *b = (struct agent_connection *) bv;
37     if (a->fd < b->fd)
38         return -1;
39     if (a->fd > b->fd)
40         return +1;
41     return 0;
42 }
43 static int agent_connfind(void *av, void *bv)
44 {
45     int afd = *(int *) av;
46     struct agent_connection *b = (struct agent_connection *) bv;
47     if (afd < b->fd)
48         return -1;
49     if (afd > b->fd)
50         return +1;
51     return 0;
52 }
53
54 static int agent_select_result(int fd, int event)
55 {
56     int ret;
57     struct agent_connection *conn;
58
59     assert(event == 1);                /* not selecting for anything but R */
60
61     conn = find234(agent_connections, &fd, agent_connfind);
62     if (!conn) {
63         uxsel_del(fd);
64         return 1;
65     }
66
67     ret = read(fd, conn->retbuf+conn->retlen, conn->retsize-conn->retlen);
68     if (ret <= 0) {
69         if (conn->retbuf != conn->sizebuf) sfree(conn->retbuf);
70         conn->retbuf = NULL;
71         conn->retlen = 0;
72         goto done;
73     }
74     conn->retlen += ret;
75     if (conn->retsize == 4 && conn->retlen == 4) {
76         conn->retsize = GET_32BIT(conn->retbuf);
77         if (conn->retsize <= 0) {
78             conn->retbuf = NULL;
79             conn->retlen = 0;
80             goto done;
81         }
82         conn->retsize += 4;
83         assert(conn->retbuf == conn->sizebuf);
84         conn->retbuf = snewn(conn->retsize, char);
85         memcpy(conn->retbuf, conn->sizebuf, 4);
86     }
87
88     if (conn->retlen < conn->retsize)
89         return 0;                      /* more data to come */
90
91     done:
92     /*
93      * We have now completed the agent query. Do the callback, and
94      * clean up. (Of course we don't free retbuf, since ownership
95      * of that passes to the callback.)
96      */
97     conn->callback(conn->callback_ctx, conn->retbuf, conn->retlen);
98     uxsel_del(fd);
99     close(fd);
100     del234(agent_connections, conn);
101     sfree(conn);
102     return 0;
103 }
104
105 int agent_query(void *in, int inlen, void **out, int *outlen,
106                 void (*callback)(void *, void *, int), void *callback_ctx)
107 {
108     char *name;
109     int sock;
110     struct sockaddr_un addr;
111     int done;
112     struct agent_connection *conn;
113
114     name = getenv("SSH_AUTH_SOCK");
115     if (!name)
116         goto failure;
117
118     sock = socket(PF_UNIX, SOCK_STREAM, 0);
119     if (sock < 0) {
120         perror("socket(PF_UNIX)");
121         exit(1);
122     }
123
124     addr.sun_family = AF_UNIX;
125     strncpy(addr.sun_path, name, sizeof(addr.sun_path));
126     if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
127         close(sock);
128         goto failure;
129     }
130
131     for (done = 0; done < inlen ;) {
132         int ret = write(sock, (char *)in + done, inlen - done);
133         if (ret <= 0) {
134             close(sock);
135             goto failure;
136         }
137         done += ret;
138     }
139
140     if (!agent_connections)
141         agent_connections = newtree234(agent_conncmp);
142
143     conn = snew(struct agent_connection);
144     conn->fd = sock;
145     conn->retbuf = conn->sizebuf;
146     conn->retsize = 4;
147     conn->retlen = 0;
148     conn->callback = callback;
149     conn->callback_ctx = callback_ctx;
150     add234(agent_connections, conn);
151
152     uxsel_set(sock, 1, agent_select_result);
153     return 0;
154
155     failure:
156     *out = NULL;
157     *outlen = 0;
158     return 1;
159 }