]> andersk Git - openssh.git/blame - ssh-agent.c
Fixed spello
[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
f370266e 534#if defined(__GNU_LIBRARY__)
535 while ((ch = getopt(ac, av, "+cks")) != -1)
536#else
dae3fa13 537 while ((ch = getopt(ac, av, "cks")) != -1)
f370266e 538#endif /* defined(__GNU_LIBRARY__) */
8efc0c15 539 {
dae3fa13 540 switch (ch)
541 {
542 case 'c':
543 if (s_flag)
544 usage();
545 c_flag++;
546 break;
547 case 'k':
548 k_flag++;
549 break;
550 case 's':
551 if (c_flag)
552 usage();
553 s_flag++;
554 break;
555 default:
556 usage();
557 }
558 }
559 ac -= optind;
560 av += optind;
561
562 if (ac > 0 && (c_flag || k_flag || s_flag))
563 usage();
564
565 if (ac == 0 && !c_flag && !k_flag && !s_flag)
566 {
567 shell = getenv("SHELL");
568 if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
569 c_flag = 1;
570 }
571
572 if (k_flag)
573 {
574 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
575 if (pidstr == NULL)
576 {
577 fprintf(stderr, "%s not set, cannot kill agent\n",
578 SSH_AGENTPID_ENV_NAME);
579 exit(1);
580 }
581 pid = atoi(pidstr);
582 if (pid < 1) /* XXX PID_MAX check too */
583 {
584 fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
585 SSH_AGENTPID_ENV_NAME, pidstr);
586 exit(1);
587 }
588 if (kill(pid, SIGTERM) == -1)
589 {
590 perror("kill");
591 exit(1);
592 }
593 format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
594 printf(format, SSH_AUTHSOCKET_ENV_NAME);
595 printf(format, SSH_AGENTPID_ENV_NAME);
596 printf("echo Agent pid %d killed;\n", pid);
597 exit(0);
8efc0c15 598 }
599
600 parent_pid = getpid();
601
602 /* Create private directory for agent socket */
603 strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
604 if (mkdtemp(socket_dir) == NULL) {
605 perror("mkdtemp: private socket dir");
606 exit(1);
607 }
dae3fa13 608 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
609 parent_pid);
5068f573 610
dae3fa13 611 /* Create socket early so it will exist before command gets run from
612 the parent. */
8efc0c15 613 sock = socket(AF_UNIX, SOCK_STREAM, 0);
614 if (sock < 0)
615 {
616 perror("socket");
dae3fa13 617 cleanup_exit(1);
8efc0c15 618 }
619 memset(&sunaddr, 0, sizeof(sunaddr));
620 sunaddr.sun_family = AF_UNIX;
621 strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
622 if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
623 {
624 perror("bind");
dae3fa13 625 cleanup_exit(1);
8efc0c15 626 }
627 if (listen(sock, 5) < 0)
628 {
629 perror("listen");
dae3fa13 630 cleanup_exit(1);
631 }
632
633 /* Fork, and have the parent execute the command, if any, or present the
634 socket data. The child continues as the authentication agent. */
635 pid = fork();
636 if (pid == -1)
637 {
638 perror("fork");
8efc0c15 639 exit(1);
640 }
dae3fa13 641 if (pid != 0)
642 { /* Parent - execute the given command. */
643 close(sock);
644 snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
645 if (ac == 0)
646 {
647 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
648 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
649 SSH_AUTHSOCKET_ENV_NAME);
650 printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
651 SSH_AGENTPID_ENV_NAME);
652 printf("echo Agent pid %d;\n", pid);
653 exit(0);
654 }
655
656 setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
657 setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
658 execvp(av[0], av);
659 perror(av[0]);
660 exit(1);
661 }
662
663 close(0);
664 close(1);
665 close(2);
666
69256d9d 667 if (setsid() == -1)
668 {
669 perror("setsid");
670 cleanup_exit(1);
671 }
dae3fa13 672
673 if (atexit(cleanup_socket) < 0)
69256d9d 674 {
675 perror("atexit");
676 cleanup_exit(1);
677 }
dae3fa13 678
8efc0c15 679 new_socket(AUTH_SOCKET, sock);
dae3fa13 680 if (ac > 0)
681 {
682 signal(SIGALRM, check_parent_exists);
683 alarm(10);
684 }
8efc0c15 685
686 signal(SIGINT, SIG_IGN);
dae3fa13 687 signal(SIGPIPE, SIG_IGN);
8efc0c15 688 while (1)
689 {
690 FD_ZERO(&readset);
691 FD_ZERO(&writeset);
692 prepare_select(&readset, &writeset);
693 if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
694 {
695 if (errno == EINTR)
696 continue;
8efc0c15 697 exit(1);
698 }
699 after_select(&readset, &writeset);
700 }
701 /*NOTREACHED*/
702}
This page took 0.141209 seconds and 5 git commands to generate.