Mercurial > dropbear
comparison cli-agentfwd.c @ 579:8c737cd7c1af
merge of '48fdaa8706d1acda35e9d564adc9a1fbc96c18c8'
and '658fd03abd21e0da7c4c89b9fff9dc693c72daae'
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sat, 27 Feb 2010 11:53:18 +0000 |
parents | 12fc5b342dc0 |
children | 3c5f631358a0 |
comparison
equal
deleted
inserted
replaced
577:69e98c45db7c | 579:8c737cd7c1af |
---|---|
1 /* | |
2 * Dropbear - a SSH2 server | |
3 * | |
4 * Copyright (c) 2005 Matt Johnston | |
5 * All rights reserved. | |
6 * | |
7 * Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 * of this software and associated documentation files (the "Software"), to deal | |
9 * in the Software without restriction, including without limitation the rights | |
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 * copies of the Software, and to permit persons to whom the Software is | |
12 * furnished to do so, subject to the following conditions: | |
13 * | |
14 * The above copyright notice and this permission notice shall be included in | |
15 * all copies or substantial portions of the Software. | |
16 * | |
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
23 * SOFTWARE. */ | |
24 | |
25 #include "includes.h" | |
26 | |
27 #ifdef ENABLE_CLI_AGENTFWD | |
28 | |
29 #include "agentfwd.h" | |
30 #include "session.h" | |
31 #include "ssh.h" | |
32 #include "dbutil.h" | |
33 #include "chansession.h" | |
34 #include "channel.h" | |
35 #include "packet.h" | |
36 #include "buffer.h" | |
37 #include "random.h" | |
38 #include "listener.h" | |
39 #include "runopts.h" | |
40 #include "atomicio.h" | |
41 #include "signkey.h" | |
42 #include "auth.h" | |
43 | |
44 /* The protocol implemented to talk to OpenSSH's SSH2 agent is documented in | |
45 PROTOCOL.agent in recent OpenSSH source distributions (5.1p1 has it). */ | |
46 | |
47 static int new_agent_chan(struct Channel * channel); | |
48 | |
49 const struct ChanType cli_chan_agent = { | |
50 0, /* sepfds */ | |
51 "[email protected]", | |
52 new_agent_chan, | |
53 NULL, | |
54 NULL, | |
55 NULL | |
56 }; | |
57 | |
58 static int connect_agent() { | |
59 | |
60 int fd = -1; | |
61 char* agent_sock = NULL; | |
62 | |
63 agent_sock = getenv("SSH_AUTH_SOCK"); | |
64 if (agent_sock == NULL) | |
65 return -1; | |
66 | |
67 fd = connect_unix(agent_sock); | |
68 | |
69 if (fd < 0) { | |
70 dropbear_log(LOG_INFO, "Failed to connect to agent"); | |
71 } | |
72 | |
73 return fd; | |
74 } | |
75 | |
76 // handle a request for a connection to the locally running ssh-agent | |
77 // or forward. | |
78 static int new_agent_chan(struct Channel * channel) { | |
79 | |
80 int fd = -1; | |
81 | |
82 if (!cli_opts.agent_fwd) | |
83 return SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; | |
84 | |
85 fd = connect_agent(); | |
86 if (cli_opts.agent_fd < 0) { | |
87 return SSH_OPEN_CONNECT_FAILED; | |
88 } | |
89 | |
90 setnonblocking(fd); | |
91 | |
92 ses.maxfd = MAX(ses.maxfd, fd); | |
93 | |
94 channel->readfd = fd; | |
95 channel->writefd = fd; | |
96 | |
97 // success | |
98 return 0; | |
99 } | |
100 | |
101 /* Sends a request to the agent, returning a newly allocated buffer | |
102 * with the response */ | |
103 /* This function will block waiting for a response - it will | |
104 * only be used by client authentication (not for forwarded requests) | |
105 * won't cause problems for interactivity. */ | |
106 /* Packet format (from draft-ylonen) | |
107 4 bytes Length, msb first. Does not include length itself. | |
108 1 byte Packet type. The value 255 is reserved for future extensions. | |
109 data Any data, depending on packet type. Encoding as in the ssh packet | |
110 protocol. | |
111 */ | |
112 static buffer * agent_request(unsigned char type, buffer *data) { | |
113 | |
114 buffer * payload = NULL; | |
115 buffer * inbuf = NULL; | |
116 size_t readlen = 0; | |
117 ssize_t ret; | |
118 const int fd = cli_opts.agent_fd; | |
119 unsigned int data_len = 0; | |
120 if (data) | |
121 { | |
122 data_len = data->len; | |
123 } | |
124 | |
125 payload = buf_new(4 + 1 + data_len); | |
126 | |
127 buf_putint(payload, 1 + data_len); | |
128 buf_putbyte(payload, type); | |
129 if (data) { | |
130 buf_putbytes(payload, data->data, data->len); | |
131 } | |
132 buf_setpos(payload, 0); | |
133 | |
134 ret = atomicio(write, fd, buf_getptr(payload, payload->len), payload->len); | |
135 if ((size_t)ret != payload->len) { | |
136 TRACE(("write failed fd %d for agent_request, %s", fd, strerror(errno))) | |
137 goto out; | |
138 } | |
139 | |
140 buf_free(payload); | |
141 payload = NULL; | |
142 TRACE(("Wrote out bytes for agent_request")) | |
143 /* Now we read the response */ | |
144 inbuf = buf_new(4); | |
145 ret = atomicio(read, fd, buf_getwriteptr(inbuf, 4), 4); | |
146 if (ret != 4) { | |
147 TRACE(("read of length failed for agent_request")) | |
148 goto out; | |
149 } | |
150 buf_setpos(inbuf, 0); | |
151 buf_setlen(inbuf, ret); | |
152 | |
153 readlen = buf_getint(inbuf); | |
154 if (readlen > MAX_AGENT_REPLY) { | |
155 TRACE(("agent reply is too big")); | |
156 goto out; | |
157 } | |
158 | |
159 TRACE(("agent_request readlen is %d", readlen)) | |
160 | |
161 buf_resize(inbuf, readlen); | |
162 buf_setpos(inbuf, 0); | |
163 ret = atomicio(read, fd, buf_getwriteptr(inbuf, readlen), readlen); | |
164 if ((size_t)ret != readlen) { | |
165 TRACE(("read of data failed for agent_request")) | |
166 goto out; | |
167 } | |
168 buf_incrwritepos(inbuf, readlen); | |
169 buf_setpos(inbuf, 0); | |
170 TRACE(("agent_request success, length %d", readlen)) | |
171 | |
172 out: | |
173 if (payload) | |
174 buf_free(payload); | |
175 | |
176 return inbuf; | |
177 } | |
178 | |
179 static void agent_get_key_list(m_list * ret_list) | |
180 { | |
181 buffer * inbuf = NULL; | |
182 unsigned int num = 0; | |
183 unsigned char packet_type; | |
184 unsigned int i; | |
185 int ret; | |
186 | |
187 inbuf = agent_request(SSH2_AGENTC_REQUEST_IDENTITIES, NULL); | |
188 if (!inbuf) { | |
189 TRACE(("agent_request failed returning identities")) | |
190 goto out; | |
191 } | |
192 | |
193 /* The reply has a format of: | |
194 byte SSH2_AGENT_IDENTITIES_ANSWER | |
195 uint32 num_keys | |
196 Followed by zero or more consecutive keys, encoded as: | |
197 string key_blob | |
198 string key_comment | |
199 */ | |
200 packet_type = buf_getbyte(inbuf); | |
201 if (packet_type != SSH2_AGENT_IDENTITIES_ANSWER) { | |
202 goto out; | |
203 } | |
204 | |
205 num = buf_getint(inbuf); | |
206 for (i = 0; i < num; i++) { | |
207 sign_key * pubkey = NULL; | |
208 int key_type = DROPBEAR_SIGNKEY_ANY; | |
209 buffer * key_buf; | |
210 | |
211 /* each public key is encoded as a string */ | |
212 key_buf = buf_getstringbuf(inbuf); | |
213 pubkey = new_sign_key(); | |
214 ret = buf_get_pub_key(key_buf, pubkey, &key_type); | |
215 buf_free(key_buf); | |
216 if (ret != DROPBEAR_SUCCESS) { | |
217 /* This is slack, properly would cleanup vars etc */ | |
218 dropbear_exit("Bad pubkey received from agent"); | |
219 } | |
220 pubkey->type = key_type; | |
221 pubkey->source = SIGNKEY_SOURCE_AGENT; | |
222 | |
223 list_append(ret_list, pubkey); | |
224 | |
225 /* We'll ignore the comment for now. might want it later.*/ | |
226 buf_eatstring(inbuf); | |
227 } | |
228 | |
229 out: | |
230 if (inbuf) { | |
231 buf_free(inbuf); | |
232 inbuf = NULL; | |
233 } | |
234 } | |
235 | |
236 void cli_setup_agent(struct Channel *channel) { | |
237 if (!getenv("SSH_AUTH_SOCK")) { | |
238 return; | |
239 } | |
240 | |
241 cli_start_send_channel_request(channel, "[email protected]"); | |
242 /* Don't want replies */ | |
243 buf_putbyte(ses.writepayload, 0); | |
244 encrypt_packet(); | |
245 } | |
246 | |
247 /* Returned keys are prepended to ret_list, which will | |
248 be updated. */ | |
249 void cli_load_agent_keys(m_list *ret_list) { | |
250 /* agent_fd will be closed after successful auth */ | |
251 cli_opts.agent_fd = connect_agent(); | |
252 if (cli_opts.agent_fd < 0) { | |
253 return; | |
254 } | |
255 | |
256 agent_get_key_list(ret_list); | |
257 } | |
258 | |
259 void agent_buf_sign(buffer *sigblob, sign_key *key, | |
260 const unsigned char *data, unsigned int len) { | |
261 buffer *request_data = buf_new(MAX_PUBKEY_SIZE + len + 12); | |
262 buffer *response; | |
263 unsigned int keylen, siglen; | |
264 int packet_type; | |
265 | |
266 /* Request format | |
267 byte SSH2_AGENTC_SIGN_REQUEST | |
268 string key_blob | |
269 string data | |
270 uint32 flags | |
271 */ | |
272 /* We write the key, then figure how long it was and write that */ | |
273 //buf_putint(request_data, 0); | |
274 buf_put_pub_key(request_data, key, key->type); | |
275 keylen = request_data->len - 4; | |
276 //buf_setpos(request_data, 0); | |
277 //buf_putint(request_data, keylen); | |
278 | |
279 //buf_setpos(request_data, request_data->len); | |
280 buf_putstring(request_data, data, len); | |
281 buf_putint(request_data, 0); | |
282 | |
283 response = agent_request(SSH2_AGENTC_SIGN_REQUEST, request_data); | |
284 buf_free(request_data); | |
285 | |
286 if (!response) { | |
287 goto fail; | |
288 } | |
289 | |
290 packet_type = buf_getbyte(response); | |
291 if (packet_type != SSH2_AGENT_SIGN_RESPONSE) { | |
292 goto fail; | |
293 } | |
294 | |
295 /* Response format | |
296 byte SSH2_AGENT_SIGN_RESPONSE | |
297 string signature_blob | |
298 */ | |
299 siglen = buf_getint(response); | |
300 buf_putbytes(sigblob, buf_getptr(response, siglen), siglen); | |
301 buf_free(response); | |
302 | |
303 return; | |
304 fail: | |
305 /* XXX don't fail badly here. instead propagate a failure code back up to | |
306 the cli auth pubkey code, and just remove this key from the list of | |
307 ones to try. */ | |
308 dropbear_exit("Agent failed signing key"); | |
309 } | |
310 | |
311 #endif |