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