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