diff options
author | Damien Miller <djm@mindrot.org> | 1999-10-27 13:42:43 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 1999-10-27 13:42:43 +1000 |
commit | d4a8b7e34dd619a4debf9a206c81db26d1402ea6 (patch) | |
tree | a47d770a2f790f40d18b0982d4e55fa7cfb1fa3b /authfd.c |
Initial revision
Diffstat (limited to 'authfd.c')
-rw-r--r-- | authfd.c | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/authfd.c b/authfd.c new file mode 100644 index 000000000..07893caf3 --- /dev/null +++ b/authfd.c | |||
@@ -0,0 +1,565 @@ | |||
1 | /* | ||
2 | |||
3 | authfd.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Mar 29 01:30:28 1995 ylo | ||
11 | |||
12 | Functions for connecting the local authentication agent. | ||
13 | |||
14 | */ | ||
15 | |||
16 | #include "includes.h" | ||
17 | RCSID("$Id: authfd.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | ||
18 | |||
19 | #include "ssh.h" | ||
20 | #include "rsa.h" | ||
21 | #include "authfd.h" | ||
22 | #include "buffer.h" | ||
23 | #include "bufaux.h" | ||
24 | #include "xmalloc.h" | ||
25 | #include "getput.h" | ||
26 | |||
27 | #include <openssl/rsa.h> | ||
28 | |||
29 | /* Returns the number of the authentication fd, or -1 if there is none. */ | ||
30 | |||
31 | int | ||
32 | ssh_get_authentication_socket() | ||
33 | { | ||
34 | const char *authsocket; | ||
35 | int sock; | ||
36 | struct sockaddr_un sunaddr; | ||
37 | |||
38 | authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); | ||
39 | if (!authsocket) | ||
40 | return -1; | ||
41 | |||
42 | sunaddr.sun_family = AF_UNIX; | ||
43 | strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); | ||
44 | |||
45 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | ||
46 | if (sock < 0) | ||
47 | return -1; | ||
48 | |||
49 | if (connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) | ||
50 | { | ||
51 | close(sock); | ||
52 | return -1; | ||
53 | } | ||
54 | |||
55 | return sock; | ||
56 | } | ||
57 | |||
58 | /* Closes the agent socket if it should be closed (depends on how it was | ||
59 | obtained). The argument must have been returned by | ||
60 | ssh_get_authentication_socket(). */ | ||
61 | |||
62 | void ssh_close_authentication_socket(int sock) | ||
63 | { | ||
64 | if (getenv(SSH_AUTHSOCKET_ENV_NAME)) | ||
65 | close(sock); | ||
66 | } | ||
67 | |||
68 | /* Opens and connects a private socket for communication with the | ||
69 | authentication agent. Returns the file descriptor (which must be | ||
70 | shut down and closed by the caller when no longer needed). | ||
71 | Returns NULL if an error occurred and the connection could not be | ||
72 | opened. */ | ||
73 | |||
74 | AuthenticationConnection *ssh_get_authentication_connection() | ||
75 | { | ||
76 | AuthenticationConnection *auth; | ||
77 | int sock; | ||
78 | |||
79 | sock = ssh_get_authentication_socket(); | ||
80 | |||
81 | /* Fail if we couldn't obtain a connection. This happens if we exited | ||
82 | due to a timeout. */ | ||
83 | if (sock < 0) | ||
84 | return NULL; | ||
85 | |||
86 | /* Applocate the connection structure and initialize it. */ | ||
87 | auth = xmalloc(sizeof(*auth)); | ||
88 | auth->fd = sock; | ||
89 | buffer_init(&auth->packet); | ||
90 | buffer_init(&auth->identities); | ||
91 | auth->howmany = 0; | ||
92 | |||
93 | return auth; | ||
94 | } | ||
95 | |||
96 | /* Closes the connection to the authentication agent and frees any associated | ||
97 | memory. */ | ||
98 | |||
99 | void ssh_close_authentication_connection(AuthenticationConnection *ac) | ||
100 | { | ||
101 | buffer_free(&ac->packet); | ||
102 | buffer_free(&ac->identities); | ||
103 | close(ac->fd); | ||
104 | /* Free the connection data structure. */ | ||
105 | xfree(ac); | ||
106 | } | ||
107 | |||
108 | /* Returns the first authentication identity held by the agent. | ||
109 | Returns true if an identity is available, 0 otherwise. | ||
110 | The caller must initialize the integers before the call, and free the | ||
111 | comment after a successful call (before calling ssh_get_next_identity). */ | ||
112 | |||
113 | int | ||
114 | ssh_get_first_identity(AuthenticationConnection *auth, | ||
115 | int *bitsp, BIGNUM *e, BIGNUM *n, char **comment) | ||
116 | { | ||
117 | unsigned char msg[8192]; | ||
118 | int len, l; | ||
119 | |||
120 | /* Send a message to the agent requesting for a list of the identities | ||
121 | it can represent. */ | ||
122 | msg[0] = 0; | ||
123 | msg[1] = 0; | ||
124 | msg[2] = 0; | ||
125 | msg[3] = 1; | ||
126 | msg[4] = SSH_AGENTC_REQUEST_RSA_IDENTITIES; | ||
127 | if (write(auth->fd, msg, 5) != 5) | ||
128 | { | ||
129 | error("write auth->fd: %.100s", strerror(errno)); | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | /* Read the length of the response. XXX implement timeouts here. */ | ||
134 | len = 4; | ||
135 | while (len > 0) | ||
136 | { | ||
137 | l = read(auth->fd, msg + 4 - len, len); | ||
138 | if (l <= 0) | ||
139 | { | ||
140 | error("read auth->fd: %.100s", strerror(errno)); | ||
141 | return 0; | ||
142 | } | ||
143 | len -= l; | ||
144 | } | ||
145 | |||
146 | /* Extract the length, and check it for sanity. (We cannot trust | ||
147 | authentication agents). */ | ||
148 | len = GET_32BIT(msg); | ||
149 | if (len < 1 || len > 256*1024) | ||
150 | fatal("Authentication reply message too long: %d\n", len); | ||
151 | |||
152 | /* Read the packet itself. */ | ||
153 | buffer_clear(&auth->identities); | ||
154 | while (len > 0) | ||
155 | { | ||
156 | l = len; | ||
157 | if (l > sizeof(msg)) | ||
158 | l = sizeof(msg); | ||
159 | l = read(auth->fd, msg, l); | ||
160 | if (l <= 0) | ||
161 | fatal("Incomplete authentication reply."); | ||
162 | buffer_append(&auth->identities, (char *)msg, l); | ||
163 | len -= l; | ||
164 | } | ||
165 | |||
166 | /* Get message type, and verify that we got a proper answer. */ | ||
167 | buffer_get(&auth->identities, (char *)msg, 1); | ||
168 | if (msg[0] != SSH_AGENT_RSA_IDENTITIES_ANSWER) | ||
169 | fatal("Bad authentication reply message type: %d", msg[0]); | ||
170 | |||
171 | /* Get the number of entries in the response and check it for sanity. */ | ||
172 | auth->howmany = buffer_get_int(&auth->identities); | ||
173 | if (auth->howmany > 1024) | ||
174 | fatal("Too many identities in authentication reply: %d\n", auth->howmany); | ||
175 | |||
176 | /* Return the first entry (if any). */ | ||
177 | return ssh_get_next_identity(auth, bitsp, e, n, comment); | ||
178 | } | ||
179 | |||
180 | /* Returns the next authentication identity for the agent. Other functions | ||
181 | can be called between this and ssh_get_first_identity or two calls of this | ||
182 | function. This returns 0 if there are no more identities. The caller | ||
183 | must free comment after a successful return. */ | ||
184 | |||
185 | int | ||
186 | ssh_get_next_identity(AuthenticationConnection *auth, | ||
187 | int *bitsp, BIGNUM *e, BIGNUM *n, char **comment) | ||
188 | { | ||
189 | /* Return failure if no more entries. */ | ||
190 | if (auth->howmany <= 0) | ||
191 | return 0; | ||
192 | |||
193 | /* Get the next entry from the packet. These will abort with a fatal | ||
194 | error if the packet is too short or contains corrupt data. */ | ||
195 | *bitsp = buffer_get_int(&auth->identities); | ||
196 | buffer_get_bignum(&auth->identities, e); | ||
197 | buffer_get_bignum(&auth->identities, n); | ||
198 | *comment = buffer_get_string(&auth->identities, NULL); | ||
199 | |||
200 | /* Decrement the number of remaining entries. */ | ||
201 | auth->howmany--; | ||
202 | |||
203 | return 1; | ||
204 | } | ||
205 | |||
206 | /* Generates a random challenge, sends it to the agent, and waits for response | ||
207 | from the agent. Returns true (non-zero) if the agent gave the correct | ||
208 | answer, zero otherwise. Response type selects the style of response | ||
209 | desired, with 0 corresponding to protocol version 1.0 (no longer supported) | ||
210 | and 1 corresponding to protocol version 1.1. */ | ||
211 | |||
212 | int | ||
213 | ssh_decrypt_challenge(AuthenticationConnection *auth, | ||
214 | int bits, BIGNUM *e, BIGNUM *n, BIGNUM *challenge, | ||
215 | unsigned char session_id[16], | ||
216 | unsigned int response_type, | ||
217 | unsigned char response[16]) | ||
218 | { | ||
219 | Buffer buffer; | ||
220 | unsigned char buf[8192]; | ||
221 | int len, l, i; | ||
222 | |||
223 | /* Response type 0 is no longer supported. */ | ||
224 | if (response_type == 0) | ||
225 | fatal("Compatibility with ssh protocol version 1.0 no longer supported."); | ||
226 | |||
227 | /* Format a message to the agent. */ | ||
228 | buf[0] = SSH_AGENTC_RSA_CHALLENGE; | ||
229 | buffer_init(&buffer); | ||
230 | buffer_append(&buffer, (char *)buf, 1); | ||
231 | buffer_put_int(&buffer, bits); | ||
232 | buffer_put_bignum(&buffer, e); | ||
233 | buffer_put_bignum(&buffer, n); | ||
234 | buffer_put_bignum(&buffer, challenge); | ||
235 | buffer_append(&buffer, (char *)session_id, 16); | ||
236 | buffer_put_int(&buffer, response_type); | ||
237 | |||
238 | /* Get the length of the message, and format it in the buffer. */ | ||
239 | len = buffer_len(&buffer); | ||
240 | PUT_32BIT(buf, len); | ||
241 | |||
242 | /* Send the length and then the packet to the agent. */ | ||
243 | if (write(auth->fd, buf, 4) != 4 || | ||
244 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != | ||
245 | buffer_len(&buffer)) | ||
246 | { | ||
247 | error("Error writing to authentication socket."); | ||
248 | error_cleanup: | ||
249 | buffer_free(&buffer); | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | /* Wait for response from the agent. First read the length of the | ||
254 | response packet. */ | ||
255 | len = 4; | ||
256 | while (len > 0) | ||
257 | { | ||
258 | l = read(auth->fd, buf + 4 - len, len); | ||
259 | if (l <= 0) | ||
260 | { | ||
261 | error("Error reading response length from authentication socket."); | ||
262 | goto error_cleanup; | ||
263 | } | ||
264 | len -= l; | ||
265 | } | ||
266 | |||
267 | /* Extract the length, and check it for sanity. */ | ||
268 | len = GET_32BIT(buf); | ||
269 | if (len > 256*1024) | ||
270 | fatal("Authentication response too long: %d", len); | ||
271 | |||
272 | /* Read the rest of the response in tothe buffer. */ | ||
273 | buffer_clear(&buffer); | ||
274 | while (len > 0) | ||
275 | { | ||
276 | l = len; | ||
277 | if (l > sizeof(buf)) | ||
278 | l = sizeof(buf); | ||
279 | l = read(auth->fd, buf, l); | ||
280 | if (l <= 0) | ||
281 | { | ||
282 | error("Error reading response from authentication socket."); | ||
283 | goto error_cleanup; | ||
284 | } | ||
285 | buffer_append(&buffer, (char *)buf, l); | ||
286 | len -= l; | ||
287 | } | ||
288 | |||
289 | /* Get the type of the packet. */ | ||
290 | buffer_get(&buffer, (char *)buf, 1); | ||
291 | |||
292 | /* Check for agent failure message. */ | ||
293 | if (buf[0] == SSH_AGENT_FAILURE) | ||
294 | { | ||
295 | log("Agent admitted failure to authenticate using the key."); | ||
296 | goto error_cleanup; | ||
297 | } | ||
298 | |||
299 | /* Now it must be an authentication response packet. */ | ||
300 | if (buf[0] != SSH_AGENT_RSA_RESPONSE) | ||
301 | fatal("Bad authentication response: %d", buf[0]); | ||
302 | |||
303 | /* Get the response from the packet. This will abort with a fatal error | ||
304 | if the packet is corrupt. */ | ||
305 | for (i = 0; i < 16; i++) | ||
306 | response[i] = buffer_get_char(&buffer); | ||
307 | |||
308 | /* The buffer containing the packet is no longer needed. */ | ||
309 | buffer_free(&buffer); | ||
310 | |||
311 | /* Correct answer. */ | ||
312 | return 1; | ||
313 | } | ||
314 | |||
315 | /* Adds an identity to the authentication server. This call is not meant to | ||
316 | be used by normal applications. */ | ||
317 | |||
318 | int ssh_add_identity(AuthenticationConnection *auth, | ||
319 | RSA *key, const char *comment) | ||
320 | { | ||
321 | Buffer buffer; | ||
322 | unsigned char buf[8192]; | ||
323 | int len, l, type; | ||
324 | |||
325 | /* Format a message to the agent. */ | ||
326 | buffer_init(&buffer); | ||
327 | buffer_put_char(&buffer, SSH_AGENTC_ADD_RSA_IDENTITY); | ||
328 | buffer_put_int(&buffer, BN_num_bits(key->n)); | ||
329 | buffer_put_bignum(&buffer, key->n); | ||
330 | buffer_put_bignum(&buffer, key->e); | ||
331 | buffer_put_bignum(&buffer, key->d); | ||
332 | /* To keep within the protocol: p < q for ssh. in SSL p > q */ | ||
333 | buffer_put_bignum(&buffer, key->iqmp); /* ssh key->u */ | ||
334 | buffer_put_bignum(&buffer, key->q); /* ssh key->p, SSL key->q */ | ||
335 | buffer_put_bignum(&buffer, key->p); /* ssh key->q, SSL key->p */ | ||
336 | buffer_put_string(&buffer, comment, strlen(comment)); | ||
337 | |||
338 | /* Get the length of the message, and format it in the buffer. */ | ||
339 | len = buffer_len(&buffer); | ||
340 | PUT_32BIT(buf, len); | ||
341 | |||
342 | /* Send the length and then the packet to the agent. */ | ||
343 | if (write(auth->fd, buf, 4) != 4 || | ||
344 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != | ||
345 | buffer_len(&buffer)) | ||
346 | { | ||
347 | error("Error writing to authentication socket."); | ||
348 | error_cleanup: | ||
349 | buffer_free(&buffer); | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | /* Wait for response from the agent. First read the length of the | ||
354 | response packet. */ | ||
355 | len = 4; | ||
356 | while (len > 0) | ||
357 | { | ||
358 | l = read(auth->fd, buf + 4 - len, len); | ||
359 | if (l <= 0) | ||
360 | { | ||
361 | error("Error reading response length from authentication socket."); | ||
362 | goto error_cleanup; | ||
363 | } | ||
364 | len -= l; | ||
365 | } | ||
366 | |||
367 | /* Extract the length, and check it for sanity. */ | ||
368 | len = GET_32BIT(buf); | ||
369 | if (len > 256*1024) | ||
370 | fatal("Add identity response too long: %d", len); | ||
371 | |||
372 | /* Read the rest of the response in tothe buffer. */ | ||
373 | buffer_clear(&buffer); | ||
374 | while (len > 0) | ||
375 | { | ||
376 | l = len; | ||
377 | if (l > sizeof(buf)) | ||
378 | l = sizeof(buf); | ||
379 | l = read(auth->fd, buf, l); | ||
380 | if (l <= 0) | ||
381 | { | ||
382 | error("Error reading response from authentication socket."); | ||
383 | goto error_cleanup; | ||
384 | } | ||
385 | buffer_append(&buffer, (char *)buf, l); | ||
386 | len -= l; | ||
387 | } | ||
388 | |||
389 | /* Get the type of the packet. */ | ||
390 | type = buffer_get_char(&buffer); | ||
391 | switch (type) | ||
392 | { | ||
393 | case SSH_AGENT_FAILURE: | ||
394 | buffer_free(&buffer); | ||
395 | return 0; | ||
396 | case SSH_AGENT_SUCCESS: | ||
397 | buffer_free(&buffer); | ||
398 | return 1; | ||
399 | default: | ||
400 | fatal("Bad response to add identity from authentication agent: %d", | ||
401 | type); | ||
402 | } | ||
403 | /*NOTREACHED*/ | ||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | /* Removes an identity from the authentication server. This call is not meant | ||
408 | to be used by normal applications. */ | ||
409 | |||
410 | int ssh_remove_identity(AuthenticationConnection *auth, RSA *key) | ||
411 | { | ||
412 | Buffer buffer; | ||
413 | unsigned char buf[8192]; | ||
414 | int len, l, type; | ||
415 | |||
416 | /* Format a message to the agent. */ | ||
417 | buffer_init(&buffer); | ||
418 | buffer_put_char(&buffer, SSH_AGENTC_REMOVE_RSA_IDENTITY); | ||
419 | buffer_put_int(&buffer, BN_num_bits(key->n)); | ||
420 | buffer_put_bignum(&buffer, key->e); | ||
421 | buffer_put_bignum(&buffer, key->n); | ||
422 | |||
423 | /* Get the length of the message, and format it in the buffer. */ | ||
424 | len = buffer_len(&buffer); | ||
425 | PUT_32BIT(buf, len); | ||
426 | |||
427 | /* Send the length and then the packet to the agent. */ | ||
428 | if (write(auth->fd, buf, 4) != 4 || | ||
429 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != | ||
430 | buffer_len(&buffer)) | ||
431 | { | ||
432 | error("Error writing to authentication socket."); | ||
433 | error_cleanup: | ||
434 | buffer_free(&buffer); | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | /* Wait for response from the agent. First read the length of the | ||
439 | response packet. */ | ||
440 | len = 4; | ||
441 | while (len > 0) | ||
442 | { | ||
443 | l = read(auth->fd, buf + 4 - len, len); | ||
444 | if (l <= 0) | ||
445 | { | ||
446 | error("Error reading response length from authentication socket."); | ||
447 | goto error_cleanup; | ||
448 | } | ||
449 | len -= l; | ||
450 | } | ||
451 | |||
452 | /* Extract the length, and check it for sanity. */ | ||
453 | len = GET_32BIT(buf); | ||
454 | if (len > 256*1024) | ||
455 | fatal("Remove identity response too long: %d", len); | ||
456 | |||
457 | /* Read the rest of the response in tothe buffer. */ | ||
458 | buffer_clear(&buffer); | ||
459 | while (len > 0) | ||
460 | { | ||
461 | l = len; | ||
462 | if (l > sizeof(buf)) | ||
463 | l = sizeof(buf); | ||
464 | l = read(auth->fd, buf, l); | ||
465 | if (l <= 0) | ||
466 | { | ||
467 | error("Error reading response from authentication socket."); | ||
468 | goto error_cleanup; | ||
469 | } | ||
470 | buffer_append(&buffer, (char *)buf, l); | ||
471 | len -= l; | ||
472 | } | ||
473 | |||
474 | /* Get the type of the packet. */ | ||
475 | type = buffer_get_char(&buffer); | ||
476 | switch (type) | ||
477 | { | ||
478 | case SSH_AGENT_FAILURE: | ||
479 | buffer_free(&buffer); | ||
480 | return 0; | ||
481 | case SSH_AGENT_SUCCESS: | ||
482 | buffer_free(&buffer); | ||
483 | return 1; | ||
484 | default: | ||
485 | fatal("Bad response to remove identity from authentication agent: %d", | ||
486 | type); | ||
487 | } | ||
488 | /*NOTREACHED*/ | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | /* Removes all identities from the agent. This call is not meant | ||
493 | to be used by normal applications. */ | ||
494 | |||
495 | int ssh_remove_all_identities(AuthenticationConnection *auth) | ||
496 | { | ||
497 | Buffer buffer; | ||
498 | unsigned char buf[8192]; | ||
499 | int len, l, type; | ||
500 | |||
501 | /* Get the length of the message, and format it in the buffer. */ | ||
502 | PUT_32BIT(buf, 1); | ||
503 | buf[4] = SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES; | ||
504 | |||
505 | /* Send the length and then the packet to the agent. */ | ||
506 | if (write(auth->fd, buf, 5) != 5) | ||
507 | { | ||
508 | error("Error writing to authentication socket."); | ||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | /* Wait for response from the agent. First read the length of the | ||
513 | response packet. */ | ||
514 | len = 4; | ||
515 | while (len > 0) | ||
516 | { | ||
517 | l = read(auth->fd, buf + 4 - len, len); | ||
518 | if (l <= 0) | ||
519 | { | ||
520 | error("Error reading response length from authentication socket."); | ||
521 | return 0; | ||
522 | } | ||
523 | len -= l; | ||
524 | } | ||
525 | |||
526 | /* Extract the length, and check it for sanity. */ | ||
527 | len = GET_32BIT(buf); | ||
528 | if (len > 256*1024) | ||
529 | fatal("Remove identity response too long: %d", len); | ||
530 | |||
531 | /* Read the rest of the response into the buffer. */ | ||
532 | buffer_init(&buffer); | ||
533 | while (len > 0) | ||
534 | { | ||
535 | l = len; | ||
536 | if (l > sizeof(buf)) | ||
537 | l = sizeof(buf); | ||
538 | l = read(auth->fd, buf, l); | ||
539 | if (l <= 0) | ||
540 | { | ||
541 | error("Error reading response from authentication socket."); | ||
542 | buffer_free(&buffer); | ||
543 | return 0; | ||
544 | } | ||
545 | buffer_append(&buffer, (char *)buf, l); | ||
546 | len -= l; | ||
547 | } | ||
548 | |||
549 | /* Get the type of the packet. */ | ||
550 | type = buffer_get_char(&buffer); | ||
551 | switch (type) | ||
552 | { | ||
553 | case SSH_AGENT_FAILURE: | ||
554 | buffer_free(&buffer); | ||
555 | return 0; | ||
556 | case SSH_AGENT_SUCCESS: | ||
557 | buffer_free(&buffer); | ||
558 | return 1; | ||
559 | default: | ||
560 | fatal("Bad response to remove identity from authentication agent: %d", | ||
561 | type); | ||
562 | } | ||
563 | /*NOTREACHED*/ | ||
564 | return 0; | ||
565 | } | ||