]> andersk Git - openssh.git/blob - ssh-agent.c
Merged OpenBSD CVS changes that go away
[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 typedef struct
39 {
40   int fd;
41   enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type;
42   Buffer input;
43   Buffer output;
44 } SocketEntry;
45
46 unsigned int sockets_alloc = 0;
47 SocketEntry *sockets = NULL;
48
49 typedef struct
50 {
51   RSA *key;
52   char *comment;
53 } Identity;
54
55 unsigned int num_identities = 0;
56 Identity *identities = NULL;
57
58 int max_fd = 0;
59
60 /* pid of shell == parent of agent */
61 int parent_pid = -1;
62
63 /* pathname and directory for AUTH_SOCKET */
64 char socket_name[1024];
65 char socket_dir[1024];
66
67 void
68 process_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
89 void
90 process_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);
139
140             if (len <= 0 || len > 32) {
141               fatal("process_authentication_challenge: "
142                     "bad challenge length %d", len);
143             }
144
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
178 void
179 process_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
224 void
225 process_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
247 void
248 process_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
317 void
318 process_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
366 void
367 new_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
399 void
400 prepare_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
420 void 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
479 void
480 check_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
491 void
492 cleanup_socket(void)
493 {
494   remove(socket_name);
495   rmdir(socket_dir);
496 }
497
498 void
499 cleanup_exit(int i)
500 {
501   cleanup_socket();
502   exit(i);
503 }
504
505 void
506 usage()
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
516 int
517 main(int ac, char **av)
518 {
519   fd_set readset, writeset;
520   int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;
521   struct sockaddr_un sunaddr;
522   pid_t pid;
523   char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
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
534   while ((ch = getopt(ac, av, "cks")) != -1)
535     {
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);
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   }
604   snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
605            parent_pid);
606
607   /* Create socket early so it will exist before command gets run from
608      the parent.  */
609   sock = socket(AF_UNIX, SOCK_STREAM, 0);
610   if (sock < 0)
611     {
612       perror("socket");
613       cleanup_exit(1);
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");
621       cleanup_exit(1);
622     }
623   if (listen(sock, 5) < 0)
624     {
625       perror("listen");
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");
635       exit(1);
636     }
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
663   if (setsid() == -1)
664     {
665       perror("setsid");
666       cleanup_exit(1);
667     }
668
669   if (atexit(cleanup_socket) < 0)
670     {
671       perror("atexit");
672       cleanup_exit(1);
673     }
674
675   new_socket(AUTH_SOCKET, sock);
676   if (ac > 0)
677     {
678       signal(SIGALRM, check_parent_exists);
679       alarm(10);
680     }
681
682   signal(SIGINT, SIG_IGN);
683   signal(SIGPIPE, SIG_IGN);
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;
693           exit(1);
694         }
695       after_select(&readset, &writeset);
696     }
697   /*NOTREACHED*/
698 }
This page took 0.090411 seconds and 5 git commands to generate.