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