b612f52f1411cab00c06e94dd3a759d8c7f49814
[u/mdw/putty] / 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 }