Annotation of src/usr.bin/ssh/ssh-agent.c, Revision 1.23
1.23 ! markus 1: /* $OpenBSD: ssh-agent.c,v 1.22 1999/11/24 00:26:03 deraadt Exp $ */
1.15 markus 2:
1.1 deraadt 3: /*
1.22 deraadt 4: * Author: Tatu Ylonen <ylo@cs.hut.fi>
5: * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
6: * All rights reserved
7: * Created: Wed Mar 29 03:46:59 1995 ylo
8: * The authentication agent program.
9: */
1.1 deraadt 10:
11: #include "includes.h"
1.23 ! markus 12: RCSID("$OpenBSD: ssh-agent.c,v 1.22 1999/11/24 00:26:03 deraadt Exp $");
1.1 deraadt 13:
14: #include "ssh.h"
15: #include "rsa.h"
16: #include "authfd.h"
17: #include "buffer.h"
18: #include "bufaux.h"
19: #include "xmalloc.h"
20: #include "packet.h"
21: #include "getput.h"
22: #include "mpaux.h"
23:
1.13 deraadt 24: #include <ssl/md5.h>
1.7 deraadt 25:
1.21 markus 26: typedef struct {
27: int fd;
28: enum {
29: AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION
30: } type;
31: Buffer input;
32: Buffer output;
1.1 deraadt 33: } SocketEntry;
34:
35: unsigned int sockets_alloc = 0;
36: SocketEntry *sockets = NULL;
37:
1.21 markus 38: typedef struct {
39: RSA *key;
40: char *comment;
1.1 deraadt 41: } Identity;
42:
43: unsigned int num_identities = 0;
44: Identity *identities = NULL;
45:
46: int max_fd = 0;
47:
1.11 markus 48: /* pid of shell == parent of agent */
1.10 markus 49: int parent_pid = -1;
50:
51: /* pathname and directory for AUTH_SOCKET */
52: char socket_name[1024];
53: char socket_dir[1024];
54:
1.20 markus 55: extern char *__progname;
56:
1.2 provos 57: void
58: process_request_identity(SocketEntry *e)
1.1 deraadt 59: {
1.21 markus 60: Buffer msg;
61: int i;
1.1 deraadt 62:
1.21 markus 63: buffer_init(&msg);
64: buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);
65: buffer_put_int(&msg, num_identities);
66: for (i = 0; i < num_identities; i++) {
67: buffer_put_int(&msg, BN_num_bits(identities[i].key->n));
68: buffer_put_bignum(&msg, identities[i].key->e);
69: buffer_put_bignum(&msg, identities[i].key->n);
70: buffer_put_string(&msg, identities[i].comment,
71: strlen(identities[i].comment));
72: }
73: buffer_put_int(&e->output, buffer_len(&msg));
74: buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
75: buffer_free(&msg);
1.1 deraadt 76: }
77:
1.2 provos 78: void
79: process_authentication_challenge(SocketEntry *e)
1.1 deraadt 80: {
1.21 markus 81: int i, pub_bits, len;
82: BIGNUM *pub_e, *pub_n, *challenge;
83: Buffer msg;
84: MD5_CTX md;
85: unsigned char buf[32], mdbuf[16], session_id[16];
86: unsigned int response_type;
87:
88: buffer_init(&msg);
89: pub_e = BN_new();
90: pub_n = BN_new();
91: challenge = BN_new();
92: pub_bits = buffer_get_int(&e->input);
93: buffer_get_bignum(&e->input, pub_e);
94: buffer_get_bignum(&e->input, pub_n);
95: buffer_get_bignum(&e->input, challenge);
96: if (buffer_len(&e->input) == 0) {
97: /* Compatibility code for old servers. */
98: memset(session_id, 0, 16);
99: response_type = 0;
100: } else {
101: /* New code. */
102: buffer_get(&e->input, (char *) session_id, 16);
103: response_type = buffer_get_int(&e->input);
104: }
105: for (i = 0; i < num_identities; i++)
106: if (pub_bits == BN_num_bits(identities[i].key->n) &&
107: BN_cmp(pub_e, identities[i].key->e) == 0 &&
108: BN_cmp(pub_n, identities[i].key->n) == 0) {
109: /* Decrypt the challenge using the private key. */
110: rsa_private_decrypt(challenge, challenge, identities[i].key);
111:
112: /* Compute the desired response. */
113: switch (response_type) {
114: case 0:/* As of protocol 1.0 */
115: /* This response type is no longer supported. */
116: log("Compatibility with ssh protocol 1.0 no longer supported.");
117: buffer_put_char(&msg, SSH_AGENT_FAILURE);
118: goto send;
119:
120: case 1:/* As of protocol 1.1 */
121: /* The response is MD5 of decrypted challenge plus session id. */
122: len = BN_num_bytes(challenge);
123:
124: if (len <= 0 || len > 32) {
125: fatal("process_authentication_challenge: "
126: "bad challenge length %d", len);
127: }
128: memset(buf, 0, 32);
129: BN_bn2bin(challenge, buf + 32 - len);
130: MD5_Init(&md);
131: MD5_Update(&md, buf, 32);
132: MD5_Update(&md, session_id, 16);
133: MD5_Final(mdbuf, &md);
134: break;
135:
136: default:
137: fatal("process_authentication_challenge: bad response_type %d",
138: response_type);
139: break;
140: }
141:
142: /* Send the response. */
143: buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
144: for (i = 0; i < 16; i++)
145: buffer_put_char(&msg, mdbuf[i]);
146:
147: goto send;
148: }
149: /* Unknown identity. Send failure. */
150: buffer_put_char(&msg, SSH_AGENT_FAILURE);
151: send:
152: buffer_put_int(&e->output, buffer_len(&msg));
153: buffer_append(&e->output, buffer_ptr(&msg),
154: buffer_len(&msg));
155: buffer_free(&msg);
156: BN_clear_free(pub_e);
157: BN_clear_free(pub_n);
158: BN_clear_free(challenge);
1.1 deraadt 159: }
160:
1.2 provos 161: void
162: process_remove_identity(SocketEntry *e)
1.1 deraadt 163: {
1.21 markus 164: unsigned int bits;
165: unsigned int i;
166: BIGNUM *dummy, *n;
167:
168: dummy = BN_new();
169: n = BN_new();
170:
171: /* Get the key from the packet. */
172: bits = buffer_get_int(&e->input);
173: buffer_get_bignum(&e->input, dummy);
174: buffer_get_bignum(&e->input, n);
175:
176: if (bits != BN_num_bits(n))
177: error("Warning: keysize mismatch: actual %d, announced %d",
178: BN_num_bits(n), bits);
179:
180: /* Check if we have the key. */
181: for (i = 0; i < num_identities; i++)
182: if (BN_cmp(identities[i].key->n, n) == 0) {
1.23 ! markus 183: /*
! 184: * We have this key. Free the old key. Since we
! 185: * don\'t want to leave empty slots in the middle of
! 186: * the array, we actually free the key there and copy
! 187: * data from the last entry.
! 188: */
1.21 markus 189: RSA_free(identities[i].key);
190: xfree(identities[i].comment);
191: if (i < num_identities - 1)
192: identities[i] = identities[num_identities - 1];
193: num_identities--;
194: BN_clear_free(dummy);
195: BN_clear_free(n);
196:
197: /* Send success. */
198: buffer_put_int(&e->output, 1);
199: buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
200: return;
201: }
202: /* We did not have the key. */
203: BN_clear(dummy);
204: BN_clear(n);
1.1 deraadt 205:
1.21 markus 206: /* Send failure. */
1.1 deraadt 207: buffer_put_int(&e->output, 1);
1.21 markus 208: buffer_put_char(&e->output, SSH_AGENT_FAILURE);
1.1 deraadt 209: }
210:
1.22 deraadt 211: /*
212: * Removes all identities from the agent.
213: */
1.2 provos 214: void
215: process_remove_all_identities(SocketEntry *e)
1.1 deraadt 216: {
1.21 markus 217: unsigned int i;
218:
219: /* Loop over all identities and clear the keys. */
220: for (i = 0; i < num_identities; i++) {
221: RSA_free(identities[i].key);
222: xfree(identities[i].comment);
223: }
224:
225: /* Mark that there are no identities. */
226: num_identities = 0;
227:
228: /* Send success. */
229: buffer_put_int(&e->output, 1);
230: buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
231: return;
1.1 deraadt 232: }
233:
1.22 deraadt 234: /*
235: * Adds an identity to the agent.
236: */
1.2 provos 237: void
238: process_add_identity(SocketEntry *e)
1.1 deraadt 239: {
1.21 markus 240: RSA *k;
241: int i;
242: BIGNUM *aux;
243: BN_CTX *ctx;
244:
245: if (num_identities == 0)
246: identities = xmalloc(sizeof(Identity));
247: else
248: identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
249:
250: identities[num_identities].key = RSA_new();
251: k = identities[num_identities].key;
252: buffer_get_int(&e->input); /* bits */
253: k->n = BN_new();
254: buffer_get_bignum(&e->input, k->n);
255: k->e = BN_new();
256: buffer_get_bignum(&e->input, k->e);
257: k->d = BN_new();
258: buffer_get_bignum(&e->input, k->d);
259: k->iqmp = BN_new();
260: buffer_get_bignum(&e->input, k->iqmp);
261: /* SSH and SSL have p and q swapped */
262: k->q = BN_new();
263: buffer_get_bignum(&e->input, k->q); /* p */
264: k->p = BN_new();
265: buffer_get_bignum(&e->input, k->p); /* q */
266:
267: /* Generate additional parameters */
268: aux = BN_new();
269: ctx = BN_CTX_new();
270:
271: BN_sub(aux, k->q, BN_value_one());
272: k->dmq1 = BN_new();
273: BN_mod(k->dmq1, k->d, aux, ctx);
274:
275: BN_sub(aux, k->p, BN_value_one());
276: k->dmp1 = BN_new();
277: BN_mod(k->dmp1, k->d, aux, ctx);
278:
279: BN_clear_free(aux);
280: BN_CTX_free(ctx);
281:
282: identities[num_identities].comment = buffer_get_string(&e->input, NULL);
283:
284: /* Check if we already have the key. */
285: for (i = 0; i < num_identities; i++)
286: if (BN_cmp(identities[i].key->n, k->n) == 0) {
1.23 ! markus 287: /*
! 288: * We already have this key. Clear and free the new
! 289: * data and return success.
! 290: */
1.21 markus 291: RSA_free(k);
292: xfree(identities[num_identities].comment);
293:
294: /* Send success. */
295: buffer_put_int(&e->output, 1);
296: buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
297: return;
298: }
299: /* Increment the number of identities. */
300: num_identities++;
1.1 deraadt 301:
1.21 markus 302: /* Send a success message. */
1.1 deraadt 303: buffer_put_int(&e->output, 1);
304: buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
305: }
306:
1.2 provos 307: void
308: process_message(SocketEntry *e)
1.1 deraadt 309: {
1.21 markus 310: unsigned int msg_len;
311: unsigned int type;
312: unsigned char *cp;
313: if (buffer_len(&e->input) < 5)
314: return; /* Incomplete message. */
315: cp = (unsigned char *) buffer_ptr(&e->input);
316: msg_len = GET_32BIT(cp);
317: if (msg_len > 256 * 1024) {
318: shutdown(e->fd, SHUT_RDWR);
319: close(e->fd);
320: e->type = AUTH_UNUSED;
321: return;
322: }
323: if (buffer_len(&e->input) < msg_len + 4)
324: return;
325: buffer_consume(&e->input, 4);
326: type = buffer_get_char(&e->input);
327:
328: switch (type) {
329: case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
330: process_request_identity(e);
331: break;
332: case SSH_AGENTC_RSA_CHALLENGE:
333: process_authentication_challenge(e);
334: break;
335: case SSH_AGENTC_ADD_RSA_IDENTITY:
336: process_add_identity(e);
337: break;
338: case SSH_AGENTC_REMOVE_RSA_IDENTITY:
339: process_remove_identity(e);
340: break;
341: case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
342: process_remove_all_identities(e);
343: break;
344: default:
345: /* Unknown message. Respond with failure. */
346: error("Unknown message %d", type);
347: buffer_clear(&e->input);
348: buffer_put_int(&e->output, 1);
349: buffer_put_char(&e->output, SSH_AGENT_FAILURE);
350: break;
351: }
1.1 deraadt 352: }
353:
1.2 provos 354: void
355: new_socket(int type, int fd)
1.1 deraadt 356: {
1.21 markus 357: unsigned int i, old_alloc;
358: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
359: error("fcntl O_NONBLOCK: %s", strerror(errno));
360:
361: if (fd > max_fd)
362: max_fd = fd;
363:
364: for (i = 0; i < sockets_alloc; i++)
365: if (sockets[i].type == AUTH_UNUSED) {
366: sockets[i].fd = fd;
367: sockets[i].type = type;
368: buffer_init(&sockets[i].input);
369: buffer_init(&sockets[i].output);
370: return;
371: }
372: old_alloc = sockets_alloc;
373: sockets_alloc += 10;
374: if (sockets)
375: sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
376: else
377: sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
378: for (i = old_alloc; i < sockets_alloc; i++)
379: sockets[i].type = AUTH_UNUSED;
380: sockets[old_alloc].type = type;
381: sockets[old_alloc].fd = fd;
382: buffer_init(&sockets[old_alloc].input);
383: buffer_init(&sockets[old_alloc].output);
1.1 deraadt 384: }
385:
1.2 provos 386: void
387: prepare_select(fd_set *readset, fd_set *writeset)
1.1 deraadt 388: {
1.21 markus 389: unsigned int i;
390: for (i = 0; i < sockets_alloc; i++)
391: switch (sockets[i].type) {
392: case AUTH_SOCKET:
393: case AUTH_CONNECTION:
394: FD_SET(sockets[i].fd, readset);
395: if (buffer_len(&sockets[i].output) > 0)
396: FD_SET(sockets[i].fd, writeset);
397: break;
398: case AUTH_UNUSED:
399: break;
400: default:
401: fatal("Unknown socket type %d", sockets[i].type);
402: break;
403: }
404: }
405:
406: void
407: after_select(fd_set *readset, fd_set *writeset)
408: {
409: unsigned int i;
410: int len, sock;
411: char buf[1024];
412: struct sockaddr_un sunaddr;
413:
414: for (i = 0; i < sockets_alloc; i++)
415: switch (sockets[i].type) {
416: case AUTH_UNUSED:
417: break;
418: case AUTH_SOCKET:
419: if (FD_ISSET(sockets[i].fd, readset)) {
420: len = sizeof(sunaddr);
421: sock = accept(sockets[i].fd, (struct sockaddr *) & sunaddr, &len);
422: if (sock < 0) {
423: perror("accept from AUTH_SOCKET");
424: break;
425: }
426: new_socket(AUTH_CONNECTION, sock);
427: }
428: break;
429: case AUTH_CONNECTION:
430: if (buffer_len(&sockets[i].output) > 0 &&
431: FD_ISSET(sockets[i].fd, writeset)) {
432: len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
433: buffer_len(&sockets[i].output));
434: if (len <= 0) {
435: shutdown(sockets[i].fd, SHUT_RDWR);
436: close(sockets[i].fd);
437: sockets[i].type = AUTH_UNUSED;
438: break;
439: }
440: buffer_consume(&sockets[i].output, len);
441: }
442: if (FD_ISSET(sockets[i].fd, readset)) {
443: len = read(sockets[i].fd, buf, sizeof(buf));
444: if (len <= 0) {
445: shutdown(sockets[i].fd, SHUT_RDWR);
446: close(sockets[i].fd);
447: sockets[i].type = AUTH_UNUSED;
448: break;
449: }
450: buffer_append(&sockets[i].input, buf, len);
451: process_message(&sockets[i]);
452: }
453: break;
454: default:
455: fatal("Unknown type %d", sockets[i].type);
456: }
1.1 deraadt 457: }
458:
1.6 deraadt 459: void
1.2 provos 460: check_parent_exists(int sig)
1.1 deraadt 461: {
1.21 markus 462: if (kill(parent_pid, 0) < 0) {
463: /* printf("Parent has died - Authentication agent exiting.\n"); */
464: exit(1);
465: }
466: signal(SIGALRM, check_parent_exists);
467: alarm(10);
1.1 deraadt 468: }
469:
1.15 markus 470: void
471: cleanup_socket(void)
472: {
1.21 markus 473: remove(socket_name);
474: rmdir(socket_dir);
1.10 markus 475: }
476:
1.15 markus 477: void
478: cleanup_exit(int i)
479: {
1.21 markus 480: cleanup_socket();
481: exit(i);
1.15 markus 482: }
483:
484: void
485: usage()
486: {
1.21 markus 487: fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
488: fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n",
489: __progname);
490: exit(1);
1.15 markus 491: }
492:
1.2 provos 493: int
494: main(int ac, char **av)
1.1 deraadt 495: {
1.21 markus 496: fd_set readset, writeset;
497: int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;
498: struct sockaddr_un sunaddr;
499: pid_t pid;
500: char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
501:
502: /* check if RSA support exists */
503: if (rsa_alive() == 0) {
504: fprintf(stderr,
505: "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
506: __progname);
507: exit(1);
508: }
509: while ((ch = getopt(ac, av, "cks")) != -1) {
510: switch (ch) {
511: case 'c':
512: if (s_flag)
513: usage();
514: c_flag++;
515: break;
516: case 'k':
517: k_flag++;
518: break;
519: case 's':
520: if (c_flag)
521: usage();
522: s_flag++;
523: break;
524: default:
525: usage();
526: }
527: }
528: ac -= optind;
529: av += optind;
530:
531: if (ac > 0 && (c_flag || k_flag || s_flag))
532: usage();
533:
534: if (ac == 0 && !c_flag && !k_flag && !s_flag) {
535: shell = getenv("SHELL");
536: if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
537: c_flag = 1;
538: }
539: if (k_flag) {
540: pidstr = getenv(SSH_AGENTPID_ENV_NAME);
541: if (pidstr == NULL) {
542: fprintf(stderr, "%s not set, cannot kill agent\n",
543: SSH_AGENTPID_ENV_NAME);
544: exit(1);
545: }
546: pid = atoi(pidstr);
547: if (pid < 1) { /* XXX PID_MAX check too */
548: fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
549: SSH_AGENTPID_ENV_NAME, pidstr);
550: exit(1);
551: }
552: if (kill(pid, SIGTERM) == -1) {
553: perror("kill");
554: exit(1);
555: }
556: format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
557: printf(format, SSH_AUTHSOCKET_ENV_NAME);
558: printf(format, SSH_AGENTPID_ENV_NAME);
559: printf("echo Agent pid %d killed;\n", pid);
560: exit(0);
561: }
562: parent_pid = getpid();
563:
564: /* Create private directory for agent socket */
565: strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
566: if (mkdtemp(socket_dir) == NULL) {
567: perror("mkdtemp: private socket dir");
568: exit(1);
569: }
570: snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
571: parent_pid);
572:
1.23 ! markus 573: /*
! 574: * Create socket early so it will exist before command gets run from
! 575: * the parent.
! 576: */
1.21 markus 577: sock = socket(AF_UNIX, SOCK_STREAM, 0);
578: if (sock < 0) {
579: perror("socket");
580: cleanup_exit(1);
581: }
582: memset(&sunaddr, 0, sizeof(sunaddr));
583: sunaddr.sun_family = AF_UNIX;
584: strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
585: if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) {
586: perror("bind");
587: cleanup_exit(1);
588: }
589: if (listen(sock, 5) < 0) {
590: perror("listen");
591: cleanup_exit(1);
592: }
1.23 ! markus 593: /*
! 594: * Fork, and have the parent execute the command, if any, or present
! 595: * the socket data. The child continues as the authentication agent.
! 596: */
1.21 markus 597: pid = fork();
598: if (pid == -1) {
599: perror("fork");
600: exit(1);
601: }
602: if (pid != 0) { /* Parent - execute the given command. */
603: close(sock);
604: snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
605: if (ac == 0) {
606: format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
607: printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
608: SSH_AUTHSOCKET_ENV_NAME);
609: printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
610: SSH_AGENTPID_ENV_NAME);
611: printf("echo Agent pid %d;\n", pid);
612: exit(0);
613: }
614: setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
615: setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
616: execvp(av[0], av);
617: perror(av[0]);
618: exit(1);
619: }
620: close(0);
621: close(1);
622: close(2);
623:
624: if (setsid() == -1) {
625: perror("setsid");
626: cleanup_exit(1);
627: }
628: if (atexit(cleanup_socket) < 0) {
629: perror("atexit");
630: cleanup_exit(1);
631: }
632: new_socket(AUTH_SOCKET, sock);
633: if (ac > 0) {
634: signal(SIGALRM, check_parent_exists);
635: alarm(10);
636: }
637: signal(SIGINT, SIG_IGN);
638: signal(SIGPIPE, SIG_IGN);
639: while (1) {
640: FD_ZERO(&readset);
641: FD_ZERO(&writeset);
642: prepare_select(&readset, &writeset);
643: if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0) {
644: if (errno == EINTR)
645: continue;
646: exit(1);
647: }
648: after_select(&readset, &writeset);
1.15 markus 649: }
1.21 markus 650: /* NOTREACHED */
1.1 deraadt 651: }