source: trunk/third/ssh/ssh-agent.c @ 12646

Revision 12646, 21.6 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12645, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2
3ssh-agent.c
4
5Author: Tatu Ylonen <ylo@cs.hut.fi>
6
7Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
8                   All rights reserved
9
10Created: Wed Mar 29 03:46:59 1995 ylo
11
12The authentication agent program.
13
14*/
15
16/*
17 * $Id: ssh-agent.c,v 1.1.1.3 1999-03-08 17:43:27 danw Exp $
18 * $Log: not supported by cvs2svn $
19 * Revision 1.19  1998/01/02  06:21:42  kivinen
20 *      Added -k option. Renamed SSH_AUTHENCATION_SOCKET to
21 *      SSH_AUTH_SOCK.
22 *
23 * Revision 1.18  1997/04/17 04:15:11  kivinen
24 *      Removed some extra variables. Removed some warnings.
25 *      Added check to socket closing that it is really opened.
26 *      Added xstrdups to putenv calls.
27 *
28 * Revision 1.17  1997/04/05 21:58:15  kivinen
29 *      Fixed agent option parsing. Added -- option support, patch from
30 *      Charles M. Hannum <mycroft@gnu.ai.mit.edu>.
31 *      Added closing of agent socket in parent, patch from Charles M.
32 *      Hannum <mycroft@gnu.ai.mit.edu>.
33 *      Added check for existance of O_NOCTTY (patch from
34 *      KOJIMA Hajime <kjm@rins.ryukoku.ac.jp>).
35 *      Added setting of SSH_AGENT_PID when running command too.
36 *
37 * Revision 1.16  1997/03/19 17:39:36  kivinen
38 *      Added -c (csh style shell) and -s (/bin/sh style shell)
39 *      options.
40 *      Fixed /bin/sh command syntax so it will print FOO=1; export
41 *      FOO; instead of export FOO=1, which some shells doesn't
42 *      understand.
43 *
44 * Revision 1.15  1996/11/24 08:27:33  kivinen
45 *      Added new mode for ssh-agent. If no command is given fork to
46 *      background and print commands that can be executed in users
47 *      shell that will set SSH_AUTHENTICATION_SOCKET and
48 *      SSH_AGENT_PID.
49 *
50 * Revision 1.14  1996/11/19 12:09:00  kivinen
51 *      Fixed check when directory needs to be created.
52 *
53 * Revision 1.13  1996/10/29 22:43:55  kivinen
54 *      log -> log_msg. Removed unused sockaddr_in sin.
55 *      Do not define SSH_AUTHENTICATION_SOCKET environment variable
56 *      if the agent could not be started.
57 *
58 * Revision 1.12  1996/10/29 12:34:29  ttsalo
59 *      Agent's behaviour improved: socket is created and listened to
60 *      before forking, and if creation fails, parent still executes
61 *      the specified command (without forking the child).
62 *
63 * Revision 1.11  1996/10/21 16:17:28  ttsalo
64 *       Implemented direct socket handling in ssh-agent
65 *
66 * Revision 1.10  1996/10/20 16:19:34  ttsalo
67 *      Added global variable 'original_real_uid' and it's initialization
68 *
69 * Revision 1.9  1996/10/12 22:19:32  ttsalo
70 *      Return value of mkdir is now checked.
71 *
72 * Revision 1.8  1996/09/27 14:01:33  ttsalo
73 *      Use AF_UNIX_SIZE(sunaddr) instead of sizeof(sunaddr)
74 *
75 * Revision 1.7  1996/09/11 17:56:52  kivinen
76 *      Changed limit of messages from 256 kB to 30 kB.
77 *      Added check that getpwuid succeeds.
78 *
79 * Revision 1.6  1996/09/04 12:41:51  ttsalo
80 *      Minor fixes
81 *
82 * Revision 1.5  1996/08/29 14:51:26  ttsalo
83 *      Agent-socket directory handling implemented
84 *
85 * Revision 1.4  1996/08/21 20:43:55  ttsalo
86 *      Made ssh-agent use a different, more secure way of storing
87 *      it's initial socket.
88 *
89 * Revision 1.3  1996/05/28 12:44:52  ylo
90 *      Remove unix-domain socket if a killed by a signal.
91 *
92 * Revision 1.2  1996/04/26 00:24:47  ylo
93 *      Fixed memory leaks.
94 *      Fixed free -> xfree (xmalloced data).
95 *
96 * Revision 1.1.1.1  1996/02/18 21:38:12  ylo
97 *      Imported ssh-1.2.13.
98 *
99 * Revision 1.6  1995/09/21  17:13:31  ylo
100 *      Support AF_UNIX_SIZE.
101 *
102 * Revision 1.5  1995/08/29  22:25:22  ylo
103 *      Added compatibility support for various authentication
104 *      protocol versions.
105 *      Fixed bug in deleting identity.
106 *      Added remove_all.
107 *      New file descriptor code.
108 *
109 * Revision 1.4  1995/08/21  23:27:31  ylo
110 *      Added support for session_id and response_type in
111 *      authentication requests.
112 *
113 * Revision 1.3  1995/07/26  23:29:13  ylo
114 *      Print software version with usage message.
115 *
116 * Revision 1.2  1995/07/13  01:38:26  ylo
117 *      Removed "Last modified" header.
118 *      Added cvs log.
119 *
120 * $Endlog$
121 */
122
123#include "includes.h"
124#include "ssh.h"
125#include "rsa.h"
126#include "randoms.h"
127#include "authfd.h"
128#include "buffer.h"
129#include "bufaux.h"
130#include "xmalloc.h"
131#include "packet.h"
132#include "md5.h"
133#include "getput.h"
134#include "mpaux.h"
135
136typedef struct
137{
138  int fd;
139  enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_SOCKET_FD } type;
140  Buffer input;
141  Buffer output;
142} SocketEntry;
143
144unsigned int sockets_alloc = 0;
145SocketEntry *sockets = NULL;
146
147typedef struct
148{
149  RSAPrivateKey key;
150  char *comment;
151} Identity;
152
153unsigned int num_identities = 0;
154Identity *identities = NULL;
155
156uid_t original_real_uid = 0;
157
158int max_fd = 0;
159
160void process_request_identity(SocketEntry *e)
161{
162  Buffer msg;
163  int i;
164
165  buffer_init(&msg);
166  buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);
167  buffer_put_int(&msg, num_identities);
168  for (i = 0; i < num_identities; i++)
169    {
170      buffer_put_int(&msg, identities[i].key.bits);
171      buffer_put_mp_int(&msg, &identities[i].key.e);
172      buffer_put_mp_int(&msg, &identities[i].key.n);
173      buffer_put_string(&msg, identities[i].comment,
174                        strlen(identities[i].comment));
175    }
176  buffer_put_int(&e->output, buffer_len(&msg));
177  buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
178  buffer_free(&msg);
179}
180
181void process_authentication_challenge(SocketEntry *e)
182{
183  int i, pub_bits;
184  MP_INT pub_e, pub_n, challenge;
185  Buffer msg;
186  struct MD5Context md;
187  unsigned char buf[32], mdbuf[16], session_id[16];
188  unsigned int response_type;
189
190  buffer_init(&msg);
191  mpz_init(&pub_e);
192  mpz_init(&pub_n);
193  mpz_init(&challenge);
194  pub_bits = buffer_get_int(&e->input);
195  buffer_get_mp_int(&e->input, &pub_e);
196  buffer_get_mp_int(&e->input, &pub_n);
197  buffer_get_mp_int(&e->input, &challenge);
198  if (buffer_len(&e->input) == 0)
199    {
200      /* Compatibility code for old servers. */
201      memset(session_id, 0, 16);
202      response_type = 0;
203    }
204  else
205    {
206      /* New code. */
207      buffer_get(&e->input, (char *)session_id, 16);
208      response_type = buffer_get_int(&e->input);
209    }
210  for (i = 0; i < num_identities; i++)
211    if (pub_bits == identities[i].key.bits &&
212        mpz_cmp(&pub_e, &identities[i].key.e) == 0 &&
213        mpz_cmp(&pub_n, &identities[i].key.n) == 0)
214      {
215        /* Decrypt the challenge using the private key. */
216        rsa_private_decrypt(&challenge, &challenge, &identities[i].key);
217
218        /* Compute the desired response. */
219        switch (response_type)
220          {
221          case 0: /* As of protocol 1.0 */
222            /* This response type is no longer supported. */
223            log_msg("Compatibility with ssh protocol 1.0 no longer supported.");
224            buffer_put_char(&msg, SSH_AGENT_FAILURE);
225            goto send;
226
227          case 1: /* As of protocol 1.1 */
228            /* The response is MD5 of decrypted challenge plus session id. */
229            mp_linearize_msb_first(buf, 32, &challenge);
230            MD5Init(&md);
231            MD5Update(&md, buf, 32);
232            MD5Update(&md, session_id, 16);
233            MD5Final(mdbuf, &md);
234            break;
235
236          default:
237            fatal("process_authentication_challenge: bad response_type %d",
238                  response_type);
239            break;
240          }
241
242        /* Send the response. */
243        buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
244        for (i = 0; i < 16; i++)
245          buffer_put_char(&msg, mdbuf[i]);
246
247        goto send;
248      }
249  /* Unknown identity.  Send failure. */
250  buffer_put_char(&msg, SSH_AGENT_FAILURE);
251 send:
252  buffer_put_int(&e->output, buffer_len(&msg));
253  buffer_append(&e->output, buffer_ptr(&msg),
254                buffer_len(&msg));
255  buffer_free(&msg);
256  mpz_clear(&pub_e);
257  mpz_clear(&pub_n);
258  mpz_clear(&challenge);
259}
260
261void process_remove_identity(SocketEntry *e)
262{
263  unsigned int bits;
264  MP_INT dummy, n;
265  unsigned int i;
266 
267  mpz_init(&dummy);
268  mpz_init(&n);
269 
270  /* Get the key from the packet. */
271  bits = buffer_get_int(&e->input);
272  buffer_get_mp_int(&e->input, &dummy);
273  buffer_get_mp_int(&e->input, &n);
274 
275  /* Check if we have the key. */
276  for (i = 0; i < num_identities; i++)
277    if (mpz_cmp(&identities[i].key.n, &n) == 0)
278      {
279        /* We have this key.  Free the old key.  Since we don\'t want to leave
280           empty slots in the middle of the array, we actually free the
281           key there and copy data from the last entry. */
282        rsa_clear_private_key(&identities[i].key);
283        xfree(identities[i].comment);
284        if (i < num_identities - 1)
285          identities[i] = identities[num_identities - 1];
286        num_identities--;
287        if (num_identities == 0)
288          xfree(identities);
289        mpz_clear(&dummy);
290        mpz_clear(&n);
291
292        /* Send success. */
293        buffer_put_int(&e->output, 1);
294        buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
295        return;
296      }
297  /* We did not have the key. */
298  mpz_clear(&dummy);
299  mpz_clear(&n);
300
301  /* Send failure. */
302  buffer_put_int(&e->output, 1);
303  buffer_put_char(&e->output, SSH_AGENT_FAILURE);
304}
305
306/* Removes all identities from the agent. */
307
308void process_remove_all_identities(SocketEntry *e)
309{
310  unsigned int i;
311 
312  /* Loop over all identities and clear the keys. */
313  for (i = 0; i < num_identities; i++)
314    {
315      rsa_clear_private_key(&identities[i].key);
316      xfree(identities[i].comment);
317    }
318
319  /* Mark that there are no identities. */
320  num_identities = 0;
321  xfree(identities);
322 
323  /* Send success. */
324  buffer_put_int(&e->output, 1);
325  buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
326  return;
327}
328
329/* Adds an identity to the agent. */
330
331void process_add_identity(SocketEntry *e)
332{
333  RSAPrivateKey *k;
334  int i;
335
336  if (num_identities == 0)
337    identities = xmalloc(sizeof(Identity));
338  else
339    identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
340  k = &identities[num_identities].key;
341  k->bits = buffer_get_int(&e->input);
342  mpz_init(&k->n);
343  buffer_get_mp_int(&e->input, &k->n);
344  mpz_init(&k->e);
345  buffer_get_mp_int(&e->input, &k->e);
346  mpz_init(&k->d);
347  buffer_get_mp_int(&e->input, &k->d);
348  mpz_init(&k->u);
349  buffer_get_mp_int(&e->input, &k->u);
350  mpz_init(&k->p);
351  buffer_get_mp_int(&e->input, &k->p);
352  mpz_init(&k->q);
353  buffer_get_mp_int(&e->input, &k->q);
354  identities[num_identities].comment = buffer_get_string(&e->input, NULL);
355
356  /* Check if we already have the key. */
357  for (i = 0; i < num_identities; i++)
358    if (mpz_cmp(&identities[i].key.n, &k->n) == 0)
359      {
360        /* We already have this key.  Clear and free the new data and
361           return success. */
362        rsa_clear_private_key(k);
363        xfree(identities[num_identities].comment);
364
365        /* Send success. */
366        buffer_put_int(&e->output, 1);
367        buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
368        return;
369      }
370
371  /* Increment the number of identities. */
372  num_identities++;
373 
374  /* Send a success message. */
375  buffer_put_int(&e->output, 1);
376  buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
377}
378
379void process_message(SocketEntry *e)
380{
381  unsigned int msg_len;
382  unsigned int type;
383  unsigned char *cp;
384  if (buffer_len(&e->input) < 5)
385    return; /* Incomplete message. */
386  cp = (unsigned char *)buffer_ptr(&e->input);
387  msg_len = GET_32BIT(cp);
388  if (msg_len > 30 * 1024)
389    {
390      shutdown(e->fd, 2);
391      close(e->fd);
392      e->type = AUTH_UNUSED;
393      buffer_free(&e->input);
394      buffer_free(&e->output);
395      return;
396    }
397  if (buffer_len(&e->input) < msg_len + 4)
398    return;
399  buffer_consume(&e->input, 4);
400  type = buffer_get_char(&e->input);
401  switch (type)
402    {
403    case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
404      process_request_identity(e);
405      break;
406    case SSH_AGENTC_RSA_CHALLENGE:
407      process_authentication_challenge(e);
408      break;
409    case SSH_AGENTC_ADD_RSA_IDENTITY:
410      process_add_identity(e);
411      break;
412    case SSH_AGENTC_REMOVE_RSA_IDENTITY:
413      process_remove_identity(e);
414      break;
415    case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
416      process_remove_all_identities(e);
417      break;
418    default:
419      /* Unknown message.  Respond with failure. */
420      error("Unknown message %d", type);
421      buffer_clear(&e->input);
422      buffer_put_int(&e->output, 1);
423      buffer_put_char(&e->output, SSH_AGENT_FAILURE);
424      break;
425    }
426}
427
428void new_socket(int type, int fd)
429{
430  unsigned int i, old_alloc;
431#if defined(O_NONBLOCK) && !defined(O_NONBLOCK_BROKEN)
432  if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
433    error("fcntl O_NONBLOCK: %s", strerror(errno));
434#else /* O_NONBLOCK && !O_NONBLOCK_BROKEN */
435  if (fcntl(fd, F_SETFL, O_NDELAY) < 0)
436    error("fcntl O_NDELAY: %s", strerror(errno));
437#endif /* O_NONBLOCK && !O_NONBLOCK_BROKEN */
438
439  if (fd > max_fd)
440    max_fd = fd;
441
442  for (i = 0; i < sockets_alloc; i++)
443    if (sockets[i].type == AUTH_UNUSED)
444      {
445        sockets[i].fd = fd;
446        sockets[i].type = type;
447        buffer_init(&sockets[i].input);
448        buffer_init(&sockets[i].output);
449        return;
450      }
451  old_alloc = sockets_alloc;
452  sockets_alloc += 10;
453  if (sockets)
454    sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
455  else
456    sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
457  for (i = old_alloc; i < sockets_alloc; i++)
458    sockets[i].type = AUTH_UNUSED;
459  sockets[old_alloc].type = type;
460  sockets[old_alloc].fd = fd;
461  buffer_init(&sockets[old_alloc].input);
462  buffer_init(&sockets[old_alloc].output);
463}
464
465void prepare_select(fd_set *readset, fd_set *writeset)
466{
467  unsigned int i;
468  for (i = 0; i < sockets_alloc; i++)
469    switch (sockets[i].type)
470      {
471      case AUTH_SOCKET_FD:
472      case AUTH_SOCKET:
473        FD_SET(sockets[i].fd, readset);
474        if (buffer_len(&sockets[i].output) > 0)
475          FD_SET(sockets[i].fd, writeset);
476        break;
477      case AUTH_UNUSED:
478        break;
479      default:
480        fatal("Unknown socket type %d", sockets[i].type);
481        break;
482      }
483}
484
485void after_select(fd_set *readset, fd_set *writeset)
486{
487  unsigned int i;
488  int len, sock;
489  char buf[1024];
490  struct sockaddr_un sunaddr;
491
492  for (i = 0; i < sockets_alloc; i++)
493    switch (sockets[i].type)
494      {
495      case AUTH_UNUSED:
496        break;
497      case AUTH_SOCKET:
498        if (FD_ISSET(sockets[i].fd, readset))
499          {
500            len = AF_UNIX_SIZE(sunaddr);
501            sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);
502            if (sock < 0)
503              {
504                perror("accept from AUTH_SOCKET");
505                break;
506              }
507            new_socket(AUTH_SOCKET_FD, sock);
508          }
509        break;
510      case AUTH_SOCKET_FD:
511        if (buffer_len(&sockets[i].output) > 0 &&
512            FD_ISSET(sockets[i].fd, writeset))
513          {
514            len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
515                        buffer_len(&sockets[i].output));
516            if (len <= 0)
517              {
518                shutdown(sockets[i].fd, 2);
519                close(sockets[i].fd);
520                sockets[i].type = AUTH_UNUSED;
521                buffer_free(&sockets[i].input);
522                buffer_free(&sockets[i].output);
523                break;
524              }
525            buffer_consume(&sockets[i].output, len);
526          }
527        if (FD_ISSET(sockets[i].fd, readset))
528          {
529            len = read(sockets[i].fd, buf, sizeof(buf));
530            if (len <= 0)
531              {
532                shutdown(sockets[i].fd, 2);
533                close(sockets[i].fd);
534                sockets[i].type = AUTH_UNUSED;
535                buffer_free(&sockets[i].input);
536                buffer_free(&sockets[i].output);
537                break;
538              }
539            buffer_append(&sockets[i].input, buf, len);
540            process_message(&sockets[i]);
541          }
542        break;
543      default:
544        fatal("Unknown type %d", sockets[i].type);
545      }
546}
547
548int parent_pid = -1;
549char socket_dir_name[1024];
550char socket_name[1024];
551
552RETSIGTYPE check_parent_exists(int sig)
553{
554  if (kill(parent_pid, 0) < 0)
555    {
556      remove(socket_name);
557      rmdir(socket_dir_name);
558      /* printf("Parent has died - Authentication agent exiting.\n"); */
559      exit(1);
560    }
561  signal(SIGALRM, check_parent_exists);
562  alarm(10);
563}
564
565RETSIGTYPE remove_socket_on_signal(int sig)
566{
567  remove(socket_name);
568  rmdir(socket_dir_name);
569  /* fprintf(stderr, "Received signal %d - Auth. agent exiting.\n", sig); */
570  exit(1);
571}
572
573int main(int ac, char **av)
574{
575  fd_set readset, writeset;
576  char buf[1024];
577  int sock = -1, creation_failed = 1, pid;
578  struct sockaddr_un sunaddr;
579  struct passwd *pw;
580  struct stat st;
581  int binsh = 1, erflg = 0;
582  int do_kill = 0;
583  char *sh;
584 
585  int i;
586  int ret;
587
588  sh = getenv("SHELL");
589  if (sh != NULL && strlen(sh) > 3 &&
590      sh[strlen(sh)-3] == 'c')
591    binsh = 0;
592 
593  while (ac > 1)
594    {
595      if (av[1][0] == '-')
596        {
597          if (av[1][1] == '-' && av[1][2] == '\0')
598            {
599              av++;
600              ac--;
601              break;
602            }
603          for(i = 1; av[1][i] != '\0'; i++)
604            {
605              switch (av[1][i])
606                {
607                case 's': binsh = 1; break;
608                case 'c': binsh = 0; break;
609                case 'k': do_kill = 1; break;
610                default: erflg++; break;
611                }
612            }
613          av++;
614          ac--;
615        }
616      else
617        break;
618    }
619  if (erflg)
620    {
621      fprintf(stderr, "Usage: ssh-agent [-csk] [command [args...]]\n");
622      exit(0);
623    }
624
625  original_real_uid = getuid();
626
627  if (do_kill)
628    {
629      if (getenv("SSH_AGENT_PID") == NULL)
630        {
631          fprintf(stderr, "No SSH_AGENT_PID environment variable found\n");
632          exit(1);
633        }
634      pid = atoi(getenv("SSH_AGENT_PID"));
635      if (pid == 0)
636        {
637          fprintf(stderr, "Invalid SSH_AGENT_PID value: %s\n",
638                  getenv("SSH_AGENT_PID"));
639          exit(1);
640        }
641      if (kill(pid, SIGTERM) != 0)
642        {
643          fprintf(stderr, "Kill failed, either no agent process or permission denied\n");
644          exit(1);
645        }
646      if (getenv(SSH_AUTHSOCKET_ENV_NAME) == NULL)
647        {
648          fprintf(stderr, "No %s environment variable found\n",
649                  SSH_AUTHSOCKET_ENV_NAME);
650        }
651      else
652        {
653          if (!binsh)           /* shell is *csh */
654            printf("unsetenv %s;\n", SSH_AUTHSOCKET_ENV_NAME);
655          else
656              printf("unset %s;\n", SSH_AUTHSOCKET_ENV_NAME);
657        }
658      if (!binsh)
659        {                       /* shell is *csh */
660          printf("unsetenv SSH_AGENT_PID;\n");
661          printf("echo Agent pid %d killed;\n", pid);
662        }
663      else
664        {
665          printf("unset SSH_AGENT_PID;\n");
666          printf("echo Agent pid %d killed;\n", pid);
667        }
668      exit(0);
669    }
670 
671  /* The agent uses SSH_AUTHENTICATION_SOCKET. */
672 
673  parent_pid = getpid();
674  pw = getpwuid(getuid());
675  if (pw == NULL)
676    {
677      fprintf(stderr, "Unknown user uid = %d\n", (int) getuid());
678      exit(1);
679    }
680 
681  sprintf(socket_dir_name, SSH_AGENT_SOCKET_DIR, pw->pw_name);
682
683  /* Start setting up the socket. Do it before forking to guarantee
684     that the socket exists when the parent starts executing the
685     command. Also, if something fails before fork, just execute the
686     command and don't bother forking the child. */
687 
688  /* Check that the per-user socket directory either doesn't exist
689     or has good modes */
690 
691  ret = stat(socket_dir_name, &st);
692  if (ret == -1 && errno != ENOENT)
693    {
694      perror("stat");
695      goto fail_socket_setup;
696    }
697  if (ret == -1 && errno == ENOENT && mkdir(socket_dir_name, S_IRWXU) != 0)
698    {
699      perror("mkdir");
700      goto fail_socket_setup;
701    }
702
703  /* Check the owner and permissions */
704  if (stat(socket_dir_name, &st) != 0 || pw->pw_uid != st.st_uid ||
705      (st.st_mode & 077) != 0)
706    {
707      fprintf(stderr, "Bad modes or owner for directory \'%s\'\n",
708              socket_dir_name);
709      goto fail_socket_setup;
710    }
711
712  sprintf(socket_name,
713          SSH_AGENT_SOCKET_DIR"/"SSH_AGENT_SOCKET,
714          pw->pw_name, (int)getpid());
715
716  /* Check that socket doesn't exist */
717  ret = stat(socket_name, &st);
718  if (ret != -1 && errno != ENOENT)
719    {
720      fprintf(stderr,
721              "\'%s\' already exists (ssh-agent already running?)\n",
722              socket_name);
723      goto fail_socket_setup;
724    }
725  sock = socket(AF_UNIX, SOCK_STREAM, 0);
726  if (sock < 0)
727    {
728      perror("socket");
729      goto fail_socket_setup;
730    }
731  memset(&sunaddr, 0, AF_UNIX_SIZE(sunaddr));
732  sunaddr.sun_family = AF_UNIX;
733  strncpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
734  if (bind(sock, (struct sockaddr *)&sunaddr, AF_UNIX_SIZE(sunaddr)) < 0)
735    {
736      perror("bind");
737      close(sock);
738      goto fail_socket_setup;
739    }
740  if (chmod(socket_name, 0700) < 0)
741    {
742      perror("chmod");
743      close(sock);
744      goto fail_socket_setup;
745    }
746  if (listen(sock, 5) < 0)
747    {
748      perror("listen");
749      close(sock);
750      goto fail_socket_setup;
751    }
752 
753  /* Everything ok so far, so permit forking. */
754  creation_failed = 0;
755 
756fail_socket_setup:
757  /* If not creation_failed, fork, and have the parent execute the command.
758     The child continues as the authentication agent. If creation failed,
759     don't fork the child and forget the socket */
760  if (!creation_failed)
761    pid = fork();
762  else
763    pid = 1;                    /* Run only parent code */
764  if (pid != 0)
765    {
766      /* Parent - execute the given command, if given command. */
767
768      /* Close the agent socket */
769      if (sock != -1)
770        close(sock);
771     
772      if (ac == 1)
773        {                       /* No command given print socket name */
774          if (creation_failed)
775            {
776              printf("echo Agent creation failed, no agent started\n");
777              exit(0);
778            }
779         
780          if (!binsh)
781            {                   /* shell is *csh */
782              printf("setenv %s %s;\n", SSH_AUTHSOCKET_ENV_NAME,
783                     socket_name);
784              printf("setenv SSH_AGENT_PID %d;\n", pid);
785              printf("echo Agent pid %d;\n", pid);
786            }
787          else
788            {
789              printf("%s=%s; export %s;\n", SSH_AUTHSOCKET_ENV_NAME,
790                     socket_name, SSH_AUTHSOCKET_ENV_NAME);
791              printf("SSH_AGENT_PID=%d; export SSH_AGENT_PID;\n", pid);
792              printf("echo Agent pid %d;\n", pid);
793            }
794          exit(0);
795        }
796      /* Command given run it */
797      if (!creation_failed)
798        {
799          sprintf(buf, "%s=%s", SSH_AUTHSOCKET_ENV_NAME, socket_name);
800          putenv(xstrdup(buf));
801          sprintf(buf, "SSH_AGENT_PID=%d", pid);
802          putenv(xstrdup(buf));
803        }
804      execvp(av[1], av + 1);
805      perror(av[1]);
806      exit(1);
807    }
808
809  close(0);
810  close(1);
811  close(2);
812  chdir("/");
813 
814  /* Disconnect from the controlling tty. */
815#ifdef TIOCNOTTY
816  {
817    int fd;
818#ifdef O_NOCTTY
819    fd = open("/dev/tty", O_RDWR | O_NOCTTY);
820#else
821    fd = open("/dev/tty", O_RDWR);
822#endif
823    if (fd >= 0)
824      {
825        (void)ioctl(fd, TIOCNOTTY, NULL);
826        close(fd);
827      }
828  }
829#endif /* TIOCNOTTY */
830#ifdef HAVE_SETSID
831#ifdef ultrix
832  setpgrp(0, 0);
833#else /* ultrix */
834  if (setsid() < 0)
835    error("setsid: %.100s", strerror(errno));
836#endif
837#endif /* HAVE_SETSID */
838 
839  new_socket(AUTH_SOCKET, sock);
840  if (ac != 1)
841    {
842      signal(SIGALRM, check_parent_exists);
843      alarm(10);
844    }
845  signal(SIGHUP, remove_socket_on_signal);
846  signal(SIGTERM, remove_socket_on_signal);
847
848  signal(SIGINT, SIG_IGN);
849  signal(SIGPIPE, SIG_IGN);
850  while (1)
851    {
852      FD_ZERO(&readset);
853      FD_ZERO(&writeset);
854      prepare_select(&readset, &writeset);
855      if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
856        {
857          if (errno == EINTR)
858            continue;
859          perror("select");
860          exit(1);
861        }
862      after_select(&readset, &writeset);
863    }
864  /*NOTREACHED*/
865}
Note: See TracBrowser for help on using the repository browser.