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