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