]> andersk Git - openssh.git/blob - ssh-agent.c
- Merged OpenBSD CVS changes:
[openssh.git] / ssh-agent.c
1 /*      $OpenBSD: ssh-agent.c,v 1.18 1999/11/15 20:53:24 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.18 1999/11/15 20:53:24 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   if (bits != BN_num_bits(n))
200     error("Warning: keysize mismatch: actual %d, announced %s",
201           BN_num_bits(n), bits);
202   
203   /* Check if we have the key. */
204   for (i = 0; i < num_identities; i++)
205     if (BN_cmp(identities[i].key->n, n) == 0)
206       {
207         /* We have this key.  Free the old key.  Since we don\'t want to leave
208            empty slots in the middle of the array, we actually free the
209            key there and copy data from the last entry. */
210         RSA_free(identities[i].key);
211         xfree(identities[i].comment);
212         if (i < num_identities - 1)
213           identities[i] = identities[num_identities - 1];
214         num_identities--;
215         BN_clear_free(dummy);
216         BN_clear_free(n);
217
218         /* Send success. */
219         buffer_put_int(&e->output, 1);
220         buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
221         return;
222       }
223   /* We did not have the key. */
224   BN_clear(dummy);
225   BN_clear(n);
226
227   /* Send failure. */
228   buffer_put_int(&e->output, 1);
229   buffer_put_char(&e->output, SSH_AGENT_FAILURE);
230 }
231
232 /* Removes all identities from the agent. */
233
234 void
235 process_remove_all_identities(SocketEntry *e)
236 {
237   unsigned int i;
238   
239   /* Loop over all identities and clear the keys. */
240   for (i = 0; i < num_identities; i++)
241     {
242       RSA_free(identities[i].key);
243       xfree(identities[i].comment);
244     }
245
246   /* Mark that there are no identities. */
247   num_identities = 0;
248
249   /* Send success. */
250   buffer_put_int(&e->output, 1);
251   buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
252   return;
253 }
254
255 /* Adds an identity to the agent. */
256
257 void
258 process_add_identity(SocketEntry *e)
259 {
260   RSA *k;
261   int i;
262   BIGNUM *aux;
263   BN_CTX *ctx;
264   
265   if (num_identities == 0)
266     identities = xmalloc(sizeof(Identity));
267   else
268     identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
269
270   identities[num_identities].key = RSA_new();
271   k = identities[num_identities].key;
272   buffer_get_int(&e->input); /* bits */
273   k->n = BN_new();
274   buffer_get_bignum(&e->input, k->n);
275   k->e = BN_new();
276   buffer_get_bignum(&e->input, k->e);
277   k->d = BN_new();
278   buffer_get_bignum(&e->input, k->d);
279   k->iqmp = BN_new();
280   buffer_get_bignum(&e->input, k->iqmp);
281   /* SSH and SSL have p and q swapped */
282   k->q = BN_new();
283   buffer_get_bignum(&e->input, k->q); /* p */
284   k->p = BN_new();
285   buffer_get_bignum(&e->input, k->p); /* q */
286
287   /* Generate additional parameters */
288   aux = BN_new();
289   ctx = BN_CTX_new();
290
291   BN_sub(aux, k->q, BN_value_one());
292   k->dmq1 = BN_new();
293   BN_mod(k->dmq1, k->d, aux, ctx);
294
295   BN_sub(aux, k->p, BN_value_one());
296   k->dmp1 = BN_new();
297   BN_mod(k->dmp1, k->d, aux, ctx);
298
299   BN_clear_free(aux);
300   BN_CTX_free(ctx);
301   
302   identities[num_identities].comment = buffer_get_string(&e->input, NULL);
303
304   /* Check if we already have the key. */
305   for (i = 0; i < num_identities; i++)
306     if (BN_cmp(identities[i].key->n, k->n) == 0)
307       {
308         /* We already have this key.  Clear and free the new data and
309            return success. */
310         RSA_free(k);
311         xfree(identities[num_identities].comment);
312
313         /* Send success. */
314         buffer_put_int(&e->output, 1);
315         buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
316         return;
317       }
318
319   /* Increment the number of identities. */
320   num_identities++;
321   
322   /* Send a success message. */
323   buffer_put_int(&e->output, 1);
324   buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
325 }
326
327 void
328 process_message(SocketEntry *e)
329 {
330   unsigned int msg_len;
331   unsigned int type;
332   unsigned char *cp;
333   if (buffer_len(&e->input) < 5)
334     return; /* Incomplete message. */
335   cp = (unsigned char *)buffer_ptr(&e->input);
336   msg_len = GET_32BIT(cp);
337   if (msg_len > 256 * 1024)
338     {
339       shutdown(e->fd, SHUT_RDWR);
340       close(e->fd);
341       e->type = AUTH_UNUSED;
342       return;
343     }
344   if (buffer_len(&e->input) < msg_len + 4)
345     return;
346   buffer_consume(&e->input, 4);
347   type = buffer_get_char(&e->input);
348
349   switch (type)
350     {
351     case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
352       process_request_identity(e);
353       break;
354     case SSH_AGENTC_RSA_CHALLENGE:
355       process_authentication_challenge(e);
356       break;
357     case SSH_AGENTC_ADD_RSA_IDENTITY:
358       process_add_identity(e);
359       break;
360     case SSH_AGENTC_REMOVE_RSA_IDENTITY:
361       process_remove_identity(e);
362       break;
363     case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
364       process_remove_all_identities(e);
365       break;
366     default:
367       /* Unknown message.  Respond with failure. */
368       error("Unknown message %d", type);
369       buffer_clear(&e->input);
370       buffer_put_int(&e->output, 1);
371       buffer_put_char(&e->output, SSH_AGENT_FAILURE);
372       break;
373     }
374 }
375
376 void
377 new_socket(int type, int fd)
378 {
379   unsigned int i, old_alloc;
380   if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
381     error("fcntl O_NONBLOCK: %s", strerror(errno));
382
383   if (fd > max_fd)
384     max_fd = fd;
385
386   for (i = 0; i < sockets_alloc; i++)
387     if (sockets[i].type == AUTH_UNUSED)
388       {
389         sockets[i].fd = fd;
390         sockets[i].type = type;
391         buffer_init(&sockets[i].input);
392         buffer_init(&sockets[i].output);
393         return;
394       }
395   old_alloc = sockets_alloc;
396   sockets_alloc += 10;
397   if (sockets)
398     sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
399   else
400     sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
401   for (i = old_alloc; i < sockets_alloc; i++)
402     sockets[i].type = AUTH_UNUSED;
403   sockets[old_alloc].type = type;
404   sockets[old_alloc].fd = fd;
405   buffer_init(&sockets[old_alloc].input);
406   buffer_init(&sockets[old_alloc].output);
407 }
408
409 void
410 prepare_select(fd_set *readset, fd_set *writeset)
411 {
412   unsigned int i;
413   for (i = 0; i < sockets_alloc; i++)
414     switch (sockets[i].type)
415       {
416       case AUTH_SOCKET:
417       case AUTH_CONNECTION:
418         FD_SET(sockets[i].fd, readset);
419         if (buffer_len(&sockets[i].output) > 0)
420           FD_SET(sockets[i].fd, writeset);
421         break;
422       case AUTH_UNUSED:
423         break;
424       default:
425         fatal("Unknown socket type %d", sockets[i].type);
426         break;
427       }
428 }
429
430 void after_select(fd_set *readset, fd_set *writeset)
431 {
432   unsigned int i;
433   int len, sock;
434   char buf[1024];
435   struct sockaddr_un sunaddr;
436
437   for (i = 0; i < sockets_alloc; i++)
438     switch (sockets[i].type)
439       {
440       case AUTH_UNUSED:
441         break;
442       case AUTH_SOCKET:
443         if (FD_ISSET(sockets[i].fd, readset))
444           {
445             len = sizeof(sunaddr);
446             sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);
447             if (sock < 0)
448               {
449                 perror("accept from AUTH_SOCKET");
450                 break;
451               }
452             new_socket(AUTH_CONNECTION, sock);
453           }
454         break;
455       case AUTH_CONNECTION:
456         if (buffer_len(&sockets[i].output) > 0 &&
457             FD_ISSET(sockets[i].fd, writeset))
458           {
459             len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
460                         buffer_len(&sockets[i].output));
461             if (len <= 0)
462               {
463                 shutdown(sockets[i].fd, SHUT_RDWR);
464                 close(sockets[i].fd);
465                 sockets[i].type = AUTH_UNUSED;
466                 break;
467               }
468             buffer_consume(&sockets[i].output, len);
469           }
470         if (FD_ISSET(sockets[i].fd, readset))
471           {
472             len = read(sockets[i].fd, buf, sizeof(buf));
473             if (len <= 0)
474               {
475                 shutdown(sockets[i].fd, SHUT_RDWR);
476                 close(sockets[i].fd);
477                 sockets[i].type = AUTH_UNUSED;
478                 break;
479               }
480             buffer_append(&sockets[i].input, buf, len);
481             process_message(&sockets[i]);
482           }
483         break;
484       default:
485         fatal("Unknown type %d", sockets[i].type);
486       }
487 }
488
489 void
490 check_parent_exists(int sig)
491 {
492   if (kill(parent_pid, 0) < 0)
493     {
494       /* printf("Parent has died - Authentication agent exiting.\n"); */
495       exit(1);
496     }
497   signal(SIGALRM, check_parent_exists);
498   alarm(10);
499 }
500
501 void
502 cleanup_socket(void)
503 {
504   remove(socket_name);
505   rmdir(socket_dir);
506 }
507
508 void
509 cleanup_exit(int i)
510 {
511   cleanup_socket();
512   exit(i);
513 }
514
515 void
516 usage()
517 {
518   fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
519   fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n",
520           __progname);
521   exit(1);
522 }
523
524 int
525 main(int ac, char **av)
526 {
527   fd_set readset, writeset;
528   int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;
529   struct sockaddr_un sunaddr;
530   pid_t pid;
531   char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
532
533   /* check if RSA support exists */
534   if (rsa_alive() == 0) {
535     fprintf(stderr,
536       "%s: no RSA support in libssl and libcrypto.  See ssl(8).\n",
537       __progname);
538     exit(1);
539   }
540
541 #if defined(__GNU_LIBRARY__)
542   while ((ch = getopt(ac, av, "+cks")) != -1)
543 #else 
544   while ((ch = getopt(ac, av, "cks")) != -1)
545 #endif /* defined(__GNU_LIBRARY__) */
546     {
547       switch (ch)
548         {
549         case 'c':
550           if (s_flag)
551             usage();
552           c_flag++;
553           break;
554         case 'k':
555           k_flag++;
556           break;
557         case 's':
558           if (c_flag)
559             usage();
560           s_flag++;
561           break;
562         default:
563           usage();
564         }
565     }
566   ac -= optind;
567   av += optind;
568
569   if (ac > 0 && (c_flag || k_flag || s_flag))
570     usage();
571
572   if (ac == 0 && !c_flag && !k_flag && !s_flag)
573     {
574       shell = getenv("SHELL");
575       if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
576         c_flag = 1;
577     }
578
579   if (k_flag)
580     {
581       pidstr = getenv(SSH_AGENTPID_ENV_NAME);
582       if (pidstr == NULL)
583         {
584           fprintf(stderr, "%s not set, cannot kill agent\n",
585                   SSH_AGENTPID_ENV_NAME);
586           exit(1);
587         }
588       pid = atoi(pidstr);
589       if (pid < 1)              /* XXX PID_MAX check too */
590         {
591           fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
592                   SSH_AGENTPID_ENV_NAME, pidstr);
593           exit(1);
594         }
595       if (kill(pid, SIGTERM) == -1)
596         {
597           perror("kill");
598           exit(1);
599         }
600       format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
601       printf(format, SSH_AUTHSOCKET_ENV_NAME);
602       printf(format, SSH_AGENTPID_ENV_NAME);
603       printf("echo Agent pid %d killed;\n", pid);
604       exit(0);
605     }
606
607   parent_pid = getpid();
608
609   /* Create private directory for agent socket */
610   strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
611   if (mkdtemp(socket_dir) == NULL) {
612       perror("mkdtemp: private socket dir");
613       exit(1);
614   }
615   snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
616            parent_pid);
617
618   /* Create socket early so it will exist before command gets run from
619      the parent.  */
620   sock = socket(AF_UNIX, SOCK_STREAM, 0);
621   if (sock < 0)
622     {
623       perror("socket");
624       cleanup_exit(1);
625     }
626   memset(&sunaddr, 0, sizeof(sunaddr));
627   sunaddr.sun_family = AF_UNIX;
628   strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
629   if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
630     {
631       perror("bind");
632       cleanup_exit(1);
633     }
634   if (listen(sock, 5) < 0)
635     {
636       perror("listen");
637       cleanup_exit(1);
638     }
639
640   /* Fork, and have the parent execute the command, if any, or present the
641      socket data.  The child continues as the authentication agent. */
642   pid = fork();
643   if (pid == -1)
644     {
645       perror("fork");
646       exit(1);
647     }
648   if (pid != 0)
649     { /* Parent - execute the given command. */
650       close(sock);
651       snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
652       if (ac == 0)
653         {
654           format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
655           printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
656                  SSH_AUTHSOCKET_ENV_NAME);
657           printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
658                  SSH_AGENTPID_ENV_NAME);
659           printf("echo Agent pid %d;\n", pid);
660           exit(0);
661         }
662
663       setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
664       setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
665       execvp(av[0], av);
666       perror(av[0]);
667       exit(1);
668     }
669
670   close(0);
671   close(1);
672   close(2);
673
674   if (setsid() == -1)
675     {
676       perror("setsid");
677       cleanup_exit(1);
678     }
679
680   if (atexit(cleanup_socket) < 0)
681     {
682       perror("atexit");
683       cleanup_exit(1);
684     }
685
686   new_socket(AUTH_SOCKET, sock);
687   if (ac > 0)
688     {
689       signal(SIGALRM, check_parent_exists);
690       alarm(10);
691     }
692
693   signal(SIGINT, SIG_IGN);
694   signal(SIGPIPE, SIG_IGN);
695   while (1)
696     {
697       FD_ZERO(&readset);
698       FD_ZERO(&writeset);
699       prepare_select(&readset, &writeset);
700       if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
701         {
702           if (errno == EINTR)
703             continue;
704           exit(1);
705         }
706       after_select(&readset, &writeset);
707     }
708   /*NOTREACHED*/
709 }
This page took 0.739282 seconds and 5 git commands to generate.