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