version 1.39, 1999/11/24 00:26:03 |
version 1.40, 1999/11/24 19:53:53 |
|
|
{ |
{ |
int 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 we are running as root and want to connect to a privileged |
|
* port, bind our own socket to a privileged port. |
|
*/ |
if (privileged) { |
if (privileged) { |
int p = IPPORT_RESERVED - 1; |
int p = IPPORT_RESERVED - 1; |
|
|
|
|
!anonymous && geteuid() == 0 && |
!anonymous && geteuid() == 0 && |
port < IPPORT_RESERVED); |
port < IPPORT_RESERVED); |
|
|
/* Connect to the host. We use the user's uid in |
/* |
the hope that it will help with the problems of |
* Connect to the host. We use the user's uid in the |
tcp_wrappers showing the remote uid as root. */ |
* hope that it will help with the problems of |
|
* tcp_wrappers showing the remote uid as root. |
|
*/ |
temporarily_use_uid(original_real_uid); |
temporarily_use_uid(original_real_uid); |
if (connect(sock, (struct sockaddr *) hostaddr, sizeof(*hostaddr)) |
if (connect(sock, (struct sockaddr *) hostaddr, sizeof(*hostaddr)) |
>= 0) { |
>= 0) { |
|
|
!anonymous && geteuid() == 0 && |
!anonymous && geteuid() == 0 && |
port < IPPORT_RESERVED); |
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. */ |
* 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); |
temporarily_use_uid(original_real_uid); |
if (connect(sock, (struct sockaddr *) hostaddr, |
if (connect(sock, (struct sockaddr *) hostaddr, |
sizeof(*hostaddr)) >= 0) { |
sizeof(*hostaddr)) >= 0) { |
|
|
debug("connect: %.100s", strerror(errno)); |
debug("connect: %.100s", strerror(errno)); |
restore_uid(); |
restore_uid(); |
|
|
/* Close the failed socket; there appear to be some problems when |
/* |
reusing a socket for which connect() has already returned an error. */ |
* 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); |
shutdown(sock, SHUT_RDWR); |
close(sock); |
close(sock); |
} |
} |
|
|
|
|
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. */ |
* Set socket options. We would like the socket to disappear as soon |
/* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, |
* as it has been closed for whatever reason. |
sizeof(on)); */ |
*/ |
|
/* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ |
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *) &on, sizeof(on)); |
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *) &on, sizeof(on)); |
linger.l_onoff = 1; |
linger.l_onoff = 1; |
linger.l_linger = 5; |
linger.l_linger = 5; |
|
|
/* 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 like our key or |
/* |
doesn\'t support RSA authentication. */ |
* The server responds with failure if it doesn\'t like our key or |
|
* 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); |
|
|
debug("Received RSA challenge from server."); |
debug("Received RSA challenge from server."); |
|
|
private_key = RSA_new(); |
private_key = RSA_new(); |
/* Load the private key. Try first with empty passphrase; if it |
/* |
fails, ask for a passphrase. */ |
* Load the private key. Try first with empty passphrase; if it |
|
* fails, ask for a passphrase. |
|
*/ |
if (!load_private_key(authfile, "", private_key, NULL)) { |
if (!load_private_key(authfile, "", private_key, NULL)) { |
char buf[300]; |
char buf[300]; |
snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ", |
snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ", |
|
|
|
|
packet_integrity_check(plen, 4 + auth.length, type); |
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, |
* If his response isn't properly encrypted with the session |
he's bogus. Bail out. */ |
* key, and the decrypted checksum fails to match, he's |
|
* bogus. Bail out. |
|
*/ |
r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session, |
r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session, |
&foreign, &local, &msg_data); |
&foreign, &local, &msg_data); |
if (r != KSUCCESS) { |
if (r != KSUCCESS) { |
|
|
} |
} |
buf[sizeof(buf) - 1] = 0; |
buf[sizeof(buf) - 1] = 0; |
|
|
/* Check that the versions match. In future this might accept |
/* |
several versions and set appropriate flags to handle them. */ |
* 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, |
if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, |
remote_version) != 3) |
remote_version) != 3) |
fatal("Bad remote protocol version identification: '%.100s'", buf); |
fatal("Bad remote protocol version identification: '%.100s'", buf); |
|
|
} |
} |
} |
} |
#if 0 |
#if 0 |
/* Removed for now, to permit compatibility with latter versions. |
/* |
The server will reject our version and disconnect if it doesn't |
* Removed for now, to permit compatibility with latter versions. The |
support it. */ |
* server will reject our version and disconnect if it doesn't |
|
* support it. |
|
*/ |
if (remote_major != PROTOCOL_MAJOR) |
if (remote_major != PROTOCOL_MAJOR) |
fatal("Protocol major versions differ: %d vs. %d", |
fatal("Protocol major versions differ: %d vs. %d", |
PROTOCOL_MAJOR, remote_major); |
PROTOCOL_MAJOR, remote_major); |
|
|
protocol_flags = packet_get_int(); |
protocol_flags = packet_get_int(); |
packet_set_protocol_flags(protocol_flags); |
packet_set_protocol_flags(protocol_flags); |
|
|
/* Get supported cipher types. */ |
|
supported_ciphers = packet_get_int(); |
supported_ciphers = packet_get_int(); |
|
|
/* Get supported authentication types. */ |
|
supported_authentications = packet_get_int(); |
supported_authentications = packet_get_int(); |
|
|
debug("Received server public key (%d bits) and host key (%d bits).", |
debug("Received server public key (%d bits) and host key (%d bits).", |
|
|
8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, |
8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, |
SSH_SMSG_PUBLIC_KEY); |
SSH_SMSG_PUBLIC_KEY); |
|
|
/* Compute the session id. */ |
|
compute_session_id(session_id, check_bytes, host_key->n, public_key->n); |
compute_session_id(session_id, check_bytes, host_key->n, public_key->n); |
|
|
/* Check if the host key is present in the user\'s list of known |
/* |
hosts or in the systemwide list. */ |
* Check if the host key is present in the user\'s list of known |
|
* hosts or in the systemwide list. |
|
*/ |
host_status = check_host_in_hostfile(options.user_hostfile, host, |
host_status = check_host_in_hostfile(options.user_hostfile, host, |
host_key->e, host_key->n, |
host_key->e, host_key->n, |
file_key->e, file_key->n); |
file_key->e, file_key->n); |
|
|
host_status = check_host_in_hostfile(options.system_hostfile, host, |
host_status = check_host_in_hostfile(options.system_hostfile, host, |
host_key->e, host_key->n, |
host_key->e, host_key->n, |
file_key->e, file_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 |
* Force accepting of the host key for localhost and 127.0.0.1. The |
multiple machines, localhost will refer to a different machine |
* problem is that if the home directory is NFS-mounted to multiple |
in each of them, and the user will get bogus HOST_CHANGED |
* machines, localhost will refer to a different machine in each of |
warnings. This essentially disables host authentication for |
* them, and the user will get bogus HOST_CHANGED warnings. This |
localhost; however, this is probably not a real problem. */ |
* essentially disables host authentication for localhost; however, |
|
* this is probably not a real problem. |
|
*/ |
if (local) { |
if (local) { |
debug("Forcing accepting of host key for localhost."); |
debug("Forcing accepting of host key for localhost."); |
host_status = HOST_OK; |
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 */ |
* 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)) { |
if (options.check_host_ip && !local && strcmp(host, ip)) { |
RSA *ip_key = RSA_new(); |
RSA *ip_key = RSA_new(); |
ip_key->n = BN_new(); |
ip_key->n = BN_new(); |
|
|
error("Add correct host key in %.100s to get rid of this message.", |
error("Add correct host key in %.100s to get rid of this message.", |
options.user_hostfile); |
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 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) |
if (options.strict_host_key_checking) |
fatal("Host key for %.200s has changed and you have requested strict checking.", host); |
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 strict host key checking has not been requested, allow |
|
* the connection but without password authentication or |
|
* agent forwarding. |
|
*/ |
if (options.password_authentication) { |
if (options.password_authentication) { |
error("Password authentication is disabled to avoid trojan horses."); |
error("Password authentication is disabled to avoid trojan horses."); |
options.password_authentication = 0; |
options.password_authentication = 0; |
|
|
error("Agent forwarding is disabled to avoid trojan horses."); |
error("Agent forwarding is disabled to avoid trojan horses."); |
options.forward_agent = 0; |
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 |
* XXX Should permit the user to change to use the new id. |
identifying sentence, tell that the host identifies |
* This could be done by converting the host key to an |
itself by that sentence, and ask the user if he/she |
* identifying sentence, tell that the host identifies itself |
whishes to accept the authentication. */ |
* by that sentence, and ask the user if he/she whishes to |
|
* accept the authentication. |
|
*/ |
break; |
break; |
} |
} |
|
|
|
|
/* Generate a session key. */ |
/* Generate a session key. */ |
arc4random_stir(); |
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 |
* Generate an encryption key for the session. The key is a 256 bit |
significant 8 bits being the first byte of the key. */ |
* 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++) { |
for (i = 0; i < 32; i++) { |
if (i % 4 == 0) |
if (i % 4 == 0) |
rand = arc4random(); |
rand = arc4random(); |
|
|
rand >>= 8; |
rand >>= 8; |
} |
} |
|
|
/* According to the protocol spec, the first byte of the session |
/* |
key is the highest byte of the integer. The session key is |
* According to the protocol spec, the first byte of the session key |
xored with the first 16 bytes of the session id. */ |
* is the highest byte of the integer. The session key is xored with |
|
* the first 16 bytes of the session id. |
|
*/ |
key = BN_new(); |
key = BN_new(); |
BN_set_word(key, 0); |
BN_set_word(key, 0); |
for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { |
for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { |
|
|
BN_add_word(key, session_key[i]); |
BN_add_word(key, session_key[i]); |
} |
} |
|
|
/* Encrypt the integer using the public key and host key of the |
/* |
server (key with smaller modulus first). */ |
* Encrypt the integer using the public key and host key of the |
|
* server (key with smaller modulus first). |
|
*/ |
if (BN_cmp(public_key->n, host_key->n) < 0) { |
if (BN_cmp(public_key->n, host_key->n) < 0) { |
/* Public key has smaller modulus. */ |
/* Public key has smaller modulus. */ |
if (BN_num_bits(host_key->n) < |
if (BN_num_bits(host_key->n) < |
|
|
/* 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 be received in encrypted form. */ |
* Expect a success message from the server. Note that this 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."); |
|
|
packet_send(); |
packet_send(); |
packet_write_wait(); |
packet_write_wait(); |
|
|
/* The server should respond with success if no authentication is |
/* |
needed (the user has no password). Otherwise the server |
* The server should respond with success if no authentication is |
responds with failure. */ |
* needed (the user has no password). Otherwise the server responds |
|
* with failure. |
|
*/ |
type = packet_read(&payload_len); |
type = packet_read(&payload_len); |
|
|
/* check whether the connection was accepted without authentication. */ |
/* check whether the connection was accepted without authentication. */ |
|
|
} |
} |
#endif /* KRB4 */ |
#endif /* KRB4 */ |
|
|
/* Use rhosts authentication if running in privileged socket and |
/* |
we do not wish to remain anonymous. */ |
* Use rhosts authentication if running in privileged socket and we |
|
* do not wish to remain anonymous. |
|
*/ |
if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && |
if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && |
options.rhosts_authentication) { |
options.rhosts_authentication) { |
debug("Trying rhosts authentication."); |
debug("Trying rhosts authentication."); |
|
|
packet_disconnect("Protocol error: got %d in response to rhosts auth", |
packet_disconnect("Protocol error: got %d in response to rhosts auth", |
type); |
type); |
} |
} |
/* Try .rhosts or /etc/hosts.equiv authentication with RSA host |
/* |
authentication. */ |
* Try .rhosts or /etc/hosts.equiv authentication with RSA host |
|
* authentication. |
|
*/ |
if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && |
if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && |
options.rhosts_rsa_authentication && host_key_valid) { |
options.rhosts_rsa_authentication && host_key_valid) { |
if (try_rhosts_rsa_authentication(local_user, own_host_key)) |
if (try_rhosts_rsa_authentication(local_user, own_host_key)) |
|
|
/* Try RSA authentication if the server supports it. */ |
/* Try RSA authentication if the server supports it. */ |
if ((supported_authentications & (1 << SSH_AUTH_RSA)) && |
if ((supported_authentications & (1 << SSH_AUTH_RSA)) && |
options.rsa_authentication) { |
options.rsa_authentication) { |
/* Try RSA authentication using the authentication agent. |
/* |
The agent is tried first because no passphrase is |
* Try RSA authentication using the authentication agent. The |
needed for it, whereas identity files may require |
* agent is tried first because no passphrase is needed for |
passphrases. */ |
* it, whereas identity files may require passphrases. |
|
*/ |
if (try_agent_authentication()) |
if (try_agent_authentication()) |
return; |
return; |
|
|