unsigned char session_id[16];
extern Options options;
+extern char *__progname;
/*
* Connect to the given ssh server using a proxy command.
char *command_string;
int pin[2], pout[2];
int pid;
- char portstring[100];
+ char strport[NI_MAXSERV];
/* Convert the port number into a string. */
- snprintf(portstring, sizeof portstring, "%hu", port);
+ snprintf(strport, sizeof strport, "%hu", port);
/* Build the final command string in the buffer by making the
appropriate substitutions to the given proxy command. */
continue;
}
if (cp[0] == '%' && cp[1] == 'p') {
- buffer_append(&command, portstring, strlen(portstring));
+ buffer_append(&command, strport, strlen(strport));
cp++;
continue;
}
* Creates a (possibly privileged) socket for use as the ssh connection.
*/
int
-ssh_create_socket(uid_t original_real_uid, int privileged)
+ssh_create_socket(uid_t original_real_uid, int privileged, int family)
{
int sock;
*/
if (privileged) {
int p = IPPORT_RESERVED - 1;
-
- sock = rresvport(&p);
+ sock = rresvport_af(&p, family);
if (sock < 0)
- fatal("rresvport: %.100s", strerror(errno));
+ fatal("rresvport: af=%d %.100s", family, 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. */
+ /*
+ * 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);
+ sock = socket(family, SOCK_STREAM, 0);
if (sock < 0)
- fatal("socket: %.100s", strerror(errno));
+ error("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,
+ * Opens a TCP/IP connection to the remote server on the given host.
+ * The address of the remote host will be returned in hostaddr.
+ * 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
* the daemon.
*/
int
-ssh_connect(const char *host, struct sockaddr_in * hostaddr,
+ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
u_short port, int connection_attempts,
int anonymous, uid_t original_real_uid,
const char *proxy_command)
{
- int sock = -1, attempt, i;
- int on = 1;
+ int sock = -1, attempt;
struct servent *sp;
- struct hostent *hp;
+ struct addrinfo hints, *ai, *aitop;
+ char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+ int gaierr;
struct linger linger;
debug("ssh_connect: getuid %d geteuid %d anon %d",
/* No proxy command. */
- /* No host lookup made yet. */
- hp = NULL;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = IPv4or6;
+ hints.ai_socktype = SOCK_STREAM;
+ snprintf(strport, sizeof strport, "%d", port);
+ if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
+ fatal("%s: %.100s: %s", __progname, host,
+ gai_strerror(gaierr));
- /* 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. */
+ /*
+ * 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.
+ /* Loop through addresses for this host, and try each one in
+ sequence until the connection succeeds. */
+ for (ai = aitop; ai; ai = ai->ai_next) {
+ if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+ continue;
+ if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
+ ntop, sizeof(ntop), strport, sizeof(strport),
+ NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
+ error("ssh_connect: getnameinfo failed");
+ continue;
+ }
+ debug("Connecting to %.200s [%.100s] port %s.",
+ host, ntop, strport);
+
+ /* Create a socket for connecting. */
+ sock = ssh_create_socket(original_real_uid,
+ !anonymous && geteuid() == 0 && port < IPPORT_RESERVED,
+ ai->ai_family);
+ if (sock < 0)
+ continue;
+
+ /* 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 connect. */
+ if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) {
+ /* Successful connection. */
+ memcpy(hostaddr, ai->ai_addr, sizeof(*(ai->ai_addr)));
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;
- }
+ } else {
debug("connect: %.100s", strerror(errno));
restore_uid();
-
/*
* Close the failed socket; there appear to
* be some problems when reusing a socket for
shutdown(sock, SHUT_RDWR);
close(sock);
}
- if (hp->h_addr_list[i])
- break; /* Successful connection. */
}
+ if (ai)
+ break; /* Successful connection. */
/* Sleep a moment before retrying. */
sleep(1);
}
+
+ freeaddrinfo(aitop);
+
/* Return failure if we didn't get a successful connection. */
if (attempt >= connection_attempts)
return 0;
* 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));
if (!load_private_key(authfile, "", private_key, NULL)) {
char buf[300];
snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ",
- comment);
+ comment);
if (!options.batch_mode)
passphrase = read_passphrase(buf, 0);
else {
/* We speak 1.3, too. */
if (remote_major == 1 && remote_minor == 3) {
enable_compat13();
- if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) {
+ if (options.forward_agent && strcmp(remote_version, "OpenSSH-1.1") != 0) {
log("Agent forwarding disabled, remote version '%s' is not compatible.",
remote_version);
options.forward_agent = 0;
/* Send our own protocol version identification. */
snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
- PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
- if (write(connection_out, buf, strlen(buf)) != strlen(buf))
+ PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
+ if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf))
fatal("write: %.100s", strerror(errno));
}
}
/*
- * Starts a dialog with the server, and authenticates the current user on the
- * server. This does not need any extra privileges. The basic connection
- * 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
- * be used. Anonymous indicates that no rhosts authentication will be used.
- * If login fails, this function prints an error and never returns.
- * This function does not require super-user privileges.
+ * check whether the supplied host key is valid, return only if ok.
*/
+
void
-ssh_login(int host_key_valid,
- RSA *own_host_key,
- const char *orighost,
- struct sockaddr_in *hostaddr,
- uid_t original_real_uid)
+check_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key)
{
- int i, type;
- 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;
+ RSA *file_key;
+ char *ip = NULL;
char hostline[1000], *hostp;
- 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;
+ int local = 0, host_ip_differ = 0;
+ int sa_len;
+ char ntop[NI_MAXHOST];
+
+ /*
+ * Force accepting of the host key for loopback/localhost. 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.
+ */
+ switch (hostaddr->sa_family) {
+ case AF_INET:
+ local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
+ sa_len = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
+ sa_len = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ local = 0;
+ sa_len = sizeof(struct sockaddr_storage);
+ break;
+ }
+ if (local) {
+ debug("Forcing accepting of host key for loopback/localhost.");
+ return;
+ }
/*
* Turn off check_host_ip for proxy connects, since
if (options.proxy_command != NULL && options.check_host_ip)
options.check_host_ip = 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.");
+ if (options.check_host_ip) {
+ if (getnameinfo(hostaddr, sa_len, ntop, sizeof(ntop),
+ NULL, 0, NI_NUMERICHOST) != 0)
+ fatal("check_host_key: getnameinfo failed");
+ ip = xstrdup(ntop);
}
- /* 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. */
+ /*
+ * 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);
-
- supported_ciphers = packet_get_int();
- 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_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.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
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);
+ "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");
}
}
/* The host key has changed. */
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
- error("@ WARNING: HOST IDENTIFICATION HAS CHANGED! @");
+ error("@ WARNING: REMOTE 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)!");
*/
break;
}
-
if (options.check_host_ip)
xfree(ip);
+}
+
+/*
+ * Starts a dialog with the server, and authenticates the current user on the
+ * server. This does not need any extra privileges. The basic connection
+ * 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
+ * be used. Anonymous indicates that no rhosts authentication will be used.
+ * If login fails, this function prints an error and never returns.
+ * This function does not require super-user privileges.
+ */
+void
+ssh_login(int host_key_valid,
+ RSA *own_host_key,
+ const char *orighost,
+ struct sockaddr *hostaddr,
+ uid_t original_real_uid)
+{
+ int i, type;
+ struct passwd *pw;
+ BIGNUM *key;
+ RSA *host_key;
+ RSA *public_key;
+ int bits, rbits;
+ unsigned char session_key[SSH_SESSION_KEY_LENGTH];
+ const char *server_user, *local_user;
+ char *host, *cp;
+ unsigned char check_bytes[8];
+ unsigned int supported_ciphers, supported_authentications;
+ unsigned int server_flags, client_flags;
+ int payload_len, clen, sum_len = 0;
+ u_int32_t rand = 0;
+
+ /* 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.");
+ }
+
+ /* Get protocol flags. */
+ server_flags = packet_get_int();
+ packet_set_protocol_flags(server_flags);
+
+ supported_ciphers = packet_get_int();
+ 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);
+
+ check_host_key(host, hostaddr, host_key);
+
+ client_flags = SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN;
+
+ compute_session_id(session_id, check_bytes, host_key->n, public_key->n);
/* Generate a session key. */
arc4random_stir();
packet_put_bignum(key);
/* Send protocol flags. */
- packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
+ packet_put_int(client_flags);
/* Send the packet now. */
packet_send();
if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) &&
options.password_authentication && !options.batch_mode) {
char prompt[80];
+
snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ",
- server_user, host);
+ server_user, host);
if (try_password_authentication(prompt))
return;
}