version 1.176, 2006/02/22 00:04:45 |
version 1.176.2.2, 2006/11/08 00:17:14 |
|
|
|
/* $OpenBSD$ */ |
/* |
/* |
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
|
|
* called by a name other than "ssh" or "Secure Shell". |
* called by a name other than "ssh" or "Secure Shell". |
*/ |
*/ |
|
|
#include "includes.h" |
|
RCSID("$OpenBSD$"); |
|
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/wait.h> |
#include <sys/wait.h> |
#include <sys/stat.h> |
#include <sys/stat.h> |
|
#include <sys/socket.h> |
|
#include <sys/time.h> |
|
|
|
#include <netinet/in.h> |
|
|
#include <ctype.h> |
#include <ctype.h> |
|
#include <errno.h> |
|
#include <netdb.h> |
#include <paths.h> |
#include <paths.h> |
|
#include <signal.h> |
|
#include <pwd.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
|
#include "ssh.h" |
|
#include "xmalloc.h" |
#include "xmalloc.h" |
|
#include "ssh.h" |
#include "rsa.h" |
#include "rsa.h" |
#include "buffer.h" |
#include "buffer.h" |
#include "packet.h" |
#include "packet.h" |
|
|
#include "atomicio.h" |
#include "atomicio.h" |
#include "misc.h" |
#include "misc.h" |
#include "dns.h" |
#include "dns.h" |
|
#include "version.h" |
|
|
char *client_version_string = NULL; |
char *client_version_string = NULL; |
char *server_version_string = NULL; |
char *server_version_string = NULL; |
|
|
int pin[2], pout[2]; |
int pin[2], pout[2]; |
pid_t pid; |
pid_t pid; |
char strport[NI_MAXSERV]; |
char strport[NI_MAXSERV]; |
size_t len; |
|
|
|
/* Convert the port number into a string. */ |
/* Convert the port number into a string. */ |
snprintf(strport, sizeof strport, "%hu", port); |
snprintf(strport, sizeof strport, "%hu", port); |
|
|
* Use "exec" to avoid "sh -c" processes on some platforms |
* Use "exec" to avoid "sh -c" processes on some platforms |
* (e.g. Solaris) |
* (e.g. Solaris) |
*/ |
*/ |
len = strlen(proxy_command) + 6; |
xasprintf(&tmp, "exec %s", proxy_command); |
tmp = xmalloc(len); |
|
strlcpy(tmp, "exec ", len); |
|
strlcat(tmp, proxy_command, len); |
|
command_string = percent_expand(tmp, "h", host, |
command_string = percent_expand(tmp, "h", host, |
"p", strport, (char *)NULL); |
"p", strport, (char *)NULL); |
xfree(tmp); |
xfree(tmp); |
|
|
char *argv[10]; |
char *argv[10]; |
|
|
/* Child. Permanently give up superuser privileges. */ |
/* Child. Permanently give up superuser privileges. */ |
seteuid(original_real_uid); |
permanently_drop_suid(original_real_uid); |
setuid(original_real_uid); |
|
|
|
/* Redirect stdin and stdout. */ |
/* Redirect stdin and stdout. */ |
close(pin[1]); |
close(pin[1]); |
|
|
fd_set *fdset; |
fd_set *fdset; |
struct timeval tv; |
struct timeval tv; |
socklen_t optlen; |
socklen_t optlen; |
int fdsetsz, optval, rc, result = -1; |
int optval, rc, result = -1; |
|
|
if (timeout <= 0) |
if (timeout <= 0) |
return (connect(sockfd, serv_addr, addrlen)); |
return (connect(sockfd, serv_addr, addrlen)); |
|
|
if (errno != EINPROGRESS) |
if (errno != EINPROGRESS) |
return (-1); |
return (-1); |
|
|
fdsetsz = howmany(sockfd + 1, NFDBITS) * sizeof(fd_mask); |
fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), |
fdset = (fd_set *)xmalloc(fdsetsz); |
sizeof(fd_mask)); |
|
|
memset(fdset, 0, fdsetsz); |
|
FD_SET(sockfd, fdset); |
FD_SET(sockfd, fdset); |
tv.tv_sec = timeout; |
tv.tv_sec = timeout; |
tv.tv_usec = 0; |
tv.tv_usec = 0; |
|
|
fatal("%s: %.100s: %s", __progname, host, |
fatal("%s: %.100s: %s", __progname, host, |
gai_strerror(gaierr)); |
gai_strerror(gaierr)); |
|
|
/* |
for (attempt = 0; attempt < connection_attempts; attempt++) { |
* Try to connect several times. On some machines, the first time |
if (attempt > 0) { |
* will sometimes fail. In general socket code appears to behave |
/* Sleep a moment before retrying. */ |
* quite magically on many machines. |
sleep(1); |
*/ |
|
for (attempt = 0; ;) { |
|
if (attempt > 0) |
|
debug("Trying again..."); |
debug("Trying again..."); |
|
} |
/* Loop through addresses for this host, and try each one in |
/* |
sequence until the connection succeeds. */ |
* Loop through addresses for this host, and try each one in |
|
* sequence until the connection succeeds. |
|
*/ |
for (ai = aitop; ai; ai = ai->ai_next) { |
for (ai = aitop; ai; ai = ai->ai_next) { |
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) |
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) |
continue; |
continue; |
|
|
} else { |
} else { |
debug("connect to address %s port %s: %s", |
debug("connect to address %s port %s: %s", |
ntop, strport, strerror(errno)); |
ntop, strport, strerror(errno)); |
/* |
|
* Close the failed socket; there appear to |
|
* be some problems when reusing a socket for |
|
* which connect() has already returned an |
|
* error. |
|
*/ |
|
close(sock); |
close(sock); |
|
sock = -1; |
} |
} |
} |
} |
if (ai) |
if (sock != -1) |
break; /* Successful connection. */ |
break; /* Successful connection. */ |
|
|
attempt++; |
|
if (attempt >= connection_attempts) |
|
break; |
|
/* Sleep a moment before retrying. */ |
|
sleep(1); |
|
} |
} |
|
|
freeaddrinfo(aitop); |
freeaddrinfo(aitop); |
|
|
/* Return failure if we didn't get a successful connection. */ |
/* Return failure if we didn't get a successful connection. */ |
if (attempt >= connection_attempts) { |
if (sock == -1) { |
error("ssh: connect to host %s port %s: %s", |
error("ssh: connect to host %s port %s: %s", |
host, strport, strerror(errno)); |
host, strport, strerror(errno)); |
return (-1); |
return (-1); |
|
|
int connection_in = packet_get_connection_in(); |
int connection_in = packet_get_connection_in(); |
int connection_out = packet_get_connection_out(); |
int connection_out = packet_get_connection_out(); |
int minor1 = PROTOCOL_MINOR_1; |
int minor1 = PROTOCOL_MINOR_1; |
u_int i; |
u_int i, n; |
|
|
/* Read other side's version identification. */ |
/* Read other side's version identification. */ |
for (;;) { |
for (n = 0;;) { |
for (i = 0; i < sizeof(buf) - 1; i++) { |
for (i = 0; i < sizeof(buf) - 1; i++) { |
size_t len = atomicio(read, connection_in, &buf[i], 1); |
size_t len = atomicio(read, connection_in, &buf[i], 1); |
|
|
|
|
buf[i + 1] = 0; |
buf[i + 1] = 0; |
break; |
break; |
} |
} |
|
if (++n > 65536) |
|
fatal("ssh_exchange_identification: No banner received"); |
} |
} |
buf[sizeof(buf) - 1] = 0; |
buf[sizeof(buf) - 1] = 0; |
if (strncmp(buf, "SSH-", 4) == 0) |
if (strncmp(buf, "SSH-", 4) == 0) |
|
|
* check whether the supplied host key is valid, return -1 if the key |
* check whether the supplied host key is valid, return -1 if the key |
* is not valid. the user_hostfile will not be updated if 'readonly' is true. |
* is not valid. the user_hostfile will not be updated if 'readonly' is true. |
*/ |
*/ |
|
#define RDRW 0 |
|
#define RDONLY 1 |
|
#define ROQUIET 2 |
static int |
static int |
check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, |
check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, |
int readonly, const char *user_hostfile, const char *system_hostfile) |
Key *host_key, int readonly, const char *user_hostfile, |
|
const char *system_hostfile) |
{ |
{ |
Key *file_key; |
Key *file_key; |
const char *type = key_type(host_key); |
const char *type = key_type(host_key); |
char *ip = NULL; |
char *ip = NULL, *host = NULL; |
char hostline[1000], *hostp, *fp; |
char hostline[1000], *hostp, *fp; |
HostStatus host_status; |
HostStatus host_status; |
HostStatus ip_status; |
HostStatus ip_status; |
|
|
if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), |
if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), |
NULL, 0, NI_NUMERICHOST) != 0) |
NULL, 0, NI_NUMERICHOST) != 0) |
fatal("check_host_key: getnameinfo failed"); |
fatal("check_host_key: getnameinfo failed"); |
ip = xstrdup(ntop); |
ip = put_host_port(ntop, port); |
} else { |
} else { |
ip = xstrdup("<no hostip for proxy command>"); |
ip = xstrdup("<no hostip for proxy command>"); |
} |
} |
|
|
* Turn off check_host_ip if the connection is to localhost, via proxy |
* Turn off check_host_ip if the connection is to localhost, via proxy |
* command or if we don't have a hostname to compare with |
* command or if we don't have a hostname to compare with |
*/ |
*/ |
if (options.check_host_ip && |
if (options.check_host_ip && (local || |
(local || strcmp(host, ip) == 0 || options.proxy_command != NULL)) |
strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) |
options.check_host_ip = 0; |
options.check_host_ip = 0; |
|
|
/* |
/* |
* Allow the user to record the key under a different name. This is |
* Allow the user to record the key under a different name or |
* useful for ssh tunneling over forwarded connections or if you run |
* differentiate a non-standard port. This is useful for ssh |
* multiple sshd's on different ports on the same machine. |
* tunneling over forwarded connections or if you run multiple |
|
* sshd's on different ports on the same machine. |
*/ |
*/ |
if (options.host_key_alias != NULL) { |
if (options.host_key_alias != NULL) { |
host = options.host_key_alias; |
host = xstrdup(options.host_key_alias); |
debug("using hostkeyalias: %s", host); |
debug("using hostkeyalias: %s", host); |
|
} else { |
|
host = put_host_port(hostname, port); |
} |
} |
|
|
/* |
/* |
|
|
} |
} |
break; |
break; |
case HOST_NEW: |
case HOST_NEW: |
|
if (options.host_key_alias == NULL && port != 0 && |
|
port != SSH_DEFAULT_PORT) { |
|
debug("checking without port identifier"); |
|
if (check_host_key(hostname, hostaddr, 0, host_key, 2, |
|
user_hostfile, system_hostfile) == 0) { |
|
debug("found matching key w/out port"); |
|
break; |
|
} |
|
} |
if (readonly) |
if (readonly) |
goto fail; |
goto fail; |
/* The host is new. */ |
/* The host is new. */ |
|
|
"list of known hosts.", hostp, type); |
"list of known hosts.", hostp, type); |
break; |
break; |
case HOST_CHANGED: |
case HOST_CHANGED: |
|
if (readonly == ROQUIET) |
|
goto fail; |
if (options.check_host_ip && host_ip_differ) { |
if (options.check_host_ip && host_ip_differ) { |
char *key_msg; |
char *key_msg; |
if (ip_status == HOST_NEW) |
if (ip_status == HOST_NEW) |
|
|
/* |
/* |
* If strict host key checking has not been requested, allow |
* If strict host key checking has not been requested, allow |
* the connection but without MITM-able authentication or |
* the connection but without MITM-able authentication or |
* agent forwarding. |
* forwarding. |
*/ |
*/ |
if (options.password_authentication) { |
if (options.password_authentication) { |
error("Password authentication is disabled to avoid " |
error("Password authentication is disabled to avoid " |
|
|
options.num_local_forwards = |
options.num_local_forwards = |
options.num_remote_forwards = 0; |
options.num_remote_forwards = 0; |
} |
} |
|
if (options.tun_open != SSH_TUNMODE_NO) { |
|
error("Tunnel forwarding is disabled to avoid " |
|
"man-in-the-middle attacks."); |
|
options.tun_open = SSH_TUNMODE_NO; |
|
} |
/* |
/* |
* XXX Should permit the user to change to use the new id. |
* XXX Should permit the user to change to use the new id. |
* This could be done by converting the host key to an |
* This could be done by converting the host key to an |
|
|
} |
} |
|
|
xfree(ip); |
xfree(ip); |
|
xfree(host); |
return 0; |
return 0; |
|
|
fail: |
fail: |
xfree(ip); |
xfree(ip); |
|
xfree(host); |
return -1; |
return -1; |
} |
} |
|
|
|
|
/* return ok if the key can be found in an old keyfile */ |
/* return ok if the key can be found in an old keyfile */ |
if (stat(options.system_hostfile2, &st) == 0 || |
if (stat(options.system_hostfile2, &st) == 0 || |
stat(options.user_hostfile2, &st) == 0) { |
stat(options.user_hostfile2, &st) == 0) { |
if (check_host_key(host, hostaddr, host_key, /*readonly*/ 1, |
if (check_host_key(host, hostaddr, options.port, host_key, |
options.user_hostfile2, options.system_hostfile2) == 0) |
RDONLY, options.user_hostfile2, |
|
options.system_hostfile2) == 0) |
return 0; |
return 0; |
} |
} |
return check_host_key(host, hostaddr, host_key, /*readonly*/ 0, |
return check_host_key(host, hostaddr, options.port, host_key, |
options.user_hostfile, options.system_hostfile); |
RDRW, options.user_hostfile, options.system_hostfile); |
} |
} |
|
|
/* |
/* |
|
|
host = xstrdup(orighost); |
host = xstrdup(orighost); |
for (cp = host; *cp; cp++) |
for (cp = host; *cp; cp++) |
if (isupper(*cp)) |
if (isupper(*cp)) |
*cp = tolower(*cp); |
*cp = (char)tolower(*cp); |
|
|
/* Exchange protocol version identification strings with the server. */ |
/* Exchange protocol version identification strings with the server. */ |
ssh_exchange_identification(); |
ssh_exchange_identification(); |
|
|
ssh_kex(host, hostaddr); |
ssh_kex(host, hostaddr); |
ssh_userauth1(local_user, server_user, host, sensitive); |
ssh_userauth1(local_user, server_user, host, sensitive); |
} |
} |
|
xfree(local_user); |
} |
} |
|
|
void |
void |
|
|
return; |
return; |
} |
} |
size = roundup(strlen(password) + 1, 32); |
size = roundup(strlen(password) + 1, 32); |
padded = xmalloc(size); |
padded = xcalloc(1, size); |
memset(padded, 0, size); |
|
strlcpy(padded, password, size); |
strlcpy(padded, password, size); |
packet_put_string(padded, size); |
packet_put_string(padded, size); |
memset(padded, 0, size); |
memset(padded, 0, size); |