void compute_session_id(unsigned char session_id[16], unsigned char cookie[8], unsigned int host_key_bits, MP_INT * host_key_n, unsigned int session_key_bits, MP_INT * session_key_n) { unsigned int bytes = (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8 + 8; unsigned char *buf = xmalloc(bytes); struct MD5Context md; mp_linearize_msb_first(buf, (host_key_bits + 7) / 8, host_key_n); mp_linearize_msb_first(buf + (host_key_bits + 7) / 8, (session_key_bits + 7) / 8, session_key_n); memcpy(buf + (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8, cookie, 8); MD5Init(&md); MD5Update(&md, buf, bytes); MD5Final(session_id, &md); xfree(buf); }
void do_connection(int privileged_port) { int i; MP_INT session_key_int; unsigned char session_key[SSH_SESSION_KEY_LENGTH]; unsigned char check_bytes[8]; char *user; unsigned int cipher_type, auth_mask, protocol_flags; /* Generate check bytes that the client must send back in the user packet in order for it to be accepted; this is used to defy ip spoofing attacks. Note that this only works against somebody doing IP spoofing from a remote machine; any machine on the local network can still see outgoing packets and catch the random cookie. This only affects rhosts authentication, and this is one of the reasons why it is inherently insecure. */ for (i = 0; i < 8; i++) check_bytes[i] = random_get_byte(&sensitive_data.random_state); /* Send our public key. We include in the packet 64 bits of random data that must be matched in the reply in order to prevent IP spoofing. */ packet_start(SSH_SMSG_PUBLIC_KEY); for (i = 0; i < 8; i++) packet_put_char(check_bytes[i]); /* Store our public server RSA key. */ packet_put_int(public_key.bits); packet_put_mp_int(&public_key.e); packet_put_mp_int(&public_key.n); /* Store our public host RSA key. */ packet_put_int(sensitive_data.host_key.bits); packet_put_mp_int(&sensitive_data.host_key.e); packet_put_mp_int(&sensitive_data.host_key.n); /* Put protocol flags. */ packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); /* Declare which ciphers we support. */ packet_put_int(cipher_mask()); /* Declare supported authentication types. */ auth_mask = 0; if (options.rhosts_authentication) auth_mask |= 1 << SSH_AUTH_RHOSTS; if (options.rhosts_rsa_authentication) auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; if (options.rsa_authentication) auth_mask |= 1 << SSH_AUTH_RSA; if (options.password_authentication) auth_mask |= 1 << SSH_AUTH_PASSWORD; packet_put_int(auth_mask); /* Send the packet and wait for it to be sent. */ packet_send(); packet_write_wait(); debug("Sent %d bit public key and %d bit host key.", public_key.bits, sensitive_data.host_key.bits); /* Read clients reply (cipher type and session key). */ packet_read_expect(SSH_CMSG_SESSION_KEY); /* Get cipher type. */ cipher_type = packet_get_char(); /* Get check bytes from the packet. These must match those we sent earlier with the public key packet. */ for (i = 0; i < 8; i++) if (check_bytes[i] != packet_get_char()) packet_disconnect("IP Spoofing check bytes do not match."); debug("Encryption type: %.200s", cipher_name(cipher_type)); /* Get the encrypted integer. */ mpz_init(&session_key_int); packet_get_mp_int(&session_key_int); /* Get protocol flags. */ protocol_flags = packet_get_int(); packet_set_protocol_flags(protocol_flags); /* Decrypt it using our private server key and private host key (key with larger modulus first). */ if (mpz_cmp(&sensitive_data.private_key.n, &sensitive_data.host_key.n) > 0) { /* Private key has bigger modulus. */ assert(sensitive_data.private_key.bits >= sensitive_data.host_key.bits + SSH_KEY_BITS_RESERVED); rsa_private_decrypt(&session_key_int, &session_key_int, &sensitive_data.private_key); rsa_private_decrypt(&session_key_int, &session_key_int, &sensitive_data.host_key); } else { /* Host key has bigger modulus (or they are equal). */ assert(sensitive_data.host_key.bits >= sensitive_data.private_key.bits + SSH_KEY_BITS_RESERVED); rsa_private_decrypt(&session_key_int, &session_key_int, &sensitive_data.host_key); rsa_private_decrypt(&session_key_int, &session_key_int, &sensitive_data.private_key); } /* Compute session id for this session. */ compute_session_id(session_id, check_bytes, sensitive_data.host_key.bits, &sensitive_data.host_key.n, sensitive_data.private_key.bits, &sensitive_data.private_key.n); /* Extract session key from the decrypted integer. The key is in the least significant 256 bits of the integer; the first byte of the key is in the highest bits. */ mp_linearize_msb_first(session_key, sizeof(session_key), &session_key_int); /* Xor the first 16 bytes of the session key with the session id. */ for (i = 0; i < 16; i++) session_key[i] ^= session_id[i]; /* Destroy the decrypted integer. It is no longer needed. */ mpz_clear(&session_key_int); /* Set the session key. From this on all communications will be encrypted. */ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type, 0); /* Destroy our copy of the session key. It is no longer needed. */ memset(session_key, 0, sizeof(session_key)); debug("Received session key; encryption turned on."); /* Send an acknowledgement packet. Note that this packet is sent encrypted. */ packet_start(SSH_SMSG_SUCCESS); packet_send(); packet_write_wait(); /* Get the name of the user that we wish to log in as. */ packet_read_expect(SSH_CMSG_USER); /* Get the user name. */ user = packet_get_string(NULL); /* Destroy the private and public keys. They will no longer be needed. */ rsa_clear_public_key(&public_key); rsa_clear_private_key(&sensitive_data.private_key); rsa_clear_private_key(&sensitive_data.host_key); /* Do the authentication. */ do_authentication(user, privileged_port, cipher_type); }