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