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

Diff for /src/usr.bin/ssh/sshconnect.c between version 1.37 and 1.38

version 1.37, 1999/11/22 21:52:42 version 1.38, 1999/11/23 22:25:55
Line 41 
Line 41 
 ssh_proxy_connect(const char *host, int port, uid_t original_real_uid,  ssh_proxy_connect(const char *host, int port, uid_t original_real_uid,
                   const char *proxy_command)                    const char *proxy_command)
 {  {
   Buffer command;          Buffer command;
   const char *cp;          const char *cp;
   char *command_string;          char *command_string;
   int pin[2], pout[2];          int pin[2], pout[2];
   int pid;          int pid;
   char portstring[100];          char portstring[100];
   
   /* Convert the port number into a string. */          /* Convert the port number into a string. */
   snprintf(portstring, sizeof portstring, "%d", port);          snprintf(portstring, sizeof portstring, "%d", port);
   
   /* Build the final command string in the buffer by making the appropriate          /* Build the final command string in the buffer by making the
      substitutions to the given proxy command. */             appropriate substitutions to the given proxy command. */
   buffer_init(&command);          buffer_init(&command);
   for (cp = proxy_command; *cp; cp++)          for (cp = proxy_command; *cp; cp++) {
     {                  if (cp[0] == '%' && cp[1] == '%') {
       if (cp[0] == '%' && cp[1] == '%')                          buffer_append(&command, "%", 1);
         {                          cp++;
           buffer_append(&command, "%", 1);                          continue;
           cp++;                  }
           continue;                  if (cp[0] == '%' && cp[1] == 'h') {
                           buffer_append(&command, host, strlen(host));
                           cp++;
                           continue;
                   }
                   if (cp[0] == '%' && cp[1] == 'p') {
                           buffer_append(&command, portstring, strlen(portstring));
                           cp++;
                           continue;
                   }
                   buffer_append(&command, cp, 1);
         }          }
       if (cp[0] == '%' && cp[1] == 'h')          buffer_append(&command, "\0", 1);
         {  
           buffer_append(&command, host, strlen(host));  
           cp++;  
           continue;  
         }  
       if (cp[0] == '%' && cp[1] == 'p')  
         {  
           buffer_append(&command, portstring, strlen(portstring));  
           cp++;  
           continue;  
         }  
       buffer_append(&command, cp, 1);  
     }  
   buffer_append(&command, "\0", 1);  
   
   /* Get the final command string. */          /* Get the final command string. */
   command_string = buffer_ptr(&command);          command_string = buffer_ptr(&command);
   
   /* Create pipes for communicating with the proxy. */          /* Create pipes for communicating with the proxy. */
   if (pipe(pin) < 0 || pipe(pout) < 0)          if (pipe(pin) < 0 || pipe(pout) < 0)
     fatal("Could not create pipes to communicate with the proxy: %.100s",                  fatal("Could not create pipes to communicate with the proxy: %.100s",
           strerror(errno));                        strerror(errno));
   
   debug("Executing proxy command: %.500s", command_string);          debug("Executing proxy command: %.500s", command_string);
   
   /* Fork and execute the proxy command. */          /* Fork and execute the proxy command. */
   if ((pid = fork()) == 0)          if ((pid = fork()) == 0) {
     {                  char *argv[10];
       char *argv[10];  
   
       /* Child.  Permanently give up superuser privileges. */                  /* Child.  Permanently give up superuser privileges. */
       permanently_set_uid(original_real_uid);                  permanently_set_uid(original_real_uid);
   
       /* Redirect stdin and stdout. */                  /* Redirect stdin and stdout. */
       close(pin[1]);                  close(pin[1]);
       if (pin[0] != 0)                  if (pin[0] != 0) {
         {                          if (dup2(pin[0], 0) < 0)
           if (dup2(pin[0], 0) < 0)                                  perror("dup2 stdin");
             perror("dup2 stdin");                          close(pin[0]);
           close(pin[0]);                  }
                   close(pout[0]);
                   if (dup2(pout[1], 1) < 0)
                           perror("dup2 stdout");
                   /* Cannot be 1 because pin allocated two descriptors. */
                   close(pout[1]);
   
                   /* Stderr is left as it is so that error messages get
                      printed on the user's terminal. */
                   argv[0] = "/bin/sh";
                   argv[1] = "-c";
                   argv[2] = command_string;
                   argv[3] = NULL;
   
                   /* Execute the proxy command.  Note that we gave up any
                      extra privileges above. */
                   execv("/bin/sh", argv);
                   perror("/bin/sh");
                   exit(1);
         }          }
       close(pout[0]);          /* Parent. */
       if (dup2(pout[1], 1) < 0)          if (pid < 0)
         perror("dup2 stdout");                  fatal("fork failed: %.100s", strerror(errno));
       close(pout[1]); /* Cannot be 1 because pin allocated two descriptors. */  
   
       /* Stderr is left as it is so that error messages get printed on          /* Close child side of the descriptors. */
          the user's terminal. */          close(pin[0]);
       argv[0] = "/bin/sh";          close(pout[1]);
       argv[1] = "-c";  
       argv[2] = command_string;  
       argv[3] = NULL;  
   
       /* Execute the proxy command.  Note that we gave up any extra  
          privileges above. */  
       execv("/bin/sh", argv);  
       perror("/bin/sh");  
       exit(1);  
     }  
   /* Parent. */  
   if (pid < 0)  
     fatal("fork failed: %.100s", strerror(errno));  
   
   /* Close child side of the descriptors. */  
   close(pin[0]);  
   close(pout[1]);  
   
   /* Free the command name. */          /* Free the command name. */
   buffer_free(&command);          buffer_free(&command);
   
   /* Set the connection file descriptors. */  
   packet_set_connection(pout[0], pin[1]);  
   
   return 1;          /* Set the connection file descriptors. */
           packet_set_connection(pout[0], pin[1]);
   
           return 1;
 }  }
   
 /* Creates a (possibly privileged) socket for use as the ssh connection. */  /* Creates a (possibly privileged) socket for use as the ssh connection. */
   
 int ssh_create_socket(uid_t original_real_uid, int privileged)  int
   ssh_create_socket(uid_t original_real_uid, int privileged)
 {  {
   int sock;          int sock;
   
   /* If we are running as root and want to connect to a privileged port,          /* If we are running as root and want to connect to a privileged
      bind our own socket to a privileged port. */             port, bind our own socket to a privileged port. */
   if (privileged)          if (privileged) {
     {                  int p = IPPORT_RESERVED - 1;
       int p = IPPORT_RESERVED - 1;  
   
       sock = rresvport(&p);                  sock = rresvport(&p);
       if (sock < 0)                  if (sock < 0)
         fatal("rresvport: %.100s", strerror(errno));                          fatal("rresvport: %.100s", strerror(errno));
       debug("Allocated local port %d.", p);                  debug("Allocated local port %d.", p);
     }          } else {
   else                  /* Just create an ordinary socket on arbitrary port.  We
     {                     use the user's uid to create the socket. */
       /* Just create an ordinary socket on arbitrary port.  We use the                  temporarily_use_uid(original_real_uid);
          user's uid to create the socket. */                  sock = socket(AF_INET, SOCK_STREAM, 0);
       temporarily_use_uid(original_real_uid);                  if (sock < 0)
       sock = socket(AF_INET, SOCK_STREAM, 0);                          fatal("socket: %.100s", strerror(errno));
       if (sock < 0)                  restore_uid();
         fatal("socket: %.100s", strerror(errno));          }
       restore_uid();          return sock;
     }  
   return sock;  
 }  }
   
 /* Opens a TCP/IP connection to the remote server on the given host.  If  /* Opens a TCP/IP connection to the remote server on the given host.  If
    port is 0, the default port will be used.  If anonymous is zero,     port is 0, the default port will be used.  If anonymous is zero,
    a privileged port will be allocated to make the connection.     a privileged port will be allocated to make the connection.
    This requires super-user privileges if anonymous is false.     This requires super-user privileges if anonymous is false.
    Connection_attempts specifies the maximum number of tries (one per     Connection_attempts specifies the maximum number of tries (one per
    second).  If proxy_command is non-NULL, it specifies the command (with %h     second).  If proxy_command is non-NULL, it specifies the command (with %h
    and %p substituted for host and port, respectively) to use to contact     and %p substituted for host and port, respectively) to use to contact
    the daemon. */     the daemon. */
   
 int ssh_connect(const char *host, struct sockaddr_in *hostaddr,  int
                 int port, int connection_attempts,  ssh_connect(const char *host, struct sockaddr_in * hostaddr,
                 int anonymous, uid_t original_real_uid,              int port, int connection_attempts,
                 const char *proxy_command)              int anonymous, uid_t original_real_uid,
               const char *proxy_command)
 {  {
   int sock = -1, attempt, i;          int sock = -1, attempt, i;
   int on = 1;          int on = 1;
   struct servent *sp;          struct servent *sp;
   struct hostent *hp;          struct hostent *hp;
   struct linger linger;          struct linger linger;
   
   debug("ssh_connect: getuid %d geteuid %d anon %d",          debug("ssh_connect: getuid %d geteuid %d anon %d",
         (int)getuid(), (int)geteuid(), anonymous);                (int) getuid(), (int) geteuid(), anonymous);
   
   /* Get default port if port has not been set. */          /* Get default port if port has not been set. */
   if (port == 0)          if (port == 0) {
     {                  sp = getservbyname(SSH_SERVICE_NAME, "tcp");
       sp = getservbyname(SSH_SERVICE_NAME, "tcp");                  if (sp)
       if (sp)                          port = ntohs(sp->s_port);
         port = ntohs(sp->s_port);                  else
       else                          port = SSH_DEFAULT_PORT;
         port = SSH_DEFAULT_PORT;          }
     }          /* If a proxy command is given, connect using it. */
           if (proxy_command != NULL)
                   return ssh_proxy_connect(host, port, original_real_uid, proxy_command);
   
   /* If a proxy command is given, connect using it. */          /* No proxy command. */
   if (proxy_command != NULL)  
     return ssh_proxy_connect(host, port, original_real_uid, proxy_command);  
   
   /* No proxy command. */          /* No host lookup made yet. */
           hp = NULL;
   
   /* No host lookup made yet. */          /* Try to connect several times.  On some machines, the first time
   hp = NULL;             will sometimes fail.  In general socket code appears to behave
              quite magically on many machines. */
   /* Try to connect several times.  On some machines, the first time will          for (attempt = 0; attempt < connection_attempts; attempt++) {
      sometimes fail.  In general socket code appears to behave quite                  if (attempt > 0)
      magically on many machines. */                          debug("Trying again...");
   for (attempt = 0; attempt < connection_attempts; attempt++)  
     {  
       if (attempt > 0)  
         debug("Trying again...");  
   
       /* Try to parse the host name as a numeric inet address. */                  /* Try to parse the host name as a numeric inet address. */
       memset(hostaddr, 0, sizeof(hostaddr));                  memset(hostaddr, 0, sizeof(hostaddr));
       hostaddr->sin_family = AF_INET;                  hostaddr->sin_family = AF_INET;
       hostaddr->sin_port = htons(port);                  hostaddr->sin_port = htons(port);
       hostaddr->sin_addr.s_addr = inet_addr(host);                  hostaddr->sin_addr.s_addr = inet_addr(host);
       if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff)                  if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) {
         {                          /* Valid numeric IP address */
           /* Valid numeric IP address */                          debug("Connecting to %.100s port %d.",
           debug("Connecting to %.100s port %d.",                                inet_ntoa(hostaddr->sin_addr), port);
                 inet_ntoa(hostaddr->sin_addr), port);  
   
           /* Create a socket. */  
           sock = ssh_create_socket(original_real_uid,  
                                    !anonymous && geteuid() == 0 &&  
                                      port < IPPORT_RESERVED);  
   
           /* Connect to the host.  We use the user's uid in the hope that  
              it will help with the problems of tcp_wrappers showing the  
              remote uid as root. */  
           temporarily_use_uid(original_real_uid);  
           if (connect(sock, (struct sockaddr *)hostaddr, sizeof(*hostaddr))  
               >= 0)  
             {  
               /* Successful connect. */  
               restore_uid();  
               break;  
             }  
           debug("connect: %.100s", strerror(errno));  
           restore_uid();  
   
           /* Destroy the failed socket. */                          /* Create a socket. */
           shutdown(sock, SHUT_RDWR);                          sock = ssh_create_socket(original_real_uid,
           close(sock);                                            !anonymous && geteuid() == 0 &&
         }                                                   port < IPPORT_RESERVED);
       else  
         {  
           /* Not a valid numeric inet address. */  
           /* Map host name to an address. */  
           if (!hp)  
             hp = gethostbyname(host);  
           if (!hp)  
             fatal("Bad host name: %.100s", host);  
           if (!hp->h_addr_list[0])  
             fatal("Host does not have an IP address: %.100s", host);  
   
           /* Loop through addresses for this host, and try each one in                          /* Connect to the host.  We use the user's uid in
              sequence until the connection succeeds. */                             the hope that it will help with the problems of
           for (i = 0; hp->h_addr_list[i]; i++)                             tcp_wrappers showing the remote uid as root. */
             {                          temporarily_use_uid(original_real_uid);
               /* Set the address to connect to. */                          if (connect(sock, (struct sockaddr *) hostaddr, sizeof(*hostaddr))
               hostaddr->sin_family = hp->h_addrtype;                              >= 0) {
               memcpy(&hostaddr->sin_addr, hp->h_addr_list[i],                                  /* Successful connect. */
                      sizeof(hostaddr->sin_addr));                                  restore_uid();
                                   break;
                           }
                           debug("connect: %.100s", strerror(errno));
                           restore_uid();
   
               debug("Connecting to %.200s [%.100s] port %d.",                          /* Destroy the failed socket. */
                     host, inet_ntoa(hostaddr->sin_addr), port);                          shutdown(sock, SHUT_RDWR);
                           close(sock);
                   } else {
                           /* Not a valid numeric inet address. */
                           /* Map host name to an address. */
                           if (!hp)
                                   hp = gethostbyname(host);
                           if (!hp)
                                   fatal("Bad host name: %.100s", host);
                           if (!hp->h_addr_list[0])
                                   fatal("Host does not have an IP address: %.100s", host);
   
               /* Create a socket for connecting. */                          /* Loop through addresses for this host, and try
               sock = ssh_create_socket(original_real_uid,                             each one in sequence until the connection
                                        !anonymous && geteuid() == 0 &&                             succeeds. */
                                          port < IPPORT_RESERVED);                          for (i = 0; hp->h_addr_list[i]; i++) {
                                   /* Set the address to connect to. */
                                   hostaddr->sin_family = hp->h_addrtype;
                                   memcpy(&hostaddr->sin_addr, hp->h_addr_list[i],
                                          sizeof(hostaddr->sin_addr));
   
               /* Connect to the host.  We use the user's uid in the hope that                                  debug("Connecting to %.200s [%.100s] port %d.",
                  it will help with tcp_wrappers showing the remote uid as                                        host, inet_ntoa(hostaddr->sin_addr), port);
                  root. */  
               temporarily_use_uid(original_real_uid);                                  /* Create a socket for connecting. */
               if (connect(sock, (struct sockaddr *)hostaddr,                                  sock = ssh_create_socket(original_real_uid,
                           sizeof(*hostaddr)) >= 0)                                            !anonymous && geteuid() == 0 &&
                 {                                                   port < IPPORT_RESERVED);
                   /* Successful connection. */  
                   restore_uid();                                  /* Connect to the host.  We use the user's uid in the hope that
                   break;                                     it will help with tcp_wrappers showing the remote uid as root. */
                                   temporarily_use_uid(original_real_uid);
                                   if (connect(sock, (struct sockaddr *) hostaddr,
                                               sizeof(*hostaddr)) >= 0) {
                                           /* Successful connection. */
                                           restore_uid();
                                           break;
                                   }
                                   debug("connect: %.100s", strerror(errno));
                                   restore_uid();
   
                                   /* Close the failed socket; there appear to be some problems when
                                      reusing a socket for which connect() has already returned an error. */
                                   shutdown(sock, SHUT_RDWR);
                                   close(sock);
                           }
                           if (hp->h_addr_list[i])
                                   break;  /* Successful connection. */
                 }                  }
               debug("connect: %.100s", strerror(errno));  
               restore_uid();  
   
               /* Close the failed socket; there appear to be some problems                  /* Sleep a moment before retrying. */
                  when reusing a socket for which connect() has already                  sleep(1);
                  returned an error. */  
               shutdown(sock, SHUT_RDWR);  
               close(sock);  
             }  
           if (hp->h_addr_list[i])  
             break; /* Successful connection. */  
         }          }
           /* Return failure if we didn't get a successful connection. */
           if (attempt >= connection_attempts)
                   return 0;
   
       /* Sleep a moment before retrying. */          debug("Connection established.");
       sleep(1);  
     }  
   /* Return failure if we didn't get a successful connection. */  
   if (attempt >= connection_attempts)  
     return 0;  
   
   debug("Connection established.");          /* Set socket options.  We would like the socket to disappear as
              soon as it has been closed for whatever reason. */
           /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
              sizeof(on)); */
           setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *) &on, sizeof(on));
           linger.l_onoff = 1;
           linger.l_linger = 5;
           setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger));
   
   /* Set socket options.  We would like the socket to disappear as soon as          /* Set the connection. */
      it has been closed for whatever reason. */          packet_set_connection(sock, sock);
   /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */  
   setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on));  
   linger.l_onoff = 1;  
   linger.l_linger = 5;  
   setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));  
   
   /* Set the connection. */          return 1;
   packet_set_connection(sock, sock);  
   
   return 1;  
 }  }
   
 /* Checks if the user has an authentication agent, and if so, tries to  /* Checks if the user has an authentication agent, and if so, tries to
Line 334 
Line 319 
 int  int
 try_agent_authentication()  try_agent_authentication()
 {  {
   int status, type;          int status, type;
   char *comment;          char *comment;
   AuthenticationConnection *auth;          AuthenticationConnection *auth;
   unsigned char response[16];          unsigned char response[16];
   unsigned int i;          unsigned int i;
   BIGNUM *e, *n, *challenge;          BIGNUM *e, *n, *challenge;
   
   /* Get connection to the agent. */  
   auth = ssh_get_authentication_connection();  
   if (!auth)  
     return 0;  
   
   e = BN_new();  
   n = BN_new();  
   challenge = BN_new();  
   
   /* Loop through identities served by the agent. */  
   for (status = ssh_get_first_identity(auth, e, n, &comment);  
        status;  
        status = ssh_get_next_identity(auth, e, n, &comment))  
     {  
       int plen, clen;  
   
       /* Try this identity. */          /* Get connection to the agent. */
       debug("Trying RSA authentication via agent with '%.100s'", comment);          auth = ssh_get_authentication_connection();
       xfree(comment);          if (!auth)
                   return 0;
       /* Tell the server that we are willing to authenticate using this key. */  
       packet_start(SSH_CMSG_AUTH_RSA);  
       packet_put_bignum(n);  
       packet_send();  
       packet_write_wait();  
   
       /* Wait for server's response. */  
       type = packet_read(&plen);  
   
       /* The server sends failure if it doesn\'t like our key or does not  
          support RSA authentication. */  
       if (type == SSH_SMSG_FAILURE)  
         {  
           debug("Server refused our key.");  
           continue;  
         }  
   
       /* Otherwise it should have sent a challenge. */  
       if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)  
         packet_disconnect("Protocol error during RSA authentication: %d",  
                           type);  
   
       packet_get_bignum(challenge, &clen);  
   
       packet_integrity_check(plen, clen, type);  
   
       debug("Received RSA challenge from server.");          e = BN_new();
           n = BN_new();
       /* Ask the agent to decrypt the challenge. */          challenge = BN_new();
       if (!ssh_decrypt_challenge(auth, e, n, challenge,  
                                  session_id, 1, response))  
         {  
           /* The agent failed to authenticate this identifier although it  
              advertised it supports this.  Just return a wrong value. */  
           log("Authentication agent failed to decrypt challenge.");  
           memset(response, 0, sizeof(response));  
         }  
   
       debug("Sending response to RSA challenge.");  
   
       /* Send the decrypted challenge back to the server. */  
       packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);  
       for (i = 0; i < 16; i++)  
         packet_put_char(response[i]);  
       packet_send();  
       packet_write_wait();  
   
       /* Wait for response from the server. */  
       type = packet_read(&plen);  
   
       /* The server returns success if it accepted the authentication. */          /* Loop through identities served by the agent. */
       if (type == SSH_SMSG_SUCCESS)          for (status = ssh_get_first_identity(auth, e, n, &comment);
         {               status;
           debug("RSA authentication accepted by server.");               status = ssh_get_next_identity(auth, e, n, &comment)) {
           BN_clear_free(e);                  int plen, clen;
           BN_clear_free(n);  
           BN_clear_free(challenge);  
           return 1;  
         }  
   
       /* Otherwise it should return failure. */                  /* Try this identity. */
       if (type != SSH_SMSG_FAILURE)                  debug("Trying RSA authentication via agent with '%.100s'", comment);
         packet_disconnect("Protocol error waiting RSA auth response: %d",                  xfree(comment);
                           type);  
     }  
   
   BN_clear_free(e);                  /* Tell the server that we are willing to authenticate using this key. */
   BN_clear_free(n);                  packet_start(SSH_CMSG_AUTH_RSA);
   BN_clear_free(challenge);                  packet_put_bignum(n);
                   packet_send();
                   packet_write_wait();
   
   debug("RSA authentication using agent refused.");                  /* Wait for server's response. */
   return 0;                  type = packet_read(&plen);
   
                   /* The server sends failure if it doesn\'t like our key or
                      does not support RSA authentication. */
                   if (type == SSH_SMSG_FAILURE) {
                           debug("Server refused our key.");
                           continue;
                   }
                   /* Otherwise it should have sent a challenge. */
                   if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
                           packet_disconnect("Protocol error during RSA authentication: %d",
                                             type);
   
                   packet_get_bignum(challenge, &clen);
   
                   packet_integrity_check(plen, clen, type);
   
                   debug("Received RSA challenge from server.");
   
                   /* Ask the agent to decrypt the challenge. */
                   if (!ssh_decrypt_challenge(auth, e, n, challenge,
                                              session_id, 1, response)) {
                           /* The agent failed to authenticate this identifier although it
                              advertised it supports this.  Just return a wrong value. */
                           log("Authentication agent failed to decrypt challenge.");
                           memset(response, 0, sizeof(response));
                   }
                   debug("Sending response to RSA challenge.");
   
                   /* Send the decrypted challenge back to the server. */
                   packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
                   for (i = 0; i < 16; i++)
                           packet_put_char(response[i]);
                   packet_send();
                   packet_write_wait();
   
                   /* Wait for response from the server. */
                   type = packet_read(&plen);
   
                   /* The server returns success if it accepted the authentication. */
                   if (type == SSH_SMSG_SUCCESS) {
                           debug("RSA authentication accepted by server.");
                           BN_clear_free(e);
                           BN_clear_free(n);
                           BN_clear_free(challenge);
                           return 1;
                   }
                   /* Otherwise it should return failure. */
                   if (type != SSH_SMSG_FAILURE)
                           packet_disconnect("Protocol error waiting RSA auth response: %d",
                                             type);
           }
   
           BN_clear_free(e);
           BN_clear_free(n);
           BN_clear_free(challenge);
   
           debug("RSA authentication using agent refused.");
           return 0;
 }  }
   
 /* Computes the proper response to a RSA challenge, and sends the response to  /* Computes the proper response to a RSA challenge, and sends the response to
    the server. */     the server. */
   
 void  void
 respond_to_rsa_challenge(BIGNUM *challenge, RSA *prv)  respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv)
 {  {
   unsigned char buf[32], response[16];          unsigned char buf[32], response[16];
   MD5_CTX md;          MD5_CTX md;
   int i, len;          int i, len;
   
   /* Decrypt the challenge using the private key. */          /* Decrypt the challenge using the private key. */
   rsa_private_decrypt(challenge, challenge, prv);          rsa_private_decrypt(challenge, challenge, prv);
   
   /* Compute the response. */          /* Compute the response. */
   /* 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 > sizeof(buf))          if (len <= 0 || len > sizeof(buf))
     packet_disconnect("respond_to_rsa_challenge: bad challenge length %d",                  packet_disconnect("respond_to_rsa_challenge: bad challenge length %d",
                       len);                                    len);
   
   memset(buf, 0, sizeof(buf));          memset(buf, 0, sizeof(buf));
   BN_bn2bin(challenge, buf + sizeof(buf) - len);          BN_bn2bin(challenge, buf + sizeof(buf) - len);
   MD5_Init(&md);          MD5_Init(&md);
   MD5_Update(&md, buf, 32);          MD5_Update(&md, buf, 32);
   MD5_Update(&md, session_id, 16);          MD5_Update(&md, session_id, 16);
   MD5_Final(response, &md);          MD5_Final(response, &md);
   
   debug("Sending response to host key RSA challenge.");  
   
   /* Send the response back to the server. */          debug("Sending response to host key RSA challenge.");
   packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);  
   for (i = 0; i < 16; i++)          /* Send the response back to the server. */
     packet_put_char(response[i]);          packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
   packet_send();          for (i = 0; i < 16; i++)
   packet_write_wait();                  packet_put_char(response[i]);
           packet_send();
   memset(buf, 0, sizeof(buf));          packet_write_wait();
   memset(response, 0, sizeof(response));  
   memset(&md, 0, sizeof(md));          memset(buf, 0, sizeof(buf));
           memset(response, 0, sizeof(response));
           memset(&md, 0, sizeof(md));
 }  }
   
 /* Checks if the user has authentication file, and if so, tries to authenticate  /* Checks if the user has authentication file, and if so, tries to authenticate
    the user using it. */     the user using it. */
   
 int  int
 try_rsa_authentication(struct passwd *pw, const char *authfile)  try_rsa_authentication(struct passwd * pw, const char *authfile)
 {  {
   extern Options options;          extern Options options;
   BIGNUM *challenge;          BIGNUM *challenge;
   RSA *private_key;          RSA *private_key;
   RSA *public_key;          RSA *public_key;
   char *passphrase, *comment;          char *passphrase, *comment;
   int type, i;          int type, i;
   int plen, clen;          int plen, clen;
   
   /* Try to load identification for the authentication key. */          /* Try to load identification for the authentication key. */
   public_key = RSA_new();          public_key = RSA_new();
   if (!load_public_key(authfile, public_key, &comment)) {          if (!load_public_key(authfile, public_key, &comment)) {
     RSA_free(public_key);                  RSA_free(public_key);
     return 0; /* Could not load it.  Fail. */                  return 0;       /* Could not load it.  Fail. */
   }          }
           debug("Trying RSA authentication with key '%.100s'", comment);
   
   debug("Trying RSA authentication with key '%.100s'", comment);          /* Tell the server that we are willing to authenticate using this key. */
           packet_start(SSH_CMSG_AUTH_RSA);
           packet_put_bignum(public_key->n);
           packet_send();
           packet_write_wait();
   
   /* Tell the server that we are willing to authenticate using this key. */          /* We no longer need the public key. */
   packet_start(SSH_CMSG_AUTH_RSA);          RSA_free(public_key);
   packet_put_bignum(public_key->n);  
   packet_send();  
   packet_write_wait();  
   
   /* We no longer need the public key. */          /* Wait for server's response. */
   RSA_free(public_key);          type = packet_read(&plen);
   
   /* Wait for server's response. */  
   type = packet_read(&plen);  
   
   /* The server responds with failure if it doesn\'t like our key or doesn\'t          /* The server responds with failure if it doesn\'t like our key or
      support RSA authentication. */             doesn\'t support RSA authentication. */
   if (type == SSH_SMSG_FAILURE)          if (type == SSH_SMSG_FAILURE) {
     {                  debug("Server refused our key.");
       debug("Server refused our key.");                  xfree(comment);
       xfree(comment);                  return 0;       /* Server refuses to authenticate with
       return 0; /* Server refuses to authenticate with this key. */                                     this key. */
     }          }
           /* Otherwise, the server should respond with a challenge. */
           if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
                   packet_disconnect("Protocol error during RSA authentication: %d", type);
   
   /* Otherwise, the server should respond with a challenge. */          /* Get the challenge from the packet. */
   if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)          challenge = BN_new();
     packet_disconnect("Protocol error during RSA authentication: %d", type);          packet_get_bignum(challenge, &clen);
   
   /* Get the challenge from the packet. */          packet_integrity_check(plen, clen, type);
   challenge = BN_new();  
   packet_get_bignum(challenge, &clen);  
   
   packet_integrity_check(plen, clen, type);          debug("Received RSA challenge from server.");
   
   debug("Received RSA challenge from server.");          private_key = RSA_new();
           /* Load the private key.  Try first with empty passphrase; if it
              fails, ask for a passphrase. */
           if (!load_private_key(authfile, "", private_key, NULL)) {
                   char buf[300];
                   snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ",
                            comment);
                   if (!options.batch_mode)
                           passphrase = read_passphrase(buf, 0);
                   else {
                           debug("Will not query passphrase for %.100s in batch mode.",
                                 comment);
                           passphrase = xstrdup("");
                   }
   
   private_key = RSA_new();                  /* Load the authentication file using the pasphrase. */
   /* Load the private key.  Try first with empty passphrase; if it fails,                  if (!load_private_key(authfile, passphrase, private_key, NULL)) {
      ask for a passphrase. */                          memset(passphrase, 0, strlen(passphrase));
   if (!load_private_key(authfile, "", private_key, NULL))                          xfree(passphrase);
     {                          error("Bad passphrase.");
       char buf[300];  
       /* Request passphrase from the user.  We read from /dev/tty to make  
          this work even if stdin has been redirected.  If running in  
          batch mode, we just use the empty passphrase, which will fail and  
          return. */  
       snprintf(buf, sizeof buf,  
         "Enter passphrase for RSA key '%.100s': ", comment);  
       if (!options.batch_mode)  
         passphrase = read_passphrase(buf, 0);  
       else  
         {  
           debug("Will not query passphrase for %.100s in batch mode.",  
                 comment);  
           passphrase = xstrdup("");  
         }  
   
       /* Load the authentication file using the pasphrase. */  
       if (!load_private_key(authfile, passphrase, private_key, NULL))  
         {  
           memset(passphrase, 0, strlen(passphrase));  
           xfree(passphrase);  
           error("Bad passphrase.");  
   
           /* Send a dummy response packet to avoid protocol error. */                          /* Send a dummy response packet to avoid protocol error. */
           packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);                          packet_start(SSH_CMSG_AUTH_RSA_RESPONSE);
           for (i = 0; i < 16; i++)                          for (i = 0; i < 16; i++)
             packet_put_char(0);                                  packet_put_char(0);
           packet_send();                          packet_send();
           packet_write_wait();                          packet_write_wait();
   
           /* Expect the server to reject it... */                          /* Expect the server to reject it... */
           packet_read_expect(&plen, SSH_SMSG_FAILURE);                          packet_read_expect(&plen, SSH_SMSG_FAILURE);
           xfree(comment);                          xfree(comment);
           return 0;                          return 0;
                   }
                   /* Destroy the passphrase. */
                   memset(passphrase, 0, strlen(passphrase));
                   xfree(passphrase);
         }          }
           /* We no longer need the comment. */
           xfree(comment);
   
       /* Destroy the passphrase. */          /* Compute and send a response to the challenge. */
       memset(passphrase, 0, strlen(passphrase));          respond_to_rsa_challenge(challenge, private_key);
       xfree(passphrase);  
     }  
   
   /* We no longer need the comment. */  
   xfree(comment);  
   
   /* Compute and send a response to the challenge. */          /* Destroy the private key. */
   respond_to_rsa_challenge(challenge, private_key);          RSA_free(private_key);
   
   /* Destroy the private key. */  
   RSA_free(private_key);  
   
   /* We no longer need the challenge. */          /* We no longer need the challenge. */
   BN_clear_free(challenge);          BN_clear_free(challenge);
   
   /* Wait for response from the server. */          /* Wait for response from the server. */
   type = packet_read(&plen);          type = packet_read(&plen);
   if (type == SSH_SMSG_SUCCESS)          if (type == SSH_SMSG_SUCCESS) {
     {                  debug("RSA authentication accepted by server.");
       debug("RSA authentication accepted by server.");                  return 1;
       return 1;          }
     }          if (type != SSH_SMSG_FAILURE)
   if (type != SSH_SMSG_FAILURE)                  packet_disconnect("Protocol error waiting RSA auth response: %d", type);
     packet_disconnect("Protocol error waiting RSA auth response: %d", type);          debug("RSA authentication refused.");
   debug("RSA authentication refused.");          return 0;
   return 0;  
 }  }
   
 /* Tries to authenticate the user using combined rhosts or /etc/hosts.equiv  /* Tries to authenticate the user using combined rhosts or /etc/hosts.equiv
    authentication and RSA host authentication. */     authentication and RSA host authentication. */
   
 int  int
 try_rhosts_rsa_authentication(const char *local_user, RSA *host_key)  try_rhosts_rsa_authentication(const char *local_user, RSA * host_key)
 {  {
   int type;          int type;
   BIGNUM *challenge;          BIGNUM *challenge;
   int plen, clen;          int plen, clen;
   
   debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication.");          debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication.");
   
   /* Tell the server that we are willing to authenticate using this key. */          /* Tell the server that we are willing to authenticate using this key. */
   packet_start(SSH_CMSG_AUTH_RHOSTS_RSA);          packet_start(SSH_CMSG_AUTH_RHOSTS_RSA);
   packet_put_string(local_user, strlen(local_user));          packet_put_string(local_user, strlen(local_user));
   packet_put_int(BN_num_bits(host_key->n));          packet_put_int(BN_num_bits(host_key->n));
   packet_put_bignum(host_key->e);          packet_put_bignum(host_key->e);
   packet_put_bignum(host_key->n);          packet_put_bignum(host_key->n);
   packet_send();          packet_send();
   packet_write_wait();          packet_write_wait();
   
   /* Wait for server's response. */          /* Wait for server's response. */
   type = packet_read(&plen);          type = packet_read(&plen);
   
   /* The server responds with failure if it doesn't admit our .rhosts          /* The server responds with failure if it doesn't admit our
      authentication or doesn't know our host key. */             .rhosts authentication or doesn't know our host key. */
   if (type == SSH_SMSG_FAILURE)          if (type == SSH_SMSG_FAILURE) {
     {                  debug("Server refused our rhosts authentication or host key.");
       debug("Server refused our rhosts authentication or host key.");                  return 0;
       return 0; /* Server refuses to authenticate us with this method. */          }
     }          /* Otherwise, the server should respond with a challenge. */
           if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
                   packet_disconnect("Protocol error during RSA authentication: %d", type);
   
   /* Otherwise, the server should respond with a challenge. */          /* Get the challenge from the packet. */
   if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)          challenge = BN_new();
     packet_disconnect("Protocol error during RSA authentication: %d", type);          packet_get_bignum(challenge, &clen);
   
   /* Get the challenge from the packet. */          packet_integrity_check(plen, clen, type);
   challenge = BN_new();  
   packet_get_bignum(challenge, &clen);  
   
   packet_integrity_check(plen, clen, type);          debug("Received RSA challenge for host key from server.");
   
   debug("Received RSA challenge for host key from server.");          /* Compute a response to the challenge. */
           respond_to_rsa_challenge(challenge, host_key);
   
   /* Compute a response to the challenge. */          /* We no longer need the challenge. */
   respond_to_rsa_challenge(challenge, host_key);          BN_clear_free(challenge);
   
   /* We no longer need the challenge. */          /* Wait for response from the server. */
   BN_clear_free(challenge);          type = packet_read(&plen);
           if (type == SSH_SMSG_SUCCESS) {
   /* Wait for response from the server. */                  debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server.");
   type = packet_read(&plen);                  return 1;
   if (type == SSH_SMSG_SUCCESS)          }
     {          if (type != SSH_SMSG_FAILURE)
       debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server.");                  packet_disconnect("Protocol error waiting RSA auth response: %d", type);
       return 1;          debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused.");
     }          return 0;
   if (type != SSH_SMSG_FAILURE)  
     packet_disconnect("Protocol error waiting RSA auth response: %d", type);  
   debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused.");  
   return 0;  
 }  }
   
 #ifdef KRB4  #ifdef KRB4
 int try_kerberos_authentication()  int
   try_kerberos_authentication()
 {  {
   KTEXT_ST auth;                     /* Kerberos data */          KTEXT_ST auth;          /* Kerberos data */
   char *reply;          char *reply;
   char inst[INST_SZ];          char inst[INST_SZ];
   char *realm;          char *realm;
   CREDENTIALS cred;          CREDENTIALS cred;
   int r, type, plen;          int r, type, plen;
   Key_schedule schedule;          Key_schedule schedule;
   u_long checksum, cksum;          u_long checksum, cksum;
   MSG_DAT msg_data;          MSG_DAT msg_data;
   struct sockaddr_in local, foreign;          struct sockaddr_in local, foreign;
   struct stat st;          struct stat st;
   
   /* Don't do anything if we don't have any tickets. */          /* Don't do anything if we don't have any tickets. */
   if (stat(tkt_string(), &st) < 0) return 0;          if (stat(tkt_string(), &st) < 0)
                   return 0;
   strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ);  
   
   realm = (char *)krb_realmofhost(get_canonical_hostname());  
   if (!realm) {  
     debug("Kerberos V4: no realm for %s", get_canonical_hostname());  
     return 0;  
   }  
   /* This can really be anything. */  
   checksum = (u_long) getpid();  
   
   r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum);  
   if (r != KSUCCESS) {  
     debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]);  
     return 0;  
   }  
   /* Get session key to decrypt the server's reply with. */  
   r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred);  
   if (r != KSUCCESS) {  
      debug("get_cred failed: %s", krb_err_txt[r]);  
      return 0;  
   }  
   des_key_sched((des_cblock *)cred.session, schedule);  
   
   /* Send authentication info to server. */  
   packet_start(SSH_CMSG_AUTH_KERBEROS);  
   packet_put_string((char *)auth.dat, auth.length);  
   packet_send();  
   packet_write_wait();  
   
   /* Zero the buffer. */  
   (void) memset(auth.dat, 0, MAX_KTXT_LEN);  
   
   r = sizeof(local);  
   memset(&local, 0, sizeof(local));  
   if (getsockname(packet_get_connection_in(),  
                   (struct sockaddr *) &local, &r) < 0)  
     debug("getsockname failed: %s", strerror(errno));  
   
   r = sizeof(foreign);  
   memset(&foreign, 0, sizeof(foreign));  
    if (getpeername(packet_get_connection_in(),  
                    (struct sockaddr *)&foreign, &r) < 0) {  
      debug("getpeername failed: %s", strerror(errno));  
      fatal_cleanup();  
    }  
   
    /* Get server reply. */  
    type = packet_read(&plen);  
    switch(type) {  
   
    case SSH_SMSG_FAILURE: /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */  
      debug("Kerberos V4 authentication failed.");  
      return 0;  
      break;  
   
    case SSH_SMSG_AUTH_KERBEROS_RESPONSE: /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */  
      debug("Kerberos V4 authentication accepted.");  
   
      /* Get server's response. */  
      reply = packet_get_string((unsigned int *)&auth.length);  
      memcpy(auth.dat, reply, auth.length);  
      xfree(reply);  
   
      packet_integrity_check(plen, 4 + auth.length, type);  
   
      /* If his response isn't properly encrypted with the session key,          strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ);
         and the decrypted checksum fails to match, he's bogus. Bail out. */  
      r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session,          realm = (char *) krb_realmofhost(get_canonical_hostname());
                      &foreign, &local, &msg_data);          if (!realm) {
      if (r != KSUCCESS) {                  debug("Kerberos V4: no realm for %s", get_canonical_hostname());
        debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]);                  return 0;
        packet_disconnect("Kerberos V4 challenge failed!");          }
      }          /* This can really be anything. */
      /* Fetch the (incremented) checksum that we supplied in the request. */          checksum = (u_long) getpid();
      (void)memcpy((char *)&cksum, (char *)msg_data.app_data, sizeof(cksum));  
      cksum = ntohl(cksum);          r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum);
           if (r != KSUCCESS) {
      /* If it matches, we're golden. */                  debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]);
      if (cksum == checksum + 1) {                  return 0;
        debug("Kerberos V4 challenge successful.");          }
        return 1;          /* Get session key to decrypt the server's reply with. */
      }          r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred);
      else          if (r != KSUCCESS) {
        packet_disconnect("Kerberos V4 challenge failed!");                  debug("get_cred failed: %s", krb_err_txt[r]);
      break;                  return 0;
           }
    default:          des_key_sched((des_cblock *) cred.session, schedule);
      packet_disconnect("Protocol error on Kerberos V4 response: %d", type);  
    }          /* Send authentication info to server. */
    return 0;          packet_start(SSH_CMSG_AUTH_KERBEROS);
           packet_put_string((char *) auth.dat, auth.length);
           packet_send();
           packet_write_wait();
   
           /* Zero the buffer. */
           (void) memset(auth.dat, 0, MAX_KTXT_LEN);
   
           r = sizeof(local);
           memset(&local, 0, sizeof(local));
           if (getsockname(packet_get_connection_in(),
                           (struct sockaddr *) & local, &r) < 0)
                   debug("getsockname failed: %s", strerror(errno));
   
           r = sizeof(foreign);
           memset(&foreign, 0, sizeof(foreign));
           if (getpeername(packet_get_connection_in(),
                           (struct sockaddr *) & foreign, &r) < 0) {
                   debug("getpeername failed: %s", strerror(errno));
                   fatal_cleanup();
           }
           /* Get server reply. */
           type = packet_read(&plen);
           switch (type) {
           case SSH_SMSG_FAILURE:
                   /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */
                   debug("Kerberos V4 authentication failed.");
                   return 0;
                   break;
   
           case SSH_SMSG_AUTH_KERBEROS_RESPONSE:
                   /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */
                   debug("Kerberos V4 authentication accepted.");
   
                   /* Get server's response. */
                   reply = packet_get_string((unsigned int *) &auth.length);
                   memcpy(auth.dat, reply, auth.length);
                   xfree(reply);
   
                   packet_integrity_check(plen, 4 + auth.length, type);
   
                   /* If his response isn't properly encrypted with the
                      session key, and the decrypted checksum fails to match,
                      he's bogus. Bail out. */
                   r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session,
                                   &foreign, &local, &msg_data);
                   if (r != KSUCCESS) {
                           debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]);
                           packet_disconnect("Kerberos V4 challenge failed!");
                   }
                   /* Fetch the (incremented) checksum that we supplied in the request. */
                   (void) memcpy((char *) &cksum, (char *) msg_data.app_data, sizeof(cksum));
                   cksum = ntohl(cksum);
   
                   /* If it matches, we're golden. */
                   if (cksum == checksum + 1) {
                           debug("Kerberos V4 challenge successful.");
                           return 1;
                   } else
                           packet_disconnect("Kerberos V4 challenge failed!");
                   break;
   
           default:
                   packet_disconnect("Protocol error on Kerberos V4 response: %d", type);
           }
           return 0;
 }  }
   
 #endif /* KRB4 */  #endif /* KRB4 */
   
 #ifdef AFS  #ifdef AFS
 int send_kerberos_tgt()  int
   send_kerberos_tgt()
 {  {
   CREDENTIALS *creds;          CREDENTIALS *creds;
   char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];          char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ];
   int r, type, plen;          int r, type, plen;
   unsigned char buffer[8192];          unsigned char buffer[8192];
   struct stat st;          struct stat st;
   
   /* Don't do anything if we don't have any tickets. */          /* Don't do anything if we don't have any tickets. */
   if (stat(tkt_string(), &st) < 0) return 0;          if (stat(tkt_string(), &st) < 0)
                   return 0;
   creds = xmalloc(sizeof(*creds));  
   
   if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) {  
     debug("Kerberos V4 tf_fullname failed: %s",krb_err_txt[r]);  
     return 0;  
   }  
   if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) {  
     debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]);  
     return 0;  
   }  
   if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) {  
     debug("Kerberos V4 ticket expired: %s", TKT_FILE);  
     return 0;  
   }  
   
   creds_to_radix(creds, buffer);          creds = xmalloc(sizeof(*creds));
   xfree(creds);  
   
   packet_start(SSH_CMSG_HAVE_KERBEROS_TGT);  
   packet_put_string((char *)buffer, strlen(buffer));  
   packet_send();  
   packet_write_wait();  
   
   type = packet_read(&plen);          if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) {
                   debug("Kerberos V4 tf_fullname failed: %s", krb_err_txt[r]);
   if (type == SSH_SMSG_FAILURE)                  return 0;
     debug("Kerberos TGT for realm %s rejected.", prealm);          }
   else if (type != SSH_SMSG_SUCCESS)          if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) {
     packet_disconnect("Protocol error on Kerberos TGT response: %d", type);                  debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]);
                   return 0;
           }
           if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) {
                   debug("Kerberos V4 ticket expired: %s", TKT_FILE);
                   return 0;
           }
           creds_to_radix(creds, buffer);
           xfree(creds);
   
   return 1;          packet_start(SSH_CMSG_HAVE_KERBEROS_TGT);
           packet_put_string((char *) buffer, strlen(buffer));
           packet_send();
           packet_write_wait();
   
           type = packet_read(&plen);
   
           if (type == SSH_SMSG_FAILURE)
                   debug("Kerberos TGT for realm %s rejected.", prealm);
           else if (type != SSH_SMSG_SUCCESS)
                   packet_disconnect("Protocol error on Kerberos TGT response: %d", type);
   
           return 1;
 }  }
   
 void send_afs_tokens(void)  void
   send_afs_tokens(void)
 {  {
   CREDENTIALS creds;          CREDENTIALS creds;
   struct ViceIoctl parms;          struct ViceIoctl parms;
   struct ClearToken ct;          struct ClearToken ct;
   int i, type, len, plen;          int i, type, len, plen;
   char buf[2048], *p, *server_cell;          char buf[2048], *p, *server_cell;
   unsigned char buffer[8192];          unsigned char buffer[8192];
   
   /* Move over ktc_GetToken, here's something leaner. */          /* Move over ktc_GetToken, here's something leaner. */
   for (i = 0; i < 100; i++) { /* just in case */          for (i = 0; i < 100; i++) {     /* just in case */
     parms.in = (char *)&i;                  parms.in = (char *) &i;
     parms.in_size = sizeof(i);                  parms.in_size = sizeof(i);
     parms.out = buf;                  parms.out = buf;
     parms.out_size = sizeof(buf);                  parms.out_size = sizeof(buf);
     if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) break;                  if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0)
     p = buf;                          break;
                   p = buf;
     /* Get secret token. */  
     memcpy(&creds.ticket_st.length, p, sizeof(unsigned int));  
     if (creds.ticket_st.length > MAX_KTXT_LEN) break;  
     p += sizeof(unsigned int);  
     memcpy(creds.ticket_st.dat, p, creds.ticket_st.length);  
     p += creds.ticket_st.length;  
   
     /* Get clear token. */  
     memcpy(&len, p, sizeof(len));  
     if (len != sizeof(struct ClearToken)) break;  
     p += sizeof(len);  
     memcpy(&ct, p, len);  
     p += len;  
     p += sizeof(len); /* primary flag */  
     server_cell = p;  
   
     /* Flesh out our credentials. */                  /* Get secret token. */
     strlcpy(creds.service, "afs", sizeof creds.service);                  memcpy(&creds.ticket_st.length, p, sizeof(unsigned int));
     creds.instance[0] = '\0';                  if (creds.ticket_st.length > MAX_KTXT_LEN)
     strlcpy(creds.realm, server_cell, REALM_SZ);                          break;
     memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ);                  p += sizeof(unsigned int);
     creds.issue_date = ct.BeginTimestamp;                  memcpy(creds.ticket_st.dat, p, creds.ticket_st.length);
     creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp);                  p += creds.ticket_st.length;
     creds.kvno = ct.AuthHandle;  
     snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId);  
     creds.pinst[0] = '\0';  
   
     /* Encode token, ship it off. */                  /* Get clear token. */
     if (!creds_to_radix(&creds, buffer)) break;                  memcpy(&len, p, sizeof(len));
     packet_start(SSH_CMSG_HAVE_AFS_TOKEN);                  if (len != sizeof(struct ClearToken))
     packet_put_string((char *)buffer, strlen(buffer));                          break;
     packet_send();                  p += sizeof(len);
     packet_write_wait();                  memcpy(&ct, p, len);
                   p += len;
                   p += sizeof(len);       /* primary flag */
                   server_cell = p;
   
     /* Roger, Roger. Clearance, Clarence. What's your vector, Victor? */                  /* Flesh out our credentials. */
     type = packet_read(&plen);                  strlcpy(creds.service, "afs", sizeof creds.service);
                   creds.instance[0] = '\0';
                   strlcpy(creds.realm, server_cell, REALM_SZ);
                   memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ);
                   creds.issue_date = ct.BeginTimestamp;
                   creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp);
                   creds.kvno = ct.AuthHandle;
                   snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId);
                   creds.pinst[0] = '\0';
   
     if (type == SSH_SMSG_FAILURE)                  /* Encode token, ship it off. */
       debug("AFS token for cell %s rejected.", server_cell);                  if (!creds_to_radix(&creds, buffer))
     else if (type != SSH_SMSG_SUCCESS)                          break;
       packet_disconnect("Protocol error on AFS token response: %d", type);                  packet_start(SSH_CMSG_HAVE_AFS_TOKEN);
   }                  packet_put_string((char *) buffer, strlen(buffer));
                   packet_send();
                   packet_write_wait();
   
                   /* Roger, Roger. Clearance, Clarence. What's your vector,
                      Victor? */
                   type = packet_read(&plen);
   
                   if (type == SSH_SMSG_FAILURE)
                           debug("AFS token for cell %s rejected.", server_cell);
                   else if (type != SSH_SMSG_SUCCESS)
                           packet_disconnect("Protocol error on AFS token response: %d", type);
           }
 }  }
   
 #endif /* AFS */  #endif /* AFS */
   
 /* Waits for the server identification string, and sends our own identification  /* Waits for the server identification string, and sends our own
    string. */     identification string. */
   
 void ssh_exchange_identification()  void
   ssh_exchange_identification()
 {  {
   char buf[256], remote_version[256]; /* must be same size! */          char buf[256], remote_version[256];     /* must be same size! */
   int remote_major, remote_minor, i;          int remote_major, remote_minor, i;
   int connection_in = packet_get_connection_in();          int connection_in = packet_get_connection_in();
   int connection_out = packet_get_connection_out();          int connection_out = packet_get_connection_out();
   extern Options options;          extern Options options;
   
   /* Read other side\'s version identification. */          /* Read other side\'s version identification. */
   for (i = 0; i < sizeof(buf) - 1; i++)          for (i = 0; i < sizeof(buf) - 1; i++) {
     {                  if (read(connection_in, &buf[i], 1) != 1)
       if (read(connection_in, &buf[i], 1) != 1)                          fatal("ssh_exchange_identification: read: %.100s", strerror(errno));
         fatal("ssh_exchange_identification: read: %.100s", strerror(errno));                  if (buf[i] == '\r') {
       if (buf[i] == '\r')                          buf[i] = '\n';
         {                          buf[i + 1] = 0;
           buf[i] = '\n';                          break;
           buf[i + 1] = 0;                  }
           break;                  if (buf[i] == '\n') {
                           buf[i + 1] = 0;
                           break;
                   }
         }          }
       if (buf[i] == '\n')          buf[sizeof(buf) - 1] = 0;
         {  
           buf[i + 1] = 0;  
           break;  
         }  
     }  
   buf[sizeof(buf) - 1] = 0;  
   
   /* Check that the versions match.  In future this might accept several  
      versions and set appropriate flags to handle them. */  
   if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor,  
              remote_version) != 3)  
     fatal("Bad remote protocol version identification: '%.100s'", buf);  
   debug("Remote protocol version %d.%d, remote software version %.100s",  
         remote_major, remote_minor, remote_version);  
   
   /* Check if the remote protocol version is too old. */          /* Check that the versions match.  In future this might accept
   if (remote_major == 1 && remote_minor < 3)             several versions and set appropriate flags to handle them. */
     fatal("Remote machine has too old SSH software version.");          if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor,
                      remote_version) != 3)
                   fatal("Bad remote protocol version identification: '%.100s'", buf);
           debug("Remote protocol version %d.%d, remote software version %.100s",
                 remote_major, remote_minor, remote_version);
   
   /* We speak 1.3, too. */          /* Check if the remote protocol version is too old. */
   if (remote_major == 1 && remote_minor == 3) {          if (remote_major == 1 && remote_minor < 3)
     enable_compat13();                  fatal("Remote machine has too old SSH software version.");
     if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) {  
       log("Agent forwarding disabled, remote version '%s' is not compatible.",          /* We speak 1.3, too. */
             remote_version);          if (remote_major == 1 && remote_minor == 3) {
       options.forward_agent = 0;                  enable_compat13();
     }                  if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) {
   }                          log("Agent forwarding disabled, remote version '%s' is not compatible.",
                               remote_version);
                           options.forward_agent = 0;
                   }
           }
 #if 0  #if 0
   /* Removed for now, to permit compatibility with latter versions.  The server          /* Removed for now, to permit compatibility with latter versions.
      will reject our version and disconnect if it doesn't support it. */             The server will reject our version and disconnect if it doesn't
   if (remote_major != PROTOCOL_MAJOR)             support it. */
     fatal("Protocol major versions differ: %d vs. %d",          if (remote_major != PROTOCOL_MAJOR)
           PROTOCOL_MAJOR, remote_major);                  fatal("Protocol major versions differ: %d vs. %d",
                         PROTOCOL_MAJOR, remote_major);
 #endif  #endif
   
   /* Send our own protocol version identification. */          /* Send our own protocol version identification. */
   snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",          snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
           PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);                   PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
   if (write(connection_out, buf, strlen(buf)) != strlen(buf))          if (write(connection_out, buf, strlen(buf)) != strlen(buf))
     fatal("write: %.100s", strerror(errno));                  fatal("write: %.100s", strerror(errno));
 }  }
   
 int ssh_cipher_default = SSH_CIPHER_3DES;  int ssh_cipher_default = SSH_CIPHER_3DES;
   
 int read_yes_or_no(const char *prompt, int defval)  int
   read_yes_or_no(const char *prompt, int defval)
 {  {
   char buf[1024];          char buf[1024];
   FILE *f;          FILE *f;
   int retval = -1;          int retval = -1;
   
   if (isatty(0))  
     f = stdin;  
   else  
     f = fopen("/dev/tty", "rw");  
   
   if (f == NULL)          if (isatty(0))
     return 0;                  f = stdin;
           else
                   f = fopen("/dev/tty", "rw");
   
   fflush(stdout);          if (f == NULL)
                   return 0;
   
   while (1)          fflush(stdout);
     {  
       fprintf(stderr, "%s", prompt);  
       if (fgets(buf, sizeof(buf), f) == NULL)  
         {  
           /* Print a newline (the prompt probably didn\'t have one). */  
           fprintf(stderr, "\n");  
           strlcpy(buf, "no", sizeof buf);  
         }  
       /* Remove newline from response. */  
       if (strchr(buf, '\n'))  
         *strchr(buf, '\n') = 0;  
   
       if (buf[0] == 0)          while (1) {
         retval = defval;                  fprintf(stderr, "%s", prompt);
       if (strcmp(buf, "yes") == 0)                  if (fgets(buf, sizeof(buf), f) == NULL) {
         retval = 1;                          /* Print a newline (the prompt probably didn\'t have one). */
       if (strcmp(buf, "no") == 0)                          fprintf(stderr, "\n");
         retval = 0;                          strlcpy(buf, "no", sizeof buf);
                   }
                   /* Remove newline from response. */
                   if (strchr(buf, '\n'))
                           *strchr(buf, '\n') = 0;
   
       if (retval != -1)                  if (buf[0] == 0)
         {                          retval = defval;
           if (f != stdin)                  if (strcmp(buf, "yes") == 0)
             fclose(f);                          retval = 1;
           return retval;                  if (strcmp(buf, "no") == 0)
                           retval = 0;
   
                   if (retval != -1) {
                           if (f != stdin)
                                   fclose(f);
                           return retval;
                   }
         }          }
     }  
 }  }
   
 /* Starts a dialog with the server, and authenticates the current user on the  /* Starts a dialog with the server, and authenticates the current user on the
    server.  This does not need any extra privileges.  The basic connection     server.  This does not need any extra privileges.  The basic connection
    to the server must already have been established before this is called.     to the server must already have been established before this is called.
    User is the remote user; if it is NULL, the current local user name will     User is the remote user; if it is NULL, the current local user name will
    be used.  Anonymous indicates that no rhosts authentication will be used.     be used.  Anonymous indicates that no rhosts authentication will be used.
    If login fails, this function prints an error and never returns.     If login fails, this function prints an error and never returns.
    This function does not require super-user privileges. */     This function does not require super-user privileges. */
   
 void ssh_login(int host_key_valid,  void
                RSA *own_host_key,  ssh_login(int host_key_valid,
                const char *orighost,            RSA *own_host_key,
                struct sockaddr_in *hostaddr,            const char *orighost,
                uid_t original_real_uid)            struct sockaddr_in *hostaddr,
             uid_t original_real_uid)
 {  {
   extern Options options;          extern Options options;
   int i, type;          int i, type;
   char *password;          char *password;
   struct passwd *pw;          struct passwd *pw;
   BIGNUM *key;          BIGNUM *key;
   RSA *host_key, *file_key;          RSA *host_key, *file_key;
   RSA *public_key;          RSA *public_key;
   int bits, rbits;          int bits, rbits;
   unsigned char session_key[SSH_SESSION_KEY_LENGTH];          unsigned char session_key[SSH_SESSION_KEY_LENGTH];
   const char *server_user, *local_user;          const char *server_user, *local_user;
   char *cp, *host, *ip = NULL;          char *cp, *host, *ip = NULL;
   unsigned char check_bytes[8];          char hostline[1000], *hostp;
   unsigned int supported_ciphers, supported_authentications, protocol_flags;          unsigned char check_bytes[8];
   HostStatus host_status;          unsigned int supported_ciphers, supported_authentications, protocol_flags;
   HostStatus ip_status;          HostStatus host_status;
   int host_ip_differ = 0;          HostStatus ip_status;
   int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;          int host_ip_differ = 0;
   int payload_len, clen, sum_len = 0;          int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
   u_int32_t rand = 0;          int payload_len, clen, sum_len = 0;
           u_int32_t rand = 0;
   
   if (options.check_host_ip)          if (options.check_host_ip)
     ip = xstrdup(inet_ntoa(hostaddr->sin_addr));                  ip = xstrdup(inet_ntoa(hostaddr->sin_addr));
   
   /* Convert the user-supplied hostname into all lowercase. */          /* Convert the user-supplied hostname into all lowercase. */
   host = xstrdup(orighost);          host = xstrdup(orighost);
   for (cp = host; *cp; cp++)          for (cp = host; *cp; cp++)
     if (isupper(*cp))                  if (isupper(*cp))
       *cp = tolower(*cp);                          *cp = tolower(*cp);
   
   /* Exchange protocol version identification strings with the server. */          /* Exchange protocol version identification strings with the server. */
   ssh_exchange_identification();          ssh_exchange_identification();
   
   /* Put the connection into non-blocking mode. */          /* Put the connection into non-blocking mode. */
   packet_set_nonblocking();          packet_set_nonblocking();
   
   /* Get local user name.  Use it as server user if no user name          /* Get local user name.  Use it as server user if no user name was given. */
      was given. */          pw = getpwuid(original_real_uid);
   pw = getpwuid(original_real_uid);          if (!pw)
   if (!pw)                  fatal("User id %d not found from user database.", original_real_uid);
     fatal("User id %d not found from user database.", original_real_uid);          local_user = xstrdup(pw->pw_name);
   local_user = xstrdup(pw->pw_name);          server_user = options.user ? options.user : local_user;
   server_user = options.user ? options.user : local_user;  
   
   debug("Waiting for server public key.");          debug("Waiting for server public key.");
   
   /* Wait for a public key packet from the server. */          /* Wait for a public key packet from the server. */
   packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY);          packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY);
   
   /* Get check bytes from the packet. */          /* Get check bytes from the packet. */
   for (i = 0; i < 8; i++)          for (i = 0; i < 8; i++)
     check_bytes[i] = packet_get_char();                  check_bytes[i] = packet_get_char();
   
   /* Get the public key. */          /* Get the public key. */
   public_key = RSA_new();          public_key = RSA_new();
   bits = packet_get_int();      /* bits */          bits = packet_get_int();/* bits */
   public_key->e = BN_new();          public_key->e = BN_new();
   packet_get_bignum(public_key->e, &clen);          packet_get_bignum(public_key->e, &clen);
   sum_len += clen;          sum_len += clen;
   public_key->n = BN_new();          public_key->n = BN_new();
   packet_get_bignum(public_key->n, &clen);          packet_get_bignum(public_key->n, &clen);
   sum_len += clen;          sum_len += clen;
   
   rbits = BN_num_bits(public_key->n);          rbits = BN_num_bits(public_key->n);
   if (bits != rbits) {          if (bits != rbits) {
     log("Warning: Server lies about size of server public key: "                  log("Warning: Server lies about size of server public key: "
         "actual size is %d bits vs. announced %d.", rbits, bits);                      "actual size is %d bits vs. announced %d.", rbits, bits);
     log("Warning: This may be due to an old implementation of ssh.");                  log("Warning: This may be due to an old implementation of ssh.");
   }          }
           /* Get the host key. */
           host_key = RSA_new();
           bits = packet_get_int();/* bits */
           host_key->e = BN_new();
           packet_get_bignum(host_key->e, &clen);
           sum_len += clen;
           host_key->n = BN_new();
           packet_get_bignum(host_key->n, &clen);
           sum_len += clen;
   
   /* Get the host key. */          rbits = BN_num_bits(host_key->n);
   host_key = RSA_new();          if (bits != rbits) {
   bits = packet_get_int();      /* bits */                  log("Warning: Server lies about size of server host key: "
   host_key->e = BN_new();                      "actual size is %d bits vs. announced %d.", rbits, bits);
   packet_get_bignum(host_key->e, &clen);                  log("Warning: This may be due to an old implementation of ssh.");
   sum_len += clen;          }
   host_key->n = BN_new();          /* Store the host key from the known host file in here so that we
   packet_get_bignum(host_key->n, &clen);             can compare it with the key for the IP address. */
   sum_len += clen;          file_key = RSA_new();
           file_key->n = BN_new();
           file_key->e = BN_new();
   
   rbits = BN_num_bits(host_key->n);          /* Get protocol flags. */
   if (bits != rbits) {          protocol_flags = packet_get_int();
     log("Warning: Server lies about size of server host key: "          packet_set_protocol_flags(protocol_flags);
         "actual size is %d bits vs. announced %d.", rbits, bits);  
     log("Warning: This may be due to an old implementation of ssh.");  
   }  
   
   /* Store the host key from the known host file in here          /* Get supported cipher types. */
    * so that we can compare it with the key for the IP          supported_ciphers = packet_get_int();
    * address. */  
   file_key = RSA_new();  
   file_key->n = BN_new();  
   file_key->e = BN_new();  
   
   /* Get protocol flags. */          /* Get supported authentication types. */
   protocol_flags = packet_get_int();          supported_authentications = packet_get_int();
   packet_set_protocol_flags(protocol_flags);  
   
   /* Get supported cipher types. */          debug("Received server public key (%d bits) and host key (%d bits).",
   supported_ciphers = packet_get_int();                BN_num_bits(public_key->n), BN_num_bits(host_key->n));
   
   /* Get supported authentication types. */          packet_integrity_check(payload_len,
   supported_authentications = packet_get_int();                                 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4,
                                  SSH_SMSG_PUBLIC_KEY);
   
   debug("Received server public key (%d bits) and host key (%d bits).",          /* Compute the session id. */
         BN_num_bits(public_key->n), BN_num_bits(host_key->n));          compute_session_id(session_id, check_bytes, host_key->n, public_key->n);
   
   packet_integrity_check(payload_len,          /* Check if the host key is present in the user\'s list of known
                          8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4,             hosts or in the systemwide list. */
                          SSH_SMSG_PUBLIC_KEY);          host_status = check_host_in_hostfile(options.user_hostfile, host,
                                                host_key->e, host_key->n,
                                                file_key->e, file_key->n);
           if (host_status == HOST_NEW)
                   host_status = check_host_in_hostfile(options.system_hostfile, host,
                                                   host_key->e, host_key->n,
                                                  file_key->e, file_key->n);
           /* Force accepting of the host key for localhost and 127.0.0.1.
              The problem is that if the home directory is NFS-mounted to
              multiple machines, localhost will refer to a different machine
              in each of them, and the user will get bogus HOST_CHANGED
              warnings.  This essentially disables host authentication for
              localhost; however, this is probably not a real problem. */
           if (local) {
                   debug("Forcing accepting of host key for localhost.");
                   host_status = HOST_OK;
           }
           /* Also perform check for the ip address, skip the check if we are
              localhost or the hostname was an ip address to begin with */
           if (options.check_host_ip && !local && strcmp(host, ip)) {
                   RSA *ip_key = RSA_new();
                   ip_key->n = BN_new();
                   ip_key->e = BN_new();
                   ip_status = check_host_in_hostfile(options.user_hostfile, ip,
                                                   host_key->e, host_key->n,
                                                      ip_key->e, ip_key->n);
   
   /* Compute the session id. */                  if (ip_status == HOST_NEW)
   compute_session_id(session_id, check_bytes, host_key->n, public_key->n);                          ip_status = check_host_in_hostfile(options.system_hostfile, ip,
                                                   host_key->e, host_key->n,
                                                      ip_key->e, ip_key->n);
                   if (host_status == HOST_CHANGED &&
                       (ip_status != HOST_CHANGED ||
                        (BN_cmp(ip_key->e, file_key->e) || BN_cmp(ip_key->n, file_key->n))))
                           host_ip_differ = 1;
   
   /* Check if the host key is present in the user\'s list of known hosts                  RSA_free(ip_key);
      or in the systemwide list. */          } else
   host_status = check_host_in_hostfile(options.user_hostfile, host,                  ip_status = host_status;
                                        host_key->e, host_key->n,  
                                        file_key->e, file_key->n);  
   if (host_status == HOST_NEW)  
     host_status = check_host_in_hostfile(options.system_hostfile, host,  
                                          host_key->e, host_key->n,  
                                          file_key->e, file_key->n);  
   /* Force accepting of the host key for localhost and 127.0.0.1.  
      The problem is that if the home directory is NFS-mounted to multiple  
      machines, localhost will refer to a different machine in each of them,  
      and the user will get bogus HOST_CHANGED warnings.  This essentially  
      disables host authentication for localhost; however, this is probably  
      not a real problem. */  
   if (local) {  
     debug("Forcing accepting of host key for localhost.");  
     host_status = HOST_OK;  
   }  
   
   /* Also perform check for the ip address, skip the check if we are          RSA_free(file_key);
      localhost or the hostname was an ip address to begin with */  
   if (options.check_host_ip && !local && strcmp(host, ip)) {  
     RSA *ip_key = RSA_new();  
     ip_key->n = BN_new();  
     ip_key->e = BN_new();  
     ip_status = check_host_in_hostfile(options.user_hostfile, ip,  
                                        host_key->e, host_key->n,  
                                        ip_key->e, ip_key->n);  
   
     if (ip_status == HOST_NEW)          switch (host_status) {
       ip_status = check_host_in_hostfile(options.system_hostfile, ip,          case HOST_OK:
                                          host_key->e, host_key->n,                  /* The host is known and the key matches. */
                                          ip_key->e, ip_key->n);                  debug("Host '%.200s' is known and matches the host key.", host);
     if (host_status == HOST_CHANGED &&                  if (options.check_host_ip) {
         (ip_status != HOST_CHANGED ||                          if (ip_status == HOST_NEW) {
          (BN_cmp(ip_key->e, file_key->e) || BN_cmp(ip_key->n, file_key->n))))                                  if (!add_host_to_hostfile(options.user_hostfile, ip,
       host_ip_differ = 1;                                                 host_key->e, host_key->n))
                                           log("Failed to add the host key for IP address '%.30s' to the list of known hosts (%.30s).",
                                               ip, options.user_hostfile);
                                   else
                                           log("Warning: Permanently added host key for IP address '%.30s' to the list of known hosts.",
                                               ip);
                           } else if (ip_status != HOST_OK)
                                   log("Warning: the host key for '%.200s' differs from the key for the IP address '%.30s'",
                                       host, ip);
                   }
                   break;
           case HOST_NEW:
                   /* The host is new. */
                   if (options.strict_host_key_checking == 1) {
                           /* User has requested strict host key checking.  We will not add the host key
                              automatically.  The only alternative left is to abort. */
                           fatal("No host key is known for %.200s and you have requested strict checking.", host);
                   } else if (options.strict_host_key_checking == 2) {
                           /* The default */
                           char prompt[1024];
                           char *fp = fingerprint(host_key->e, host_key->n);
                           snprintf(prompt, sizeof(prompt),
                                    "The authenticity of host '%.200s' can't be established.\n"
                                    "Key fingerprint is %d %s.\n"
                                    "Are you sure you want to continue connecting (yes/no)? ",
                                    host, BN_num_bits(host_key->n), fp);
                           if (!read_yes_or_no(prompt, -1))
                                   fatal("Aborted by user!\n");
                   }
                   if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) {
                           snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
                           hostp = hostline;
                   } else
                           hostp = host;
   
     RSA_free(ip_key);                  /* If not in strict mode, add the key automatically to the local known_hosts file. */
   } else                  if (!add_host_to_hostfile(options.user_hostfile, hostp,
     ip_status = host_status;                                            host_key->e, host_key->n))
                           log("Failed to add the host to the list of known hosts (%.500s).",
                               options.user_hostfile);
                   else
                           log("Warning: Permanently added '%.200s' to the list of known hosts.",
                               hostp);
                   break;
           case HOST_CHANGED:
                   if (options.check_host_ip && host_ip_differ) {
                           char *msg;
                           if (ip_status == HOST_NEW)
                                   msg = "is unknown";
                           else if (ip_status == HOST_OK)
                                   msg = "is unchanged";
                           else
                                   msg = "has a different value";
                           error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
                           error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
                           error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
                           error("The host key for %s has changed,", host);
                           error("and the key for the according IP address %s", ip);
                           error("%s. This could either mean that", msg);
                           error("DNS SPOOFING is happening or the IP address for the host");
                           error("and its host key have changed at the same time");
                   }
                   /* The host key has changed. */
                   error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
                   error("@       WARNING: HOST IDENTIFICATION HAS CHANGED!         @");
                   error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
                   error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
                   error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
                   error("It is also possible that the host key has just been changed.");
                   error("Please contact your system administrator.");
                   error("Add correct host key in %.100s to get rid of this message.",
                         options.user_hostfile);
   
   RSA_free(file_key);                  /* If strict host key checking is in use, the user will
                      have to edit the key manually and we can only abort. */
                   if (options.strict_host_key_checking)
                           fatal("Host key for %.200s has changed and you have requested strict checking.", host);
   
   switch (host_status) {                  /* If strict host key checking has not been requested, allow the connection
   case HOST_OK:                     but without password authentication or agent forwarding. */
     /* The host is known and the key matches. */                  if (options.password_authentication) {
     debug("Host '%.200s' is known and matches the host key.", host);                          error("Password authentication is disabled to avoid trojan horses.");
     if (options.check_host_ip) {                          options.password_authentication = 0;
       if (ip_status == HOST_NEW) {                  }
         if (!add_host_to_hostfile(options.user_hostfile, ip,                  if (options.forward_agent) {
                                   host_key->e, host_key->n))                          error("Agent forwarding is disabled to avoid trojan horses.");
           log("Failed to add the host key for IP address '%.30s' to the list of known hosts (%.30s).",                          options.forward_agent = 0;
               ip, options.user_hostfile);                  }
         else                  /* XXX Should permit the user to change to use the new id.
           log("Warning: Permanently added host key for IP address '%.30s' to the list of known hosts.",                     This could be done by converting the host key to an
                ip);                     identifying sentence, tell that the host identifies
       } else if (ip_status != HOST_OK)                     itself by that sentence, and ask the user if he/she
         log("Warning: the host key for '%.200s' differs from the key for the IP address '%.30s'",                     whishes to accept the authentication. */
             host, ip);                  break;
     }          }
   
     break;  
   case HOST_NEW:  
     {  
       char hostline[1000], *hostp = hostline;  
       /* The host is new. */  
       if (options.strict_host_key_checking == 1) {  
         /* User has requested strict host key checking.  We will not  
            add the host key automatically.  The only alternative left  
            is to abort. */  
         fatal("No host key is known for %.200s and you have requested strict checking.", host);  
       } else if (options.strict_host_key_checking == 2) { /* The default */  
         char prompt[1024];  
         char *fp = fingerprint(host_key->e, host_key->n);  
         snprintf(prompt, sizeof(prompt),  
                  "The authenticity of host '%.200s' can't be established.\n"  
                  "Key fingerprint is %d %s.\n"  
                  "Are you sure you want to continue connecting (yes/no)? ",  
                  host, BN_num_bits(host_key->n), fp);  
         if (!read_yes_or_no(prompt, -1))  
           fatal("Aborted by user!\n");  
       }  
   
       if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip))  
         snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);  
       else  
         hostp = host;  
   
       /* If not in strict mode, add the key automatically to the local  
          known_hosts file. */  
       if (!add_host_to_hostfile(options.user_hostfile, hostp,  
                                 host_key->e, host_key->n))  
         log("Failed to add the host to the list of known hosts (%.500s).",  
             options.user_hostfile);  
       else  
         log("Warning: Permanently added '%.200s' to the list of known hosts.",  
             hostp);  
       break;  
     }  
   case HOST_CHANGED:  
     if (options.check_host_ip) {  
       if (host_ip_differ) {  
         char *msg;  
         if (ip_status == HOST_NEW)  
           msg = "is unknown";  
         else if (ip_status == HOST_OK)  
           msg = "is unchanged";  
         else  
           msg = "has a different value";  
         error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");  
         error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");  
         error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");  
         error("The host key for %s has changed,", host);  
         error("and the key for the according IP address %s", ip);  
         error("%s. This could either mean that", msg);  
         error("DNS SPOOFING is happening or the IP address for the host");  
         error("and its host key have changed at the same time");  
       }  
     }  
   
     /* The host key has changed. */  
     error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");  
     error("@       WARNING: HOST IDENTIFICATION HAS CHANGED!         @");  
     error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");  
     error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");  
     error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");  
     error("It is also possible that the host key has just been changed.");  
     error("Please contact your system administrator.");  
     error("Add correct host key in %.100s to get rid of this message.",  
           options.user_hostfile);  
   
     /* If strict host key checking is in use, the user will have to edit  
        the key manually and we can only abort. */  
     if (options.strict_host_key_checking)  
       fatal("Host key for %.200s has changed and you have requested strict checking.", host);  
   
     /* If strict host key checking has not been requested, allow the  
        connection but without password authentication or  
        agent forwarding. */  
     if (options.password_authentication) {  
       error("Password authentication is disabled to avoid trojan horses.");  
       options.password_authentication = 0;  
     }  
     if (options.forward_agent) {  
       error("Agent forwarding is disabled to avoid trojan horses.");  
       options.forward_agent = 0;  
     }  
     /* XXX Should permit the user to change to use the new id.  This could  
        be done by converting the host key to an identifying sentence, tell  
        that the host identifies itself by that sentence, and ask the user  
        if he/she whishes to accept the authentication. */  
     break;  
   }  
   
   if (options.check_host_ip)          if (options.check_host_ip)
     xfree(ip);                  xfree(ip);
   
   /* Generate a session key. */  
   arc4random_stir();  
   
   /* Generate an encryption key for the session.   The key is a 256 bit  
      random number, interpreted as a 32-byte key, with the least significant  
      8 bits being the first byte of the key. */  
   for (i = 0; i < 32; i++) {  
     if (i % 4 == 0)  
       rand = arc4random();  
     session_key[i] = rand & 0xff;  
     rand >>= 8;  
   }  
   
   /* According to the protocol spec, the first byte of the session key is          /* Generate a session key. */
      the highest byte of the integer.  The session key is xored with the          arc4random_stir();
      first 16 bytes of the session id. */  
   key = BN_new();  
   BN_set_word(key, 0);  
   for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++)  
     {  
       BN_lshift(key, key, 8);  
       if (i < 16)  
         BN_add_word(key, session_key[i] ^ session_id[i]);  
       else  
         BN_add_word(key, session_key[i]);  
     }  
   
   /* Encrypt the integer using the public key and host key of the server          /* Generate an encryption key for the session.   The key is a 256
      (key with smaller modulus first). */             bit random number, interpreted as a 32-byte key, with the least
   if (BN_cmp(public_key->n, host_key->n) < 0)             significant 8 bits being the first byte of the key. */
     {          for (i = 0; i < 32; i++) {
       /* Public key has smaller modulus. */                  if (i % 4 == 0)
       if (BN_num_bits(host_key->n) <                          rand = arc4random();
           BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED) {                  session_key[i] = rand & 0xff;
         fatal("respond_to_rsa_challenge: host_key %d < public_key %d + "                  rand >>= 8;
               "SSH_KEY_BITS_RESERVED %d",          }
               BN_num_bits(host_key->n),  
               BN_num_bits(public_key->n),  
               SSH_KEY_BITS_RESERVED);  
       }  
   
       rsa_public_encrypt(key, key, public_key);          /* According to the protocol spec, the first byte of the session
       rsa_public_encrypt(key, key, host_key);             key is the highest byte of the integer.  The session key is
     }             xored with the first 16 bytes of the session id. */
   else          key = BN_new();
     {          BN_set_word(key, 0);
       /* Host key has smaller modulus (or they are equal). */          for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) {
       if (BN_num_bits(public_key->n) <                  BN_lshift(key, key, 8);
           BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED) {                  if (i < 16)
         fatal("respond_to_rsa_challenge: public_key %d < host_key %d + "                          BN_add_word(key, session_key[i] ^ session_id[i]);
               "SSH_KEY_BITS_RESERVED %d",                  else
               BN_num_bits(public_key->n),                          BN_add_word(key, session_key[i]);
               BN_num_bits(host_key->n),          }
               SSH_KEY_BITS_RESERVED);  
       }  
   
       rsa_public_encrypt(key, key, host_key);          /* Encrypt the integer using the public key and host key of the
       rsa_public_encrypt(key, key, public_key);             server (key with smaller modulus first). */
     }          if (BN_cmp(public_key->n, host_key->n) < 0) {
                   /* Public key has smaller modulus. */
                   if (BN_num_bits(host_key->n) <
                       BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED) {
                           fatal("respond_to_rsa_challenge: host_key %d < public_key %d + "
                                 "SSH_KEY_BITS_RESERVED %d",
                                 BN_num_bits(host_key->n),
                                 BN_num_bits(public_key->n),
                                 SSH_KEY_BITS_RESERVED);
                   }
                   rsa_public_encrypt(key, key, public_key);
                   rsa_public_encrypt(key, key, host_key);
           } else {
                   /* Host key has smaller modulus (or they are equal). */
                   if (BN_num_bits(public_key->n) <
                       BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED) {
                           fatal("respond_to_rsa_challenge: public_key %d < host_key %d + "
                                 "SSH_KEY_BITS_RESERVED %d",
                                 BN_num_bits(public_key->n),
                                 BN_num_bits(host_key->n),
                                 SSH_KEY_BITS_RESERVED);
                   }
                   rsa_public_encrypt(key, key, host_key);
                   rsa_public_encrypt(key, key, public_key);
           }
   
   if (options.cipher == SSH_CIPHER_NOT_SET) {          if (options.cipher == SSH_CIPHER_NOT_SET) {
     if (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default))                  if (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default))
       options.cipher = ssh_cipher_default;                          options.cipher = ssh_cipher_default;
     else {                  else {
       debug("Cipher %s not supported, using %.100s instead.",                          debug("Cipher %s not supported, using %.100s instead.",
             cipher_name(ssh_cipher_default),                                cipher_name(ssh_cipher_default),
             cipher_name(SSH_FALLBACK_CIPHER));                                cipher_name(SSH_FALLBACK_CIPHER));
       options.cipher = SSH_FALLBACK_CIPHER;                          options.cipher = SSH_FALLBACK_CIPHER;
     }                  }
   }          }
           /* Check that the selected cipher is supported. */
           if (!(supported_ciphers & (1 << options.cipher)))
                   fatal("Selected cipher type %.100s not supported by server.",
                         cipher_name(options.cipher));
   
   /* Check that the selected cipher is supported. */          debug("Encryption type: %.100s", cipher_name(options.cipher));
   if (!(supported_ciphers & (1 << options.cipher)))  
     fatal("Selected cipher type %.100s not supported by server.",  
           cipher_name(options.cipher));  
   
   debug("Encryption type: %.100s", cipher_name(options.cipher));          /* Send the encrypted session key to the server. */
           packet_start(SSH_CMSG_SESSION_KEY);
           packet_put_char(options.cipher);
   
   /* Send the encrypted session key to the server. */          /* Send the check bytes back to the server. */
   packet_start(SSH_CMSG_SESSION_KEY);          for (i = 0; i < 8; i++)
   packet_put_char(options.cipher);                  packet_put_char(check_bytes[i]);
   
   /* Send the check bytes back to the server. */          /* Send the encrypted encryption key. */
   for (i = 0; i < 8; i++)          packet_put_bignum(key);
     packet_put_char(check_bytes[i]);  
   
   /* Send the encrypted encryption key. */          /* Send protocol flags. */
   packet_put_bignum(key);          packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
   
   /* Send protocol flags. */          /* Send the packet now. */
   packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN);          packet_send();
           packet_write_wait();
   
   /* Send the packet now. */          /* Destroy the session key integer and the public keys since we no longer need them. */
   packet_send();          BN_clear_free(key);
   packet_write_wait();          RSA_free(public_key);
           RSA_free(host_key);
   
   /* Destroy the session key integer and the public keys since we no longer          debug("Sent encrypted session key.");
      need them. */  
   BN_clear_free(key);  
   RSA_free(public_key);  
   RSA_free(host_key);  
   
   debug("Sent encrypted session key.");          /* Set the encryption key. */
           packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, options.cipher);
   /* Set the encryption key. */  
   packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, options.cipher);  
   
   /* We will no longer need the session key here.  Destroy any extra copies. */          /* We will no longer need the session key here.  Destroy any extra copies. */
   memset(session_key, 0, sizeof(session_key));          memset(session_key, 0, sizeof(session_key));
   
   /* Expect a success message from the server.  Note that this message will          /* Expect a success message from the server.  Note that this
      be received in encrypted form. */             message will be received in encrypted form. */
   packet_read_expect(&payload_len, SSH_SMSG_SUCCESS);          packet_read_expect(&payload_len, SSH_SMSG_SUCCESS);
   
   debug("Received encrypted confirmation.");          debug("Received encrypted confirmation.");
   
   /* Send the name of the user to log in as on the server. */          /* Send the name of the user to log in as on the server. */
   packet_start(SSH_CMSG_USER);          packet_start(SSH_CMSG_USER);
   packet_put_string(server_user, strlen(server_user));          packet_put_string(server_user, strlen(server_user));
   packet_send();          packet_send();
   packet_write_wait();          packet_write_wait();
   
   /* The server should respond with success if no authentication is needed          /* The server should respond with success if no authentication is
      (the user has no password).  Otherwise the server responds with             needed (the user has no password).  Otherwise the server
      failure. */             responds with failure. */
   type = packet_read(&payload_len);          type = packet_read(&payload_len);
   if (type == SSH_SMSG_SUCCESS)  
     return;  /* Connection was accepted without authentication. */  
   if (type != SSH_SMSG_FAILURE)  
     packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER",  
                       type);  
   
 #ifdef AFS  
   /* Try Kerberos tgt passing if the server supports it. */  
   if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) &&  
       options.kerberos_tgt_passing)  
     {  
       if (options.cipher == SSH_CIPHER_NONE)  
         log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!");  
       (void)send_kerberos_tgt();  
     }  
   
   /* Try AFS token passing if the server supports it. */          /* check whether the connection was accepted without authentication. */
   if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) &&          if (type == SSH_SMSG_SUCCESS)
       options.afs_token_passing && k_hasafs())  {                  return;
     if (options.cipher == SSH_CIPHER_NONE)          if (type != SSH_SMSG_FAILURE)
       log("WARNING: Encryption is disabled! Token will be transmitted in the clear!");                  packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER",
     send_afs_tokens();                                    type);
   }  
   #ifdef AFS
           /* Try Kerberos tgt passing if the server supports it. */
           if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) &&
               options.kerberos_tgt_passing) {
                   if (options.cipher == SSH_CIPHER_NONE)
                           log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!");
                   (void) send_kerberos_tgt();
           }
           /* Try AFS token passing if the server supports it. */
           if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) &&
               options.afs_token_passing && k_hasafs()) {
                   if (options.cipher == SSH_CIPHER_NONE)
                           log("WARNING: Encryption is disabled! Token will be transmitted in the clear!");
                   send_afs_tokens();
           }
 #endif /* AFS */  #endif /* AFS */
   
 #ifdef KRB4  #ifdef KRB4
   if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) &&          if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) &&
       options.kerberos_authentication)              options.kerberos_authentication) {
     {                  debug("Trying Kerberos authentication.");
       debug("Trying Kerberos authentication.");                  if (try_kerberos_authentication()) {
       if (try_kerberos_authentication()) {                          /* The server should respond with success or failure. */
         /* The server should respond with success or failure. */                          type = packet_read(&payload_len);
         type = packet_read(&payload_len);                          if (type == SSH_SMSG_SUCCESS)
         if (type == SSH_SMSG_SUCCESS)                                  return;
           return; /* Successful connection. */                          if (type != SSH_SMSG_FAILURE)
         if (type != SSH_SMSG_FAILURE)                                  packet_disconnect("Protocol error: got %d in response to Kerberos auth", type);
           packet_disconnect("Protocol error: got %d in response to Kerberos auth", type);                  }
       }          }
     }  
 #endif /* KRB4 */  #endif /* KRB4 */
   
   /* Use rhosts authentication if running in privileged socket and we do not  
      wish to remain anonymous. */  
   if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) &&  
       options.rhosts_authentication)  
     {  
       debug("Trying rhosts authentication.");  
       packet_start(SSH_CMSG_AUTH_RHOSTS);  
       packet_put_string(local_user, strlen(local_user));  
       packet_send();  
       packet_write_wait();  
   
       /* The server should respond with success or failure. */          /* Use rhosts authentication if running in privileged socket and
       type = packet_read(&payload_len);             we do not wish to remain anonymous. */
       if (type == SSH_SMSG_SUCCESS)          if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) &&
         return; /* Successful connection. */              options.rhosts_authentication) {
       if (type != SSH_SMSG_FAILURE)                  debug("Trying rhosts authentication.");
         packet_disconnect("Protocol error: got %d in response to rhosts auth",                  packet_start(SSH_CMSG_AUTH_RHOSTS);
                           type);                  packet_put_string(local_user, strlen(local_user));
     }                  packet_send();
                   packet_write_wait();
   
   /* Try .rhosts or /etc/hosts.equiv authentication with RSA host                  /* The server should respond with success or failure. */
      authentication. */                  type = packet_read(&payload_len);
   if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) &&                  if (type == SSH_SMSG_SUCCESS)
       options.rhosts_rsa_authentication && host_key_valid)                          return;
     {                  if (type != SSH_SMSG_FAILURE)
       if (try_rhosts_rsa_authentication(local_user, own_host_key))                          packet_disconnect("Protocol error: got %d in response to rhosts auth",
         return; /* Successful authentication. */                                            type);
     }          }
           /* Try .rhosts or /etc/hosts.equiv authentication with RSA host
              authentication. */
           if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) &&
               options.rhosts_rsa_authentication && host_key_valid) {
                   if (try_rhosts_rsa_authentication(local_user, own_host_key))
                           return;
           }
           /* Try RSA authentication if the server supports it. */
           if ((supported_authentications & (1 << SSH_AUTH_RSA)) &&
               options.rsa_authentication) {
                   /* Try RSA authentication using the authentication agent.
                      The agent is tried first because no passphrase is
                      needed for it, whereas identity files may require
                      passphrases. */
                   if (try_agent_authentication())
                           return;
   
   /* Try RSA authentication if the server supports it. */                  /* Try RSA authentication for each identity. */
   if ((supported_authentications & (1 << SSH_AUTH_RSA)) &&                  for (i = 0; i < options.num_identity_files; i++)
       options.rsa_authentication)                          if (try_rsa_authentication(pw, options.identity_files[i]))
     {                                  return;
       /* Try RSA authentication using the authentication agent.  The agent          }
          is tried first because no passphrase is needed for it, whereas          /* Try skey authentication if the server supports it. */
          identity files may require passphrases. */          if ((supported_authentications & (1 << SSH_AUTH_TIS)) &&
       if (try_agent_authentication())              options.skey_authentication && !options.batch_mode) {
         return; /* Successful connection. */                  debug("Doing skey authentication.");
   
       /* Try RSA authentication for each identity. */                  /* request a challenge */
       for (i = 0; i < options.num_identity_files; i++)                  packet_start(SSH_CMSG_AUTH_TIS);
         if (try_rsa_authentication(pw, options.identity_files[i]))                  packet_send();
           return; /* Successful connection. */                  packet_write_wait();
     }  
   
   /* Try skey authentication if the server supports it. */                  type = packet_read(&payload_len);
   if ((supported_authentications & (1 << SSH_AUTH_TIS)) &&                  if (type != SSH_SMSG_FAILURE &&
       options.skey_authentication && !options.batch_mode)                      type != SSH_SMSG_AUTH_TIS_CHALLENGE) {
     {                          packet_disconnect("Protocol error: got %d in response "
       debug("Doing skey authentication.");                                            "to skey auth", type);
                   }
       /* request a challenge */                  if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) {
       packet_start(SSH_CMSG_AUTH_TIS);                          debug("No challenge for skey authentication.");
       packet_send();                  } else {
       packet_write_wait();                          char *challenge, *response;
                           challenge = packet_get_string(&payload_len);
       type = packet_read(&payload_len);                          if (options.cipher == SSH_CIPHER_NONE)
       if (type != SSH_SMSG_FAILURE &&                                  log("WARNING: Encryption is disabled! "
           type != SSH_SMSG_AUTH_TIS_CHALLENGE) {                                      "Reponse will be transmitted in clear text.");
         packet_disconnect("Protocol error: got %d in response "                          fprintf(stderr, "%s\n", challenge);
                           "to skey auth", type);                          fflush(stderr);
       }                          for (i = 0; i < options.number_of_password_prompts; i++) {
       if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) {                                  if (i != 0)
         debug("No challenge for skey authentication.");                                          error("Permission denied, please try again.");
       } else {                                  response = read_passphrase("Response: ", 0);
         char *challenge, *response;                                  packet_start(SSH_CMSG_AUTH_TIS_RESPONSE);
         challenge = packet_get_string(&payload_len);                                  packet_put_string(response, strlen(response));
         if (options.cipher == SSH_CIPHER_NONE)                                  memset(response, 0, strlen(response));
           log("WARNING: Encryption is disabled! "                                  xfree(response);
               "Reponse will be transmitted in clear text.");                                  packet_send();
         fprintf(stderr, "%s\n", challenge);                                  packet_write_wait();
         fflush(stderr);                                  type = packet_read(&payload_len);
         for (i = 0; i < options.number_of_password_prompts; i++) {                                  if (type == SSH_SMSG_SUCCESS)
           if (i != 0)                                          return;
             error("Permission denied, please try again.");                                  if (type != SSH_SMSG_FAILURE)
           response = read_passphrase("Response: ", 0);                                          packet_disconnect("Protocol error: got %d in response "
           packet_start(SSH_CMSG_AUTH_TIS_RESPONSE);                                                            "to skey auth", type);
           packet_put_string(response, strlen(response));                          }
           memset(response, 0, strlen(response));                  }
           xfree(response);  
           packet_send();  
           packet_write_wait();  
           type = packet_read(&payload_len);  
           if (type == SSH_SMSG_SUCCESS)  
             return;  
           if (type != SSH_SMSG_FAILURE)  
             packet_disconnect("Protocol error: got %d in response "  
                               "to skey auth", type);  
         }          }
       }          /* Try password authentication if the server supports it. */
     }          if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) &&
               options.password_authentication && !options.batch_mode) {
   /* Try password authentication if the server supports it. */                  char prompt[80];
   if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) &&                  snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ",
       options.password_authentication && !options.batch_mode)                           server_user, host);
     {                  debug("Doing password authentication.");
       char prompt[80];                  if (options.cipher == SSH_CIPHER_NONE)
       snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ",                          log("WARNING: Encryption is disabled! Password will be transmitted in clear text.");
         server_user, host);                  for (i = 0; i < options.number_of_password_prompts; i++) {
       debug("Doing password authentication.");                          if (i != 0)
       if (options.cipher == SSH_CIPHER_NONE)                                  error("Permission denied, please try again.");
         log("WARNING: Encryption is disabled! Password will be transmitted in clear text.");                          password = read_passphrase(prompt, 0);
       for (i = 0; i < options.number_of_password_prompts; i++) {                          packet_start(SSH_CMSG_AUTH_PASSWORD);
         if (i != 0)                          packet_put_string(password, strlen(password));
           error("Permission denied, please try again.");                          memset(password, 0, strlen(password));
         password = read_passphrase(prompt, 0);                          xfree(password);
         packet_start(SSH_CMSG_AUTH_PASSWORD);                          packet_send();
         packet_put_string(password, strlen(password));                          packet_write_wait();
         memset(password, 0, strlen(password));  
         xfree(password);  
         packet_send();  
         packet_write_wait();  
   
         type = packet_read(&payload_len);  
         if (type == SSH_SMSG_SUCCESS)  
           return; /* Successful connection. */  
         if (type != SSH_SMSG_FAILURE)  
           packet_disconnect("Protocol error: got %d in response to passwd auth", type);  
       }  
     }  
   
   /* All authentication methods have failed.  Exit with an error message. */                          type = packet_read(&payload_len);
   fatal("Permission denied.");                          if (type == SSH_SMSG_SUCCESS)
   /*NOTREACHED*/                                  return;
                           if (type != SSH_SMSG_FAILURE)
                                   packet_disconnect("Protocol error: got %d in response to passwd auth", type);
                   }
           }
           /* All authentication methods have failed.  Exit with an error message. */
           fatal("Permission denied.");
           /* NOTREACHED */
 }  }

Legend:
Removed from v.1.37  
changed lines
  Added in v.1.38