diff options
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 2851 |
1 files changed, 1421 insertions, 1430 deletions
diff --git a/sshconnect.c b/sshconnect.c index fba389d8..0657c37e 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -1,21 +1,14 @@ /* - -sshconnect.c - -Author: Tatu Ylonen <ylo@cs.hut.fi> - -Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - All rights reserved - -Created: Sat Mar 18 22:15:47 1995 ylo - -Code to connect to a remote host, and to perform the client side of the -login (authentication) dialog. - -*/ + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * Created: Sat Mar 18 22:15:47 1995 ylo + * Code to connect to a remote host, and to perform the client side of the + * login (authentication) dialog. + */ #include "includes.h" -RCSID("$Id: sshconnect.c,v 1.13 1999/11/21 02:23:53 damien Exp $"); +RCSID("$Id: sshconnect.c,v 1.14 1999/11/24 13:26:23 damien Exp $"); #ifdef HAVE_OPENSSL #include <openssl/bn.h> @@ -41,1497 +34,1495 @@ RCSID("$Id: sshconnect.c,v 1.13 1999/11/21 02:23:53 damien Exp $"); /* Session id for the current session. */ unsigned char session_id[16]; -/* Connect to the given ssh server using a proxy command. */ - +/* + * Connect to the given ssh server using a proxy command. + */ int ssh_proxy_connect(const char *host, int port, uid_t original_real_uid, const char *proxy_command) { - Buffer command; - const char *cp; - char *command_string; - int pin[2], pout[2]; - int pid; - char portstring[100]; - - /* Convert the port number into a string. */ - snprintf(portstring, sizeof portstring, "%d", port); - - /* Build the final command string in the buffer by making the appropriate - substitutions to the given proxy command. */ - buffer_init(&command); - for (cp = proxy_command; *cp; cp++) - { - if (cp[0] == '%' && cp[1] == '%') - { - buffer_append(&command, "%", 1); - 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 command; + const char *cp; + char *command_string; + int pin[2], pout[2]; + int pid; + char portstring[100]; + + /* Convert the port number into a string. */ + snprintf(portstring, sizeof portstring, "%d", port); + + /* Build the final command string in the buffer by making the + appropriate substitutions to the given proxy command. */ + buffer_init(&command); + for (cp = proxy_command; *cp; cp++) { + if (cp[0] == '%' && cp[1] == '%') { + buffer_append(&command, "%", 1); + 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); } - buffer_append(&command, cp, 1); - } - buffer_append(&command, "\0", 1); - - /* Get the final command string. */ - command_string = buffer_ptr(&command); - - /* Create pipes for communicating with the proxy. */ - if (pipe(pin) < 0 || pipe(pout) < 0) - fatal("Could not create pipes to communicate with the proxy: %.100s", - strerror(errno)); - - debug("Executing proxy command: %.500s", command_string); - - /* Fork and execute the proxy command. */ - if ((pid = fork()) == 0) - { - char *argv[10]; - - /* Child. Permanently give up superuser privileges. */ - permanently_set_uid(original_real_uid); - - /* Redirect stdin and stdout. */ - close(pin[1]); - if (pin[0] != 0) - { - if (dup2(pin[0], 0) < 0) - perror("dup2 stdin"); - close(pin[0]); + buffer_append(&command, "\0", 1); + + /* Get the final command string. */ + command_string = buffer_ptr(&command); + + /* Create pipes for communicating with the proxy. */ + if (pipe(pin) < 0 || pipe(pout) < 0) + fatal("Could not create pipes to communicate with the proxy: %.100s", + strerror(errno)); + + debug("Executing proxy command: %.500s", command_string); + + /* Fork and execute the proxy command. */ + if ((pid = fork()) == 0) { + char *argv[10]; + + /* Child. Permanently give up superuser privileges. */ + permanently_set_uid(original_real_uid); + + /* Redirect stdin and stdout. */ + close(pin[1]); + if (pin[0] != 0) { + if (dup2(pin[0], 0) < 0) + perror("dup2 stdin"); + 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]); - if (dup2(pout[1], 1) < 0) - perror("dup2 stdout"); - close(pout[1]); /* Cannot be 1 because pin allocated two descriptors. */ - - /* 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); - } - /* 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. */ - buffer_free(&command); - - /* Set the connection file descriptors. */ - packet_set_connection(pout[0], pin[1]); - - return 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. */ + buffer_free(&command); + + /* Set the connection file descriptors. */ + packet_set_connection(pout[0], pin[1]); -/* Creates a (possibly privileged) socket for use as the ssh connection. */ + return 1; +} -int ssh_create_socket(uid_t original_real_uid, int privileged) +/* + * Creates a (possibly privileged) socket for use as the ssh connection. + */ +int +ssh_create_socket(uid_t original_real_uid, int privileged) { - int sock; - - /* If we are running as root and want to connect to a privileged port, - bind our own socket to a privileged port. */ - if (privileged) - { - int p = IPPORT_RESERVED - 1; - - sock = rresvport(&p); - if (sock < 0) - fatal("rresvport: %.100s", strerror(errno)); - debug("Allocated local port %d.", p); - } - else - { - /* Just create an ordinary socket on arbitrary port. We use the - user's uid to create the socket. */ - temporarily_use_uid(original_real_uid); - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) - fatal("socket: %.100s", strerror(errno)); - restore_uid(); - } - return sock; + int sock; + + /* If we are running as root and want to connect to a privileged + port, bind our own socket to a privileged port. */ + if (privileged) { + int p = IPPORT_RESERVED - 1; + + sock = rresvport(&p); + if (sock < 0) + fatal("rresvport: %.100s", strerror(errno)); + debug("Allocated local port %d.", p); + } else { + /* Just create an ordinary socket on arbitrary port. We + use the user's uid to create the socket. */ + temporarily_use_uid(original_real_uid); + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + fatal("socket: %.100s", strerror(errno)); + restore_uid(); + } + return sock; } -/* 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, - a privileged port will be allocated to make the connection. - This requires super-user privileges if anonymous is false. - Connection_attempts specifies the maximum number of tries (one per - 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 - the daemon. */ - -int ssh_connect(const char *host, struct sockaddr_in *hostaddr, - int port, int connection_attempts, - int anonymous, uid_t original_real_uid, - const char *proxy_command) +/* + * 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, + * a privileged port will be allocated to make the connection. + * This requires super-user privileges if anonymous is false. + * Connection_attempts specifies the maximum number of tries (one per + * 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 + * the daemon. + */ +int +ssh_connect(const char *host, struct sockaddr_in * hostaddr, + int port, int connection_attempts, + int anonymous, uid_t original_real_uid, + const char *proxy_command) { - int sock = -1, attempt, i; - int on = 1; - struct servent *sp; - struct hostent *hp; - struct linger linger; - - debug("ssh_connect: getuid %d geteuid %d anon %d", - (int)getuid(), (int)geteuid(), anonymous); - - /* Get default port if port has not been set. */ - if (port == 0) - { - sp = getservbyname(SSH_SERVICE_NAME, "tcp"); - if (sp) - port = ntohs(sp->s_port); - else - 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); - - /* No proxy command. */ - - /* No host lookup made yet. */ - hp = NULL; - - /* Try to connect several times. On some machines, the first time will - sometimes fail. In general socket code appears to behave quite - magically on many machines. */ - for (attempt = 0; attempt < connection_attempts; attempt++) - { - if (attempt > 0) - debug("Trying again..."); - - /* Try to parse the host name as a numeric inet address. */ - memset(hostaddr, 0, sizeof(hostaddr)); - hostaddr->sin_family = AF_INET; - hostaddr->sin_port = htons(port); - hostaddr->sin_addr.s_addr = inet_addr(host); - if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) - { - /* Valid numeric IP address */ - debug("Connecting to %.100s port %d.", - 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. */ - shutdown(sock, SHUT_RDWR); - close(sock); + int sock = -1, attempt, i; + int on = 1; + struct servent *sp; + struct hostent *hp; + struct linger linger; + + debug("ssh_connect: getuid %d geteuid %d anon %d", + (int) getuid(), (int) geteuid(), anonymous); + + /* Get default port if port has not been set. */ + if (port == 0) { + sp = getservbyname(SSH_SERVICE_NAME, "tcp"); + if (sp) + port = ntohs(sp->s_port); + else + port = SSH_DEFAULT_PORT; } - 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 - sequence until the connection succeeds. */ - 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)); - - debug("Connecting to %.200s [%.100s] port %d.", - host, inet_ntoa(hostaddr->sin_addr), port); - - /* Create a socket for connecting. */ - 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 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; + /* If a proxy command is given, connect using it. */ + 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; + + /* Try to connect several times. On some machines, the first time + will sometimes fail. In general socket code appears to behave + quite magically on many machines. */ + for (attempt = 0; attempt < connection_attempts; attempt++) { + if (attempt > 0) + debug("Trying again..."); + + /* Try to parse the host name as a numeric inet address. */ + memset(hostaddr, 0, sizeof(hostaddr)); + hostaddr->sin_family = AF_INET; + hostaddr->sin_port = htons(port); + hostaddr->sin_addr.s_addr = inet_addr(host); + if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) { + /* Valid numeric IP address */ + debug("Connecting to %.100s port %d.", + 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. */ + 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); + + /* Loop through addresses for this host, and try + each one in sequence until the connection + succeeds. */ + 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)); + + debug("Connecting to %.200s [%.100s] port %d.", + host, inet_ntoa(hostaddr->sin_addr), port); + + /* Create a socket for connecting. */ + 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 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 - 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. */ - } - /* Sleep a moment before retrying. */ - sleep(1); - } - /* Return failure if we didn't get a successful connection. */ - if (attempt >= connection_attempts) - return 0; + /* Sleep a moment before retrying. */ + sleep(1); + } + /* Return failure if we didn't get a successful connection. */ + if (attempt >= connection_attempts) + return 0; - debug("Connection established."); + 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 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 the connection. */ - packet_set_connection(sock, sock); + /* Set the connection. */ + packet_set_connection(sock, sock); - return 1; + return 1; } -/* Checks if the user has an authentication agent, and if so, tries to - authenticate using the agent. */ - +/* + * Checks if the user has an authentication agent, and if so, tries to + * authenticate using the agent. + */ int try_agent_authentication() { - int status, type; - char *comment; - AuthenticationConnection *auth; - unsigned char response[16]; - unsigned int i; - 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. */ - debug("Trying RSA authentication via agent with '%.100s'", comment); - xfree(comment); - - /* 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."); - - /* 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; - } + int status, type; + char *comment; + AuthenticationConnection *auth; + unsigned char response[16]; + unsigned int i; + 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. */ + debug("Trying RSA authentication via agent with '%.100s'", comment); + xfree(comment); + + /* 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); - /* Otherwise it should return failure. */ - if (type != SSH_SMSG_FAILURE) - packet_disconnect("Protocol error waiting RSA auth response: %d", - type); - } + packet_get_bignum(challenge, &clen); - BN_clear_free(e); - BN_clear_free(n); - BN_clear_free(challenge); + packet_integrity_check(plen, clen, type); - debug("RSA authentication using agent refused."); - return 0; -} + 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); -/* Computes the proper response to a RSA challenge, and sends the response to - the server. */ + debug("RSA authentication using agent refused."); + return 0; +} +/* + * Computes the proper response to a RSA challenge, and sends the response to + * the server. + */ void -respond_to_rsa_challenge(BIGNUM *challenge, RSA *prv) +respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv) { - unsigned char buf[32], response[16]; - MD5_CTX md; - int i, len; - - /* Decrypt the challenge using the private key. */ - rsa_private_decrypt(challenge, challenge, prv); - - /* Compute the response. */ - /* The response is MD5 of decrypted challenge plus session id. */ - len = BN_num_bytes(challenge); - if (len <= 0 || len > sizeof(buf)) - packet_disconnect("respond_to_rsa_challenge: bad challenge length %d", - len); - - memset(buf, 0, sizeof(buf)); - BN_bn2bin(challenge, buf + sizeof(buf) - len); - MD5_Init(&md); - MD5_Update(&md, buf, 32); - MD5_Update(&md, session_id, 16); - MD5_Final(response, &md); - - debug("Sending response to host key RSA challenge."); - - /* Send the response 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(); - - memset(buf, 0, sizeof(buf)); - memset(response, 0, sizeof(response)); - memset(&md, 0, sizeof(md)); -} + unsigned char buf[32], response[16]; + MD5_CTX md; + int i, len; + + /* Decrypt the challenge using the private key. */ + rsa_private_decrypt(challenge, challenge, prv); + + /* Compute the response. */ + /* The response is MD5 of decrypted challenge plus session id. */ + len = BN_num_bytes(challenge); + if (len <= 0 || len > sizeof(buf)) + packet_disconnect("respond_to_rsa_challenge: bad challenge length %d", + len); + + memset(buf, 0, sizeof(buf)); + BN_bn2bin(challenge, buf + sizeof(buf) - len); + MD5_Init(&md); + MD5_Update(&md, buf, 32); + MD5_Update(&md, session_id, 16); + MD5_Final(response, &md); + + debug("Sending response to host key RSA challenge."); + + /* Send the response 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(); -/* Checks if the user has authentication file, and if so, tries to authenticate - the user using it. */ + 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 + * the user using it. + */ int -try_rsa_authentication(struct passwd *pw, const char *authfile) +try_rsa_authentication(struct passwd * pw, const char *authfile) { - extern Options options; - BIGNUM *challenge; - RSA *private_key; - RSA *public_key; - char *passphrase, *comment; - int type, i; - int plen, clen; - - /* Try to load identification for the authentication key. */ - public_key = RSA_new(); - if (!load_public_key(authfile, public_key, &comment)) { - RSA_free(public_key); - return 0; /* Could not load it. Fail. */ - } - - 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(); - - /* We no longer need the public key. */ - RSA_free(public_key); - - /* Wait for server's response. */ - type = packet_read(&plen); - - /* The server responds with failure if it doesn\'t like our key or doesn\'t - support RSA authentication. */ - if (type == SSH_SMSG_FAILURE) - { - debug("Server refused our key."); - xfree(comment); - return 0; /* Server refuses to authenticate with 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); - - /* Get the challenge from the packet. */ - challenge = BN_new(); - packet_get_bignum(challenge, &clen); - - packet_integrity_check(plen, clen, type); - - 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]; - /* 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(""); + extern Options options; + BIGNUM *challenge; + RSA *private_key; + RSA *public_key; + char *passphrase, *comment; + int type, i; + int plen, clen; + + /* Try to load identification for the authentication key. */ + public_key = RSA_new(); + if (!load_public_key(authfile, public_key, &comment)) { + RSA_free(public_key); + return 0; /* Could not load it. Fail. */ } - - /* 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. */ - packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); - for (i = 0; i < 16; i++) - packet_put_char(0); - packet_send(); - packet_write_wait(); - - /* Expect the server to reject it... */ - packet_read_expect(&plen, SSH_SMSG_FAILURE); - xfree(comment); - return 0; + 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(); + + /* We no longer need the public key. */ + RSA_free(public_key); + + /* Wait for server's response. */ + type = packet_read(&plen); + + /* The server responds with failure if it doesn\'t like our key or + doesn\'t support RSA authentication. */ + if (type == SSH_SMSG_FAILURE) { + debug("Server refused our key."); + xfree(comment); + return 0; /* Server refuses to authenticate with + 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); + + /* Get the challenge from the packet. */ + challenge = BN_new(); + packet_get_bignum(challenge, &clen); + + packet_integrity_check(plen, clen, type); + + 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(""); + } - /* Destroy the passphrase. */ - memset(passphrase, 0, strlen(passphrase)); - xfree(passphrase); - } - - /* We no longer need the comment. */ - xfree(comment); - - /* Compute and send a response to the challenge. */ - respond_to_rsa_challenge(challenge, private_key); - - /* Destroy the private key. */ - RSA_free(private_key); - - /* We no longer need the challenge. */ - BN_clear_free(challenge); - - /* Wait for response from the server. */ - type = packet_read(&plen); - if (type == SSH_SMSG_SUCCESS) - { - debug("RSA authentication accepted by server."); - return 1; - } - if (type != SSH_SMSG_FAILURE) - packet_disconnect("Protocol error waiting RSA auth response: %d", type); - debug("RSA authentication refused."); - return 0; -} + /* 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. */ + packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); + for (i = 0; i < 16; i++) + packet_put_char(0); + packet_send(); + packet_write_wait(); + + /* Expect the server to reject it... */ + packet_read_expect(&plen, SSH_SMSG_FAILURE); + xfree(comment); + return 0; + } + /* Destroy the passphrase. */ + memset(passphrase, 0, strlen(passphrase)); + xfree(passphrase); + } + /* We no longer need the comment. */ + xfree(comment); + + /* Compute and send a response to the challenge. */ + respond_to_rsa_challenge(challenge, private_key); + + /* Destroy the private key. */ |