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