[BACK]Return to ssh-agent.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Diff for /src/usr.bin/ssh/ssh-agent.c between version 1.20 and 1.21

version 1.20, 1999/11/19 10:20:51 version 1.21, 1999/11/23 22:25:55
Line 30 
Line 30 
   
 #include <ssl/md5.h>  #include <ssl/md5.h>
   
 typedef struct  typedef struct {
 {          int fd;
   int fd;          enum {
   enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type;                  AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION
   Buffer input;          } type;
   Buffer output;          Buffer input;
           Buffer output;
 } SocketEntry;  } SocketEntry;
   
 unsigned int sockets_alloc = 0;  unsigned int sockets_alloc = 0;
 SocketEntry *sockets = NULL;  SocketEntry *sockets = NULL;
   
 typedef struct  typedef struct {
 {          RSA *key;
   RSA *key;          char *comment;
   char *comment;  
 } Identity;  } Identity;
   
 unsigned int num_identities = 0;  unsigned int num_identities = 0;
Line 64 
Line 64 
 void  void
 process_request_identity(SocketEntry *e)  process_request_identity(SocketEntry *e)
 {  {
   Buffer msg;          Buffer msg;
   int i;          int i;
   
   buffer_init(&msg);          buffer_init(&msg);
   buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);          buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);
   buffer_put_int(&msg, num_identities);          buffer_put_int(&msg, num_identities);
   for (i = 0; i < num_identities; i++)          for (i = 0; i < num_identities; i++) {
     {                  buffer_put_int(&msg, BN_num_bits(identities[i].key->n));
       buffer_put_int(&msg, BN_num_bits(identities[i].key->n));                  buffer_put_bignum(&msg, identities[i].key->e);
       buffer_put_bignum(&msg, identities[i].key->e);                  buffer_put_bignum(&msg, identities[i].key->n);
       buffer_put_bignum(&msg, identities[i].key->n);                  buffer_put_string(&msg, identities[i].comment,
       buffer_put_string(&msg, identities[i].comment,                                    strlen(identities[i].comment));
                         strlen(identities[i].comment));          }
     }          buffer_put_int(&e->output, buffer_len(&msg));
   buffer_put_int(&e->output, buffer_len(&msg));          buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
   buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));          buffer_free(&msg);
   buffer_free(&msg);  
 }  }
   
 void  void
 process_authentication_challenge(SocketEntry *e)  process_authentication_challenge(SocketEntry *e)
 {  {
   int i, pub_bits, len;          int i, pub_bits, len;
   BIGNUM *pub_e, *pub_n, *challenge;          BIGNUM *pub_e, *pub_n, *challenge;
   Buffer msg;          Buffer msg;
   MD5_CTX md;          MD5_CTX md;
   unsigned char buf[32], mdbuf[16], session_id[16];          unsigned char buf[32], mdbuf[16], session_id[16];
   unsigned int response_type;          unsigned int response_type;
   
   buffer_init(&msg);          buffer_init(&msg);
   pub_e = BN_new();          pub_e = BN_new();
   pub_n = BN_new();          pub_n = BN_new();
   challenge = BN_new();          challenge = BN_new();
   pub_bits = buffer_get_int(&e->input);          pub_bits = buffer_get_int(&e->input);
   buffer_get_bignum(&e->input, pub_e);          buffer_get_bignum(&e->input, pub_e);
   buffer_get_bignum(&e->input, pub_n);          buffer_get_bignum(&e->input, pub_n);
   buffer_get_bignum(&e->input, challenge);          buffer_get_bignum(&e->input, challenge);
   if (buffer_len(&e->input) == 0)          if (buffer_len(&e->input) == 0) {
     {                  /* Compatibility code for old servers. */
       /* Compatibility code for old servers. */                  memset(session_id, 0, 16);
       memset(session_id, 0, 16);                  response_type = 0;
       response_type = 0;          } else {
     }                  /* New code. */
   else                  buffer_get(&e->input, (char *) session_id, 16);
     {                  response_type = buffer_get_int(&e->input);
       /* New code. */          }
       buffer_get(&e->input, (char *)session_id, 16);          for (i = 0; i < num_identities; i++)
       response_type = buffer_get_int(&e->input);                  if (pub_bits == BN_num_bits(identities[i].key->n) &&
     }                      BN_cmp(pub_e, identities[i].key->e) == 0 &&
   for (i = 0; i < num_identities; i++)                      BN_cmp(pub_n, identities[i].key->n) == 0) {
     if (pub_bits == BN_num_bits(identities[i].key->n) &&                          /* Decrypt the challenge using the private key. */
         BN_cmp(pub_e, identities[i].key->e) == 0 &&                          rsa_private_decrypt(challenge, challenge, identities[i].key);
         BN_cmp(pub_n, identities[i].key->n) == 0)  
       {  
         /* Decrypt the challenge using the private key. */  
         rsa_private_decrypt(challenge, challenge, identities[i].key);  
   
         /* Compute the desired response. */                          /* Compute the desired response. */
         switch (response_type)                          switch (response_type) {
           {                          case 0:/* As of protocol 1.0 */
           case 0: /* As of protocol 1.0 */                                  /* This response type is no longer supported. */
             /* This response type is no longer supported. */                                  log("Compatibility with ssh protocol 1.0 no longer supported.");
             log("Compatibility with ssh protocol 1.0 no longer supported.");                                  buffer_put_char(&msg, SSH_AGENT_FAILURE);
             buffer_put_char(&msg, SSH_AGENT_FAILURE);                                  goto send;
             goto send;  
   
           case 1: /* As of protocol 1.1 */                          case 1:/* As of protocol 1.1 */
             /* The response is MD5 of decrypted challenge plus session id. */                                  /* The response is MD5 of decrypted challenge plus session id. */
             len = BN_num_bytes(challenge);                                  len = BN_num_bytes(challenge);
   
             if (len <= 0 || len > 32) {                                  if (len <= 0 || len > 32) {
               fatal("process_authentication_challenge: "                                          fatal("process_authentication_challenge: "
                     "bad challenge length %d", len);                                           "bad challenge length %d", len);
             }                                  }
                                   memset(buf, 0, 32);
                                   BN_bn2bin(challenge, buf + 32 - len);
                                   MD5_Init(&md);
                                   MD5_Update(&md, buf, 32);
                                   MD5_Update(&md, session_id, 16);
                                   MD5_Final(mdbuf, &md);
                                   break;
   
             memset(buf, 0, 32);                          default:
             BN_bn2bin(challenge, buf + 32 - len);                                  fatal("process_authentication_challenge: bad response_type %d",
             MD5_Init(&md);                                        response_type);
             MD5_Update(&md, buf, 32);                                  break;
             MD5_Update(&md, session_id, 16);                          }
             MD5_Final(mdbuf, &md);  
             break;  
   
           default:                          /* Send the response. */
             fatal("process_authentication_challenge: bad response_type %d",                          buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
                   response_type);                          for (i = 0; i < 16; i++)
             break;                                  buffer_put_char(&msg, mdbuf[i]);
           }  
   
         /* Send the response. */                          goto send;
         buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);                  }
         for (i = 0; i < 16; i++)          /* Unknown identity.  Send failure. */
           buffer_put_char(&msg, mdbuf[i]);          buffer_put_char(&msg, SSH_AGENT_FAILURE);
   send:
         goto send;          buffer_put_int(&e->output, buffer_len(&msg));
       }          buffer_append(&e->output, buffer_ptr(&msg),
   /* Unknown identity.  Send failure. */                        buffer_len(&msg));
   buffer_put_char(&msg, SSH_AGENT_FAILURE);          buffer_free(&msg);
  send:          BN_clear_free(pub_e);
   buffer_put_int(&e->output, buffer_len(&msg));          BN_clear_free(pub_n);
   buffer_append(&e->output, buffer_ptr(&msg),          BN_clear_free(challenge);
                 buffer_len(&msg));  
   buffer_free(&msg);  
   BN_clear_free(pub_e);  
   BN_clear_free(pub_n);  
   BN_clear_free(challenge);  
 }  }
   
 void  void
 process_remove_identity(SocketEntry *e)  process_remove_identity(SocketEntry *e)
 {  {
   unsigned int bits;          unsigned int bits;
   unsigned int i;          unsigned int i;
   BIGNUM *dummy, *n;          BIGNUM *dummy, *n;
   
   dummy = BN_new();  
   n = BN_new();  
   
   /* Get the key from the packet. */  
   bits = buffer_get_int(&e->input);  
   buffer_get_bignum(&e->input, dummy);  
   buffer_get_bignum(&e->input, n);  
   
   if (bits != BN_num_bits(n))          dummy = BN_new();
     error("Warning: keysize mismatch: actual %d, announced %d",          n = BN_new();
           BN_num_bits(n), bits);  
   
   /* Check if we have the key. */  
   for (i = 0; i < num_identities; i++)  
     if (BN_cmp(identities[i].key->n, n) == 0)  
       {  
         /* We have this key.  Free the old key.  Since we don\'t want to leave  
            empty slots in the middle of the array, we actually free the  
            key there and copy data from the last entry. */  
         RSA_free(identities[i].key);  
         xfree(identities[i].comment);  
         if (i < num_identities - 1)  
           identities[i] = identities[num_identities - 1];  
         num_identities--;  
         BN_clear_free(dummy);  
         BN_clear_free(n);  
   
         /* Send success. */          /* Get the key from the packet. */
         buffer_put_int(&e->output, 1);          bits = buffer_get_int(&e->input);
         buffer_put_char(&e->output, SSH_AGENT_SUCCESS);          buffer_get_bignum(&e->input, dummy);
         return;          buffer_get_bignum(&e->input, n);
       }  
   /* We did not have the key. */  
   BN_clear(dummy);  
   BN_clear(n);  
   
   /* Send failure. */          if (bits != BN_num_bits(n))
   buffer_put_int(&e->output, 1);                  error("Warning: keysize mismatch: actual %d, announced %d",
   buffer_put_char(&e->output, SSH_AGENT_FAILURE);                        BN_num_bits(n), bits);
   
           /* Check if we have the key. */
           for (i = 0; i < num_identities; i++)
                   if (BN_cmp(identities[i].key->n, n) == 0) {
                           /* We have this key.  Free the old key.  Since we
                              don\'t want to leave empty slots in the middle
                              of the array, we actually free the key there
                              and copy data from the last entry. */
                           RSA_free(identities[i].key);
                           xfree(identities[i].comment);
                           if (i < num_identities - 1)
                                   identities[i] = identities[num_identities - 1];
                           num_identities--;
                           BN_clear_free(dummy);
                           BN_clear_free(n);
   
                           /* Send success. */
                           buffer_put_int(&e->output, 1);
                           buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
                           return;
                   }
           /* We did not have the key. */
           BN_clear(dummy);
           BN_clear(n);
   
           /* Send failure. */
           buffer_put_int(&e->output, 1);
           buffer_put_char(&e->output, SSH_AGENT_FAILURE);
 }  }
   
 /* Removes all identities from the agent. */  /* Removes all identities from the agent. */
Line 225 
Line 218 
 void  void
 process_remove_all_identities(SocketEntry *e)  process_remove_all_identities(SocketEntry *e)
 {  {
   unsigned int i;          unsigned int i;
   
   /* Loop over all identities and clear the keys. */  
   for (i = 0; i < num_identities; i++)  
     {  
       RSA_free(identities[i].key);  
       xfree(identities[i].comment);  
     }  
   
   /* Mark that there are no identities. */          /* Loop over all identities and clear the keys. */
   num_identities = 0;          for (i = 0; i < num_identities; i++) {
                   RSA_free(identities[i].key);
                   xfree(identities[i].comment);
           }
   
   /* Send success. */          /* Mark that there are no identities. */
   buffer_put_int(&e->output, 1);          num_identities = 0;
   buffer_put_char(&e->output, SSH_AGENT_SUCCESS);  
   return;          /* Send success. */
           buffer_put_int(&e->output, 1);
           buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
           return;
 }  }
   
 /* Adds an identity to the agent. */  /* Adds an identity to the agent. */
Line 248 
Line 240 
 void  void
 process_add_identity(SocketEntry *e)  process_add_identity(SocketEntry *e)
 {  {
   RSA *k;          RSA *k;
   int i;          int i;
   BIGNUM *aux;          BIGNUM *aux;
   BN_CTX *ctx;          BN_CTX *ctx;
   
   if (num_identities == 0)  
     identities = xmalloc(sizeof(Identity));  
   else  
     identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));  
   
   identities[num_identities].key = RSA_new();          if (num_identities == 0)
   k = identities[num_identities].key;                  identities = xmalloc(sizeof(Identity));
   buffer_get_int(&e->input); /* bits */          else
   k->n = BN_new();                  identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
   buffer_get_bignum(&e->input, k->n);  
   k->e = BN_new();  
   buffer_get_bignum(&e->input, k->e);  
   k->d = BN_new();  
   buffer_get_bignum(&e->input, k->d);  
   k->iqmp = BN_new();  
   buffer_get_bignum(&e->input, k->iqmp);  
   /* SSH and SSL have p and q swapped */  
   k->q = BN_new();  
   buffer_get_bignum(&e->input, k->q); /* p */  
   k->p = BN_new();  
   buffer_get_bignum(&e->input, k->p); /* q */  
   
   /* Generate additional parameters */          identities[num_identities].key = RSA_new();
   aux = BN_new();          k = identities[num_identities].key;
   ctx = BN_CTX_new();          buffer_get_int(&e->input);      /* bits */
           k->n = BN_new();
           buffer_get_bignum(&e->input, k->n);
           k->e = BN_new();
           buffer_get_bignum(&e->input, k->e);
           k->d = BN_new();
           buffer_get_bignum(&e->input, k->d);
           k->iqmp = BN_new();
           buffer_get_bignum(&e->input, k->iqmp);
           /* SSH and SSL have p and q swapped */
           k->q = BN_new();
           buffer_get_bignum(&e->input, k->q);     /* p */
           k->p = BN_new();
           buffer_get_bignum(&e->input, k->p);     /* q */
   
   BN_sub(aux, k->q, BN_value_one());          /* Generate additional parameters */
   k->dmq1 = BN_new();          aux = BN_new();
   BN_mod(k->dmq1, k->d, aux, ctx);          ctx = BN_CTX_new();
   
   BN_sub(aux, k->p, BN_value_one());          BN_sub(aux, k->q, BN_value_one());
   k->dmp1 = BN_new();          k->dmq1 = BN_new();
   BN_mod(k->dmp1, k->d, aux, ctx);          BN_mod(k->dmq1, k->d, aux, ctx);
   
   BN_clear_free(aux);          BN_sub(aux, k->p, BN_value_one());
   BN_CTX_free(ctx);          k->dmp1 = BN_new();
           BN_mod(k->dmp1, k->d, aux, ctx);
   identities[num_identities].comment = buffer_get_string(&e->input, NULL);  
   
   /* Check if we already have the key. */          BN_clear_free(aux);
   for (i = 0; i < num_identities; i++)          BN_CTX_free(ctx);
     if (BN_cmp(identities[i].key->n, k->n) == 0)  
       {  
         /* We already have this key.  Clear and free the new data and  
            return success. */  
         RSA_free(k);  
         xfree(identities[num_identities].comment);  
   
         /* Send success. */          identities[num_identities].comment = buffer_get_string(&e->input, NULL);
   
           /* Check if we already have the key. */
           for (i = 0; i < num_identities; i++)
                   if (BN_cmp(identities[i].key->n, k->n) == 0) {
                           /* We already have this key.  Clear and free the
                              new data and return success. */
                           RSA_free(k);
                           xfree(identities[num_identities].comment);
   
                           /* Send success. */
                           buffer_put_int(&e->output, 1);
                           buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
                           return;
                   }
           /* Increment the number of identities. */
           num_identities++;
   
           /* Send a success message. */
         buffer_put_int(&e->output, 1);          buffer_put_int(&e->output, 1);
         buffer_put_char(&e->output, SSH_AGENT_SUCCESS);          buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
         return;  
       }  
   
   /* Increment the number of identities. */  
   num_identities++;  
   
   /* Send a success message. */  
   buffer_put_int(&e->output, 1);  
   buffer_put_char(&e->output, SSH_AGENT_SUCCESS);  
 }  }
   
 void  void
 process_message(SocketEntry *e)  process_message(SocketEntry *e)
 {  {
   unsigned int msg_len;          unsigned int msg_len;
   unsigned int type;          unsigned int type;
   unsigned char *cp;          unsigned char *cp;
   if (buffer_len(&e->input) < 5)          if (buffer_len(&e->input) < 5)
     return; /* Incomplete message. */                  return;         /* Incomplete message. */
   cp = (unsigned char *)buffer_ptr(&e->input);          cp = (unsigned char *) buffer_ptr(&e->input);
   msg_len = GET_32BIT(cp);          msg_len = GET_32BIT(cp);
   if (msg_len > 256 * 1024)          if (msg_len > 256 * 1024) {
     {                  shutdown(e->fd, SHUT_RDWR);
       shutdown(e->fd, SHUT_RDWR);                  close(e->fd);
       close(e->fd);                  e->type = AUTH_UNUSED;
       e->type = AUTH_UNUSED;                  return;
       return;          }
     }          if (buffer_len(&e->input) < msg_len + 4)
   if (buffer_len(&e->input) < msg_len + 4)                  return;
     return;          buffer_consume(&e->input, 4);
   buffer_consume(&e->input, 4);          type = buffer_get_char(&e->input);
   type = buffer_get_char(&e->input);  
   
   switch (type)          switch (type) {
     {          case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
     case SSH_AGENTC_REQUEST_RSA_IDENTITIES:                  process_request_identity(e);
       process_request_identity(e);                  break;
       break;          case SSH_AGENTC_RSA_CHALLENGE:
     case SSH_AGENTC_RSA_CHALLENGE:                  process_authentication_challenge(e);
       process_authentication_challenge(e);                  break;
       break;          case SSH_AGENTC_ADD_RSA_IDENTITY:
     case SSH_AGENTC_ADD_RSA_IDENTITY:                  process_add_identity(e);
       process_add_identity(e);                  break;
       break;          case SSH_AGENTC_REMOVE_RSA_IDENTITY:
     case SSH_AGENTC_REMOVE_RSA_IDENTITY:                  process_remove_identity(e);
       process_remove_identity(e);                  break;
       break;          case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
     case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:                  process_remove_all_identities(e);
       process_remove_all_identities(e);                  break;
       break;          default:
     default:                  /* Unknown message.  Respond with failure. */
       /* Unknown message.  Respond with failure. */                  error("Unknown message %d", type);
       error("Unknown message %d", type);                  buffer_clear(&e->input);
       buffer_clear(&e->input);                  buffer_put_int(&e->output, 1);
       buffer_put_int(&e->output, 1);                  buffer_put_char(&e->output, SSH_AGENT_FAILURE);
       buffer_put_char(&e->output, SSH_AGENT_FAILURE);                  break;
       break;          }
     }  
 }  }
   
 void  void
 new_socket(int type, int fd)  new_socket(int type, int fd)
 {  {
   unsigned int i, old_alloc;          unsigned int i, old_alloc;
   if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)          if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
     error("fcntl O_NONBLOCK: %s", strerror(errno));                  error("fcntl O_NONBLOCK: %s", strerror(errno));
   
   if (fd > max_fd)          if (fd > max_fd)
     max_fd = fd;                  max_fd = fd;
   
   for (i = 0; i < sockets_alloc; i++)          for (i = 0; i < sockets_alloc; i++)
     if (sockets[i].type == AUTH_UNUSED)                  if (sockets[i].type == AUTH_UNUSED) {
       {                          sockets[i].fd = fd;
         sockets[i].fd = fd;                          sockets[i].type = type;
         sockets[i].type = type;                          buffer_init(&sockets[i].input);
         buffer_init(&sockets[i].input);                          buffer_init(&sockets[i].output);
         buffer_init(&sockets[i].output);                          return;
         return;                  }
       }          old_alloc = sockets_alloc;
   old_alloc = sockets_alloc;          sockets_alloc += 10;
   sockets_alloc += 10;          if (sockets)
   if (sockets)                  sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
     sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));          else
   else                  sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
     sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));          for (i = old_alloc; i < sockets_alloc; i++)
   for (i = old_alloc; i < sockets_alloc; i++)                  sockets[i].type = AUTH_UNUSED;
     sockets[i].type = AUTH_UNUSED;          sockets[old_alloc].type = type;
   sockets[old_alloc].type = type;          sockets[old_alloc].fd = fd;
   sockets[old_alloc].fd = fd;          buffer_init(&sockets[old_alloc].input);
   buffer_init(&sockets[old_alloc].input);          buffer_init(&sockets[old_alloc].output);
   buffer_init(&sockets[old_alloc].output);  
 }  }
   
 void  void
 prepare_select(fd_set *readset, fd_set *writeset)  prepare_select(fd_set *readset, fd_set *writeset)
 {  {
   unsigned int i;          unsigned int i;
   for (i = 0; i < sockets_alloc; i++)          for (i = 0; i < sockets_alloc; i++)
     switch (sockets[i].type)                  switch (sockets[i].type) {
       {                  case AUTH_SOCKET:
       case AUTH_SOCKET:                  case AUTH_CONNECTION:
       case AUTH_CONNECTION:                          FD_SET(sockets[i].fd, readset);
         FD_SET(sockets[i].fd, readset);                          if (buffer_len(&sockets[i].output) > 0)
         if (buffer_len(&sockets[i].output) > 0)                                  FD_SET(sockets[i].fd, writeset);
           FD_SET(sockets[i].fd, writeset);                          break;
         break;                  case AUTH_UNUSED:
       case AUTH_UNUSED:                          break;
         break;                  default:
       default:                          fatal("Unknown socket type %d", sockets[i].type);
         fatal("Unknown socket type %d", sockets[i].type);                          break;
         break;                  }
       }  
 }  }
   
 void after_select(fd_set *readset, fd_set *writeset)  void
   after_select(fd_set *readset, fd_set *writeset)
 {  {
   unsigned int i;          unsigned int i;
   int len, sock;          int len, sock;
   char buf[1024];          char buf[1024];
   struct sockaddr_un sunaddr;          struct sockaddr_un sunaddr;
   
   for (i = 0; i < sockets_alloc; i++)          for (i = 0; i < sockets_alloc; i++)
     switch (sockets[i].type)                  switch (sockets[i].type) {
       {                  case AUTH_UNUSED:
       case AUTH_UNUSED:                          break;
         break;                  case AUTH_SOCKET:
       case AUTH_SOCKET:                          if (FD_ISSET(sockets[i].fd, readset)) {
         if (FD_ISSET(sockets[i].fd, readset))                                  len = sizeof(sunaddr);
           {                                  sock = accept(sockets[i].fd, (struct sockaddr *) & sunaddr, &len);
             len = sizeof(sunaddr);                                  if (sock < 0) {
             sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);                                          perror("accept from AUTH_SOCKET");
             if (sock < 0)                                          break;
               {                                  }
                 perror("accept from AUTH_SOCKET");                                  new_socket(AUTH_CONNECTION, sock);
                 break;                          }
               }                          break;
             new_socket(AUTH_CONNECTION, sock);                  case AUTH_CONNECTION:
           }                          if (buffer_len(&sockets[i].output) > 0 &&
         break;                              FD_ISSET(sockets[i].fd, writeset)) {
       case AUTH_CONNECTION:                                  len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
         if (buffer_len(&sockets[i].output) > 0 &&                                           buffer_len(&sockets[i].output));
             FD_ISSET(sockets[i].fd, writeset))                                  if (len <= 0) {
           {                                          shutdown(sockets[i].fd, SHUT_RDWR);
             len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),                                          close(sockets[i].fd);
                         buffer_len(&sockets[i].output));                                          sockets[i].type = AUTH_UNUSED;
             if (len <= 0)                                          break;
               {                                  }
                 shutdown(sockets[i].fd, SHUT_RDWR);                                  buffer_consume(&sockets[i].output, len);
                 close(sockets[i].fd);                          }
                 sockets[i].type = AUTH_UNUSED;                          if (FD_ISSET(sockets[i].fd, readset)) {
                 break;                                  len = read(sockets[i].fd, buf, sizeof(buf));
               }                                  if (len <= 0) {
             buffer_consume(&sockets[i].output, len);                                          shutdown(sockets[i].fd, SHUT_RDWR);
           }                                          close(sockets[i].fd);
         if (FD_ISSET(sockets[i].fd, readset))                                          sockets[i].type = AUTH_UNUSED;
           {                                          break;
             len = read(sockets[i].fd, buf, sizeof(buf));                                  }
             if (len <= 0)                                  buffer_append(&sockets[i].input, buf, len);
               {                                  process_message(&sockets[i]);
                 shutdown(sockets[i].fd, SHUT_RDWR);                          }
                 close(sockets[i].fd);                          break;
                 sockets[i].type = AUTH_UNUSED;                  default:
                 break;                          fatal("Unknown type %d", sockets[i].type);
               }                  }
             buffer_append(&sockets[i].input, buf, len);  
             process_message(&sockets[i]);  
           }  
         break;  
       default:  
         fatal("Unknown type %d", sockets[i].type);  
       }  
 }  }
   
 void  void
 check_parent_exists(int sig)  check_parent_exists(int sig)
 {  {
   if (kill(parent_pid, 0) < 0)          if (kill(parent_pid, 0) < 0) {
     {                  /* printf("Parent has died - Authentication agent exiting.\n"); */
       /* printf("Parent has died - Authentication agent exiting.\n"); */                  exit(1);
       exit(1);          }
     }          signal(SIGALRM, check_parent_exists);
   signal(SIGALRM, check_parent_exists);          alarm(10);
   alarm(10);  
 }  }
   
 void  void
 cleanup_socket(void)  cleanup_socket(void)
 {  {
   remove(socket_name);          remove(socket_name);
   rmdir(socket_dir);          rmdir(socket_dir);
 }  }
   
 void  void
 cleanup_exit(int i)  cleanup_exit(int i)
 {  {
   cleanup_socket();          cleanup_socket();
   exit(i);          exit(i);
 }  }
   
 void  void
 usage()  usage()
 {  {
   fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);          fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
   fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n",          fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n",
           __progname);                  __progname);
   exit(1);          exit(1);
 }  }
   
 int  int
 main(int ac, char **av)  main(int ac, char **av)
 {  {
   fd_set readset, writeset;          fd_set readset, writeset;
   int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;          int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;
   struct sockaddr_un sunaddr;          struct sockaddr_un sunaddr;
   pid_t pid;          pid_t pid;
   char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];          char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
   
   /* check if RSA support exists */          /* check if RSA support exists */
   if (rsa_alive() == 0) {          if (rsa_alive() == 0) {
     fprintf(stderr,                  fprintf(stderr,
       "%s: no RSA support in libssl and libcrypto.  See ssl(8).\n",                          "%s: no RSA support in libssl and libcrypto.  See ssl(8).\n",
       __progname);                          __progname);
     exit(1);                  exit(1);
   }  
   
   while ((ch = getopt(ac, av, "cks")) != -1)  
     {  
       switch (ch)  
         {  
         case 'c':  
           if (s_flag)  
             usage();  
           c_flag++;  
           break;  
         case 'k':  
           k_flag++;  
           break;  
         case 's':  
           if (c_flag)  
             usage();  
           s_flag++;  
           break;  
         default:  
           usage();  
         }          }
     }          while ((ch = getopt(ac, av, "cks")) != -1) {
   ac -= optind;                  switch (ch) {
   av += optind;                  case 'c':
                           if (s_flag)
                                   usage();
                           c_flag++;
                           break;
                   case 'k':
                           k_flag++;
                           break;
                   case 's':
                           if (c_flag)
                                   usage();
                           s_flag++;
                           break;
                   default:
                           usage();
                   }
           }
           ac -= optind;
           av += optind;
   
   if (ac > 0 && (c_flag || k_flag || s_flag))          if (ac > 0 && (c_flag || k_flag || s_flag))
     usage();                  usage();
   
   if (ac == 0 && !c_flag && !k_flag && !s_flag)          if (ac == 0 && !c_flag && !k_flag && !s_flag) {
     {                  shell = getenv("SHELL");
       shell = getenv("SHELL");                  if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
       if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)                          c_flag = 1;
         c_flag = 1;  
     }  
   
   if (k_flag)  
     {  
       pidstr = getenv(SSH_AGENTPID_ENV_NAME);  
       if (pidstr == NULL)  
         {  
           fprintf(stderr, "%s not set, cannot kill agent\n",  
                   SSH_AGENTPID_ENV_NAME);  
           exit(1);  
         }          }
       pid = atoi(pidstr);          if (k_flag) {
       if (pid < 1)              /* XXX PID_MAX check too */                  pidstr = getenv(SSH_AGENTPID_ENV_NAME);
         {                  if (pidstr == NULL) {
           fprintf(stderr, "%s=\"%s\", which is not a good PID\n",                          fprintf(stderr, "%s not set, cannot kill agent\n",
                   SSH_AGENTPID_ENV_NAME, pidstr);                                  SSH_AGENTPID_ENV_NAME);
           exit(1);                          exit(1);
                   }
                   pid = atoi(pidstr);
                   if (pid < 1) {  /* XXX PID_MAX check too */
                           fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
                                   SSH_AGENTPID_ENV_NAME, pidstr);
                           exit(1);
                   }
                   if (kill(pid, SIGTERM) == -1) {
                           perror("kill");
                           exit(1);
                   }
                   format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
                   printf(format, SSH_AUTHSOCKET_ENV_NAME);
                   printf(format, SSH_AGENTPID_ENV_NAME);
                   printf("echo Agent pid %d killed;\n", pid);
                   exit(0);
         }          }
       if (kill(pid, SIGTERM) == -1)          parent_pid = getpid();
         {  
           perror("kill");          /* Create private directory for agent socket */
           exit(1);          strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
           if (mkdtemp(socket_dir) == NULL) {
                   perror("mkdtemp: private socket dir");
                   exit(1);
         }          }
       format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";          snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
       printf(format, SSH_AUTHSOCKET_ENV_NAME);                   parent_pid);
       printf(format, SSH_AGENTPID_ENV_NAME);  
       printf("echo Agent pid %d killed;\n", pid);  
       exit(0);  
     }  
   
   parent_pid = getpid();          /* Create socket early so it will exist before command gets run
              from the parent.  */
   /* Create private directory for agent socket */          sock = socket(AF_UNIX, SOCK_STREAM, 0);
   strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);          if (sock < 0) {
   if (mkdtemp(socket_dir) == NULL) {                  perror("socket");
       perror("mkdtemp: private socket dir");                  cleanup_exit(1);
       exit(1);  
   }  
   snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,  
            parent_pid);  
   
   /* Create socket early so it will exist before command gets run from  
      the parent.  */  
   sock = socket(AF_UNIX, SOCK_STREAM, 0);  
   if (sock < 0)  
     {  
       perror("socket");  
       cleanup_exit(1);  
     }  
   memset(&sunaddr, 0, sizeof(sunaddr));  
   sunaddr.sun_family = AF_UNIX;  
   strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));  
   if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)  
     {  
       perror("bind");  
       cleanup_exit(1);  
     }  
   if (listen(sock, 5) < 0)  
     {  
       perror("listen");  
       cleanup_exit(1);  
     }  
   
   /* Fork, and have the parent execute the command, if any, or present the  
      socket data.  The child continues as the authentication agent. */  
   pid = fork();  
   if (pid == -1)  
     {  
       perror("fork");  
       exit(1);  
     }  
   if (pid != 0)  
     { /* Parent - execute the given command. */  
       close(sock);  
       snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);  
       if (ac == 0)  
         {  
           format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";  
           printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,  
                  SSH_AUTHSOCKET_ENV_NAME);  
           printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,  
                  SSH_AGENTPID_ENV_NAME);  
           printf("echo Agent pid %d;\n", pid);  
           exit(0);  
         }          }
           memset(&sunaddr, 0, sizeof(sunaddr));
           sunaddr.sun_family = AF_UNIX;
           strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
           if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) {
                   perror("bind");
                   cleanup_exit(1);
           }
           if (listen(sock, 5) < 0) {
                   perror("listen");
                   cleanup_exit(1);
           }
           /* Fork, and have the parent execute the command, if any, or
              present the socket data.  The child continues as the
              authentication agent. */
           pid = fork();
           if (pid == -1) {
                   perror("fork");
                   exit(1);
           }
           if (pid != 0) {         /* Parent - execute the given command. */
                   close(sock);
                   snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
                   if (ac == 0) {
                           format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
                           printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
                                  SSH_AUTHSOCKET_ENV_NAME);
                           printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
                                  SSH_AGENTPID_ENV_NAME);
                           printf("echo Agent pid %d;\n", pid);
                           exit(0);
                   }
                   setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
                   setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
                   execvp(av[0], av);
                   perror(av[0]);
                   exit(1);
           }
           close(0);
           close(1);
           close(2);
   
       setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);          if (setsid() == -1) {
       setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);                  perror("setsid");
       execvp(av[0], av);                  cleanup_exit(1);
       perror(av[0]);  
       exit(1);  
     }  
   
   close(0);  
   close(1);  
   close(2);  
   
   if (setsid() == -1)  
     {  
       perror("setsid");  
       cleanup_exit(1);  
     }  
   
   if (atexit(cleanup_socket) < 0)  
     {  
       perror("atexit");  
       cleanup_exit(1);  
     }  
   
   new_socket(AUTH_SOCKET, sock);  
   if (ac > 0)  
     {  
       signal(SIGALRM, check_parent_exists);  
       alarm(10);  
     }  
   
   signal(SIGINT, SIG_IGN);  
   signal(SIGPIPE, SIG_IGN);  
   while (1)  
     {  
       FD_ZERO(&readset);  
       FD_ZERO(&writeset);  
       prepare_select(&readset, &writeset);  
       if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)  
         {  
           if (errno == EINTR)  
             continue;  
           exit(1);  
         }          }
       after_select(&readset, &writeset);          if (atexit(cleanup_socket) < 0) {
     }                  perror("atexit");
   /*NOTREACHED*/                  cleanup_exit(1);
           }
           new_socket(AUTH_SOCKET, sock);
           if (ac > 0) {
                   signal(SIGALRM, check_parent_exists);
                   alarm(10);
           }
           signal(SIGINT, SIG_IGN);
           signal(SIGPIPE, SIG_IGN);
           while (1) {
                   FD_ZERO(&readset);
                   FD_ZERO(&writeset);
                   prepare_select(&readset, &writeset);
                   if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0) {
                           if (errno == EINTR)
                                   continue;
                           exit(1);
                   }
                   after_select(&readset, &writeset);
           }
           /* NOTREACHED */
 }  }

Legend:
Removed from v.1.20  
changed lines
  Added in v.1.21