- extern Options options;
- int i, type;
- char *password;
- struct passwd *pw;
- BIGNUM *key;
- RSA *host_key, *file_key;
- RSA *public_key;
- int bits, rbits;
- unsigned char session_key[SSH_SESSION_KEY_LENGTH];
- const char *server_user, *local_user;
- char *cp, *host, *ip = NULL;
- unsigned char check_bytes[8];
- unsigned int supported_ciphers, supported_authentications, protocol_flags;
- HostStatus host_status;
- HostStatus ip_status;
- int host_ip_differ = 0;
- int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
- int payload_len, clen, sum_len = 0;
- u_int32_t rand = 0;
-
- if (options.check_host_ip)
- ip = xstrdup(inet_ntoa(hostaddr->sin_addr));
-
- /* Convert the user-supplied hostname into all lowercase. */
- host = xstrdup(orighost);
- for (cp = host; *cp; cp++)
- if (isupper(*cp))
- *cp = tolower(*cp);
-
- /* Exchange protocol version identification strings with the server. */
- ssh_exchange_identification();
-
- /* Put the connection into non-blocking mode. */
- packet_set_nonblocking();
-
- /* Get local user name. Use it as server user if no user name
- was given. */
- pw = getpwuid(original_real_uid);
- if (!pw)
- fatal("User id %d not found from user database.", original_real_uid);
- local_user = xstrdup(pw->pw_name);
- server_user = options.user ? options.user : local_user;
-
- debug("Waiting for server public key.");
-
- /* Wait for a public key packet from the server. */
- packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY);
-
- /* Get check bytes from the packet. */
- for (i = 0; i < 8; i++)
- check_bytes[i] = packet_get_char();
-
- /* Get the public key. */
- public_key = RSA_new();
- bits = packet_get_int(); /* bits */
- public_key->e = BN_new();
- packet_get_bignum(public_key->e, &clen);
- sum_len += clen;
- public_key->n = BN_new();
- packet_get_bignum(public_key->n, &clen);
- sum_len += clen;
-
- rbits = BN_num_bits(public_key->n);
- if (bits != rbits) {
- log("Warning: Server lies about size of server public key: "
- "actual size is %d bits vs. announced %d.", rbits, bits);
- 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;
-
- rbits = BN_num_bits(host_key->n);
- if (bits != rbits) {
- log("Warning: Server lies about size of server host key: "
- "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
- * so that we can compare it with the key for the IP
- * address. */
- file_key = RSA_new();
- file_key->n = BN_new();
- file_key->e = BN_new();
-
- /* Get protocol flags. */
- protocol_flags = packet_get_int();
- packet_set_protocol_flags(protocol_flags);
-
- /* Get supported cipher types. */
- supported_ciphers = packet_get_int();
-
- /* Get supported authentication types. */
- supported_authentications = packet_get_int();
-
- debug("Received server public key (%d bits) and host key (%d bits).",
- BN_num_bits(public_key->n), BN_num_bits(host_key->n));
-
- packet_integrity_check(payload_len,
- 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4,
- SSH_SMSG_PUBLIC_KEY);
-
- /* Compute the session id. */
- 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. */
- 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);
-
- if (ip_status == HOST_NEW)
- 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;
-
- RSA_free(ip_key);
- } else
- ip_status = host_status;
-
- RSA_free(file_key);
-
- switch (host_status) {
- case HOST_OK:
- /* The host is known and the key matches. */
- debug("Host '%.200s' is known and matches the host key.", host);
- if (options.check_host_ip) {
- if (ip_status == HOST_NEW) {
- if (!add_host_to_hostfile(options.user_hostfile, ip,
- 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:
- {
- 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)
- 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
- the highest byte of the integer. The session key is xored with the
- 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
- (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 (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default))
- options.cipher = ssh_cipher_default;
- else {
- debug("Cipher %s not supported, using %.100s instead.",
- cipher_name(ssh_cipher_default),
- cipher_name(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));
-
- 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 check bytes back to the server. */
- for (i = 0; i < 8; i++)
- packet_put_char(check_bytes[i]);
-
- /* Send the encrypted encryption key. */
- packet_put_bignum(key);
-
- /* Send protocol flags. */
- packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
-
- /* Send the packet now. */
- packet_send();
- packet_write_wait();
-
- /* Destroy the session key integer and the public keys since we no longer
- 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);
-
- /* We will no longer need the session key here. Destroy any extra copies. */
- memset(session_key, 0, sizeof(session_key));
-
- /* 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);
-
- debug("Received encrypted confirmation.");
-
- /* Send the name of the user to log in as on the server. */
- packet_start(SSH_CMSG_USER);
- packet_put_string(server_user, strlen(server_user));
- packet_send();
- packet_write_wait();
-
- /* The server should respond with success if no authentication is needed
- (the user has no password). Otherwise the server responds with
- failure. */
- 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. */
- 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 */
-
-#ifdef KRB4
- if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) &&
- options.kerberos_authentication)
- {
- debug("Trying Kerberos authentication.");
- if (try_kerberos_authentication()) {
- /* The server should respond with success or failure. */
- 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 Kerberos auth", type);
- }
- }
-#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. */
- 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 rhosts auth",
- 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; /* Successful authentication. */
- }
-
- /* 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; /* Successful connection. */
-
- /* Try RSA authentication for each identity. */
- for (i = 0; i < options.num_identity_files; i++)
- if (try_rsa_authentication(pw, options.identity_files[i]))
- return; /* Successful connection. */
- }
-
- /* Try password authentication if the server supports it. */
- if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) &&
- options.password_authentication && !options.batch_mode)
- {
- char prompt[80];
- snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ",
- server_user, host);
- debug("Doing password authentication.");
- if (options.cipher == SSH_CIPHER_NONE)
- log("WARNING: Encryption is disabled! Password will be transmitted in clear text.");
- for (i = 0; i < options.number_of_password_prompts; i++) {
- if (i != 0)
- error("Permission denied, please try again.");
- password = read_passphrase(prompt, 0);
- packet_start(SSH_CMSG_AUTH_PASSWORD);
- packet_put_string(password, strlen(password));
- 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. */
- fatal("Permission denied.");
- /*NOTREACHED*/