c5e438ec |
1 | /* |
2 | * SSH agent client code. |
3 | */ |
4 | |
5 | #include <stdio.h> |
6 | #include <stdlib.h> |
fbbb86aa |
7 | #include <assert.h> |
8 | #include <unistd.h> |
9 | #include <sys/socket.h> |
10 | #include <sys/un.h> |
89e97516 |
11 | #include <fcntl.h> |
c5e438ec |
12 | |
839f10db |
13 | #include "putty.h" |
c5e438ec |
14 | #include "misc.h" |
839f10db |
15 | #include "tree234.h" |
c5e438ec |
16 | #include "puttymem.h" |
17 | |
c5e438ec |
18 | int agent_exists(void) |
19 | { |
2527703c |
20 | const char *p = getenv("SSH_AUTH_SOCK"); |
21 | if (p && *p) |
fbbb86aa |
22 | return TRUE; |
23 | return FALSE; |
c5e438ec |
24 | } |
25 | |
839f10db |
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) |
c5e438ec |
109 | { |
fbbb86aa |
110 | char *name; |
111 | int sock; |
112 | struct sockaddr_un addr; |
113 | int done; |
839f10db |
114 | struct agent_connection *conn; |
fbbb86aa |
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 | |
db9b7dce |
126 | cloexec(sock); |
89e97516 |
127 | |
fbbb86aa |
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 | |
839f10db |
144 | if (!agent_connections) |
145 | agent_connections = newtree234(agent_conncmp); |
fbbb86aa |
146 | |
839f10db |
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); |
fbbb86aa |
155 | |
839f10db |
156 | uxsel_set(sock, 1, agent_select_result); |
157 | return 0; |
fbbb86aa |
158 | |
159 | failure: |
160 | *out = NULL; |
161 | *outlen = 0; |
839f10db |
162 | return 1; |
c5e438ec |
163 | } |