version 1.228, 2010/10/06 21:10:21 |
version 1.229, 2010/11/29 23:45:51 |
|
|
extern uid_t original_real_uid; |
extern uid_t original_real_uid; |
extern uid_t original_effective_uid; |
extern uid_t original_effective_uid; |
|
|
static int show_other_keys(const char *, Key *); |
static int show_other_keys(struct hostkeys *, Key *); |
static void warn_changed_key(Key *); |
static void warn_changed_key(Key *); |
|
|
/* |
/* |
|
|
return 1; |
return 1; |
} |
} |
|
|
|
static int |
|
sockaddr_is_local(struct sockaddr *hostaddr) |
|
{ |
|
switch (hostaddr->sa_family) { |
|
case AF_INET: |
|
return (ntohl(((struct sockaddr_in *)hostaddr)-> |
|
sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; |
|
case AF_INET6: |
|
return IN6_IS_ADDR_LOOPBACK( |
|
&(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); |
|
default: |
|
return 0; |
|
} |
|
} |
|
|
/* |
/* |
|
* Prepare the hostname and ip address strings that are used to lookup |
|
* host keys in known_hosts files. These may have a port number appended. |
|
*/ |
|
void |
|
get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, |
|
u_short port, char **hostfile_hostname, char **hostfile_ipaddr) |
|
{ |
|
char ntop[NI_MAXHOST]; |
|
|
|
/* |
|
* We don't have the remote ip-address for connections |
|
* using a proxy command |
|
*/ |
|
if (hostfile_ipaddr != NULL) { |
|
if (options.proxy_command == NULL) { |
|
if (getnameinfo(hostaddr, hostaddr->sa_len, |
|
ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) |
|
fatal("check_host_key: getnameinfo failed"); |
|
*hostfile_ipaddr = put_host_port(ntop, port); |
|
} else { |
|
*hostfile_ipaddr = xstrdup("<no hostip for proxy " |
|
"command>"); |
|
} |
|
} |
|
|
|
/* |
|
* Allow the user to record the key under a different name or |
|
* differentiate a non-standard port. This is useful for ssh |
|
* tunneling over forwarded connections or if you run multiple |
|
* sshd's on different ports on the same machine. |
|
*/ |
|
if (hostfile_hostname != NULL) { |
|
if (options.host_key_alias != NULL) { |
|
*hostfile_hostname = xstrdup(options.host_key_alias); |
|
debug("using hostkeyalias: %s", *hostfile_hostname); |
|
} else { |
|
*hostfile_hostname = put_host_port(hostname, port); |
|
} |
|
} |
|
} |
|
|
|
/* |
* 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 ROQUIET 2 |
#define ROQUIET 2 |
static int |
static int |
check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, |
check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, |
Key *host_key, int readonly, const char *user_hostfile, |
Key *host_key, int readonly, char *user_hostfile, |
const char *system_hostfile) |
char *system_hostfile) |
{ |
{ |
Key *file_key, *raw_key = NULL; |
Key *raw_key = NULL; |
const char *type; |
const char *type; |
char *ip = NULL, *host = NULL; |
char *ip = NULL, *host = NULL; |
char hostline[1000], *hostp, *fp, *ra; |
char hostline[1000], *hostp, *fp, *ra; |
HostStatus host_status; |
HostStatus host_status; |
HostStatus ip_status; |
HostStatus ip_status; |
int r, want_cert, local = 0, host_ip_differ = 0; |
int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; |
char ntop[NI_MAXHOST]; |
int local = sockaddr_is_local(hostaddr); |
char msg[1024]; |
char msg[1024]; |
int len, host_line, ip_line, cancelled_forwarding = 0; |
int len, cancelled_forwarding = 0; |
const char *host_file = NULL, *ip_file = NULL; |
struct hostkeys *host_hostkeys, *ip_hostkeys; |
|
const struct hostkey_entry *host_found, *ip_found; |
|
|
/* |
/* |
* Force accepting of the host key for loopback/localhost. The |
* Force accepting of the host key for loopback/localhost. The |
|
|
* essentially disables host authentication for localhost; however, |
* essentially disables host authentication for localhost; however, |
* this is probably not a real problem. |
* this is probably not a real problem. |
*/ |
*/ |
/** hostaddr == 0! */ |
|
switch (hostaddr->sa_family) { |
|
case AF_INET: |
|
local = (ntohl(((struct sockaddr_in *)hostaddr)-> |
|
sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; |
|
break; |
|
case AF_INET6: |
|
local = IN6_IS_ADDR_LOOPBACK( |
|
&(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); |
|
break; |
|
default: |
|
local = 0; |
|
break; |
|
} |
|
if (options.no_host_authentication_for_localhost == 1 && local && |
if (options.no_host_authentication_for_localhost == 1 && local && |
options.host_key_alias == NULL) { |
options.host_key_alias == NULL) { |
debug("Forcing accepting of host key for " |
debug("Forcing accepting of host key for " |
|
|
} |
} |
|
|
/* |
/* |
* We don't have the remote ip-address for connections |
* Prepare the hostname and address strings used for hostkey lookup. |
* using a proxy command |
* In some cases, these will have a port number appended. |
*/ |
*/ |
if (options.proxy_command == NULL) { |
get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); |
if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), |
|
NULL, 0, NI_NUMERICHOST) != 0) |
|
fatal("check_host_key: getnameinfo failed"); |
|
ip = put_host_port(ntop, port); |
|
} else { |
|
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 |
|
|
strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) |
strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) |
options.check_host_ip = 0; |
options.check_host_ip = 0; |
|
|
/* |
host_hostkeys = init_hostkeys(); |
* Allow the user to record the key under a different name or |
load_hostkeys(host_hostkeys, host, user_hostfile); |
* differentiate a non-standard port. This is useful for ssh |
load_hostkeys(host_hostkeys, host, system_hostfile); |
* tunneling over forwarded connections or if you run multiple |
|
* sshd's on different ports on the same machine. |
ip_hostkeys = NULL; |
*/ |
if (!want_cert && options.check_host_ip) { |
if (options.host_key_alias != NULL) { |
ip_hostkeys = init_hostkeys(); |
host = xstrdup(options.host_key_alias); |
load_hostkeys(ip_hostkeys, ip, user_hostfile); |
debug("using hostkeyalias: %s", host); |
load_hostkeys(ip_hostkeys, ip, system_hostfile); |
} else { |
|
host = put_host_port(hostname, port); |
|
} |
} |
|
|
retry: |
retry: |
|
/* Reload these as they may have changed on cert->key downgrade */ |
want_cert = key_is_cert(host_key); |
want_cert = key_is_cert(host_key); |
type = key_type(host_key); |
type = key_type(host_key); |
|
|
/* |
/* |
* 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 = key_new(key_is_cert(host_key) ? KEY_UNSPEC : host_key->type); |
|
|
|
/* |
|
* Check if the host key is present in the user's list of known |
* Check if the host key is present in the user's list of known |
* hosts or in the systemwide list. |
* hosts or in the systemwide list. |
*/ |
*/ |
host_file = user_hostfile; |
host_status = check_key_in_hostkeys(host_hostkeys, host_key, |
host_status = check_host_in_hostfile(host_file, host, host_key, |
&host_found); |
file_key, &host_line); |
|
if (host_status == HOST_NEW) { |
|
host_file = system_hostfile; |
|
host_status = check_host_in_hostfile(host_file, host, host_key, |
|
file_key, &host_line); |
|
} |
|
/* |
/* |
* Also perform check for the ip address, skip the check if we are |
* Also perform check for the ip address, skip the check if we are |
* localhost, looking for a certificate, or the hostname was an ip |
* localhost, looking for a certificate, or the hostname was an ip |
* address to begin with. |
* address to begin with. |
*/ |
*/ |
if (!want_cert && options.check_host_ip) { |
if (!want_cert && ip_hostkeys != NULL) { |
Key *ip_key = key_new(host_key->type); |
ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, |
|
&ip_found); |
ip_file = user_hostfile; |
|
ip_status = check_host_in_hostfile(ip_file, ip, host_key, |
|
ip_key, &ip_line); |
|
if (ip_status == HOST_NEW) { |
|
ip_file = system_hostfile; |
|
ip_status = check_host_in_hostfile(ip_file, ip, |
|
host_key, ip_key, &ip_line); |
|
} |
|
if (host_status == HOST_CHANGED && |
if (host_status == HOST_CHANGED && |
(ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) |
(ip_status != HOST_CHANGED || |
|
(ip_found != NULL && |
|
!key_equal(ip_found->key, host_found->key)))) |
host_ip_differ = 1; |
host_ip_differ = 1; |
|
|
key_free(ip_key); |
|
} else |
} else |
ip_status = host_status; |
ip_status = host_status; |
|
|
key_free(file_key); |
|
|
|
switch (host_status) { |
switch (host_status) { |
case HOST_OK: |
case HOST_OK: |
/* The host is known and the key matches. */ |
/* The host is known and the key matches. */ |
debug("Host '%.200s' is known and matches the %s host %s.", |
debug("Host '%.200s' is known and matches the %s host %s.", |
host, type, want_cert ? "certificate" : "key"); |
host, type, want_cert ? "certificate" : "key"); |
debug("Found %s in %s:%d", |
debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", |
want_cert ? "CA key" : "key", host_file, host_line); |
host_found->file, host_found->line); |
if (want_cert && !check_host_cert(hostname, host_key)) |
if (want_cert && !check_host_cert(hostname, host_key)) |
goto fail; |
goto fail; |
if (options.check_host_ip && ip_status == HOST_NEW) { |
if (options.check_host_ip && ip_status == HOST_NEW) { |
|
|
} else if (options.strict_host_key_checking == 2) { |
} else if (options.strict_host_key_checking == 2) { |
char msg1[1024], msg2[1024]; |
char msg1[1024], msg2[1024]; |
|
|
if (show_other_keys(host, host_key)) |
if (show_other_keys(host_hostkeys, host_key)) |
snprintf(msg1, sizeof(msg1), |
snprintf(msg1, sizeof(msg1), |
"\nbut keys of different type are already" |
"\nbut keys of different type are already" |
" known for this host."); |
" known for this host."); |
|
|
* local known_hosts file. |
* local known_hosts file. |
*/ |
*/ |
if (options.check_host_ip && ip_status == HOST_NEW) { |
if (options.check_host_ip && ip_status == HOST_NEW) { |
snprintf(hostline, sizeof(hostline), "%s,%s", |
snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); |
host, ip); |
|
hostp = hostline; |
hostp = hostline; |
if (options.hash_known_hosts) { |
if (options.hash_known_hosts) { |
/* Add hash of host and IP separately */ |
/* Add hash of host and IP separately */ |
|
|
* all hosts that one might visit. |
* all hosts that one might visit. |
*/ |
*/ |
debug("Host certificate authority does not " |
debug("Host certificate authority does not " |
"match %s in %s:%d", CA_MARKER, |
"match %s in %s:%lu", CA_MARKER, |
host_file, host_line); |
host_found->file, host_found->line); |
goto fail; |
goto fail; |
} |
} |
if (readonly == ROQUIET) |
if (readonly == ROQUIET) |
|
|
error("DNS SPOOFING is happening or the IP address for the host"); |
error("DNS SPOOFING is happening or the IP address for the host"); |
error("and its host key have changed at the same time."); |
error("and its host key have changed at the same time."); |
if (ip_status != HOST_NEW) |
if (ip_status != HOST_NEW) |
error("Offending key for IP in %s:%d", ip_file, ip_line); |
error("Offending key for IP in %s:%lu", |
|
ip_found->file, ip_found->line); |
} |
} |
/* The host key has changed. */ |
/* The host key has changed. */ |
warn_changed_key(host_key); |
warn_changed_key(host_key); |
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.", |
user_hostfile); |
user_hostfile); |
error("Offending key in %s:%d", host_file, host_line); |
error("Offending %s key in %s:%lu", key_type(host_found->key), |
|
host_found->file, host_found->line); |
|
|
/* |
/* |
* If strict host key checking is in use, the user will have |
* If strict host key checking is in use, the user will have |
|
|
snprintf(msg, sizeof(msg), |
snprintf(msg, sizeof(msg), |
"Warning: the %s host key for '%.200s' " |
"Warning: the %s host key for '%.200s' " |
"differs from the key for the IP address '%.128s'" |
"differs from the key for the IP address '%.128s'" |
"\nOffending key for IP in %s:%d", |
"\nOffending key for IP in %s:%lu", |
type, host, ip, ip_file, ip_line); |
type, host, ip, ip_found->file, ip_found->line); |
if (host_status == HOST_OK) { |
if (host_status == HOST_OK) { |
len = strlen(msg); |
len = strlen(msg); |
snprintf(msg + len, sizeof(msg) - len, |
snprintf(msg + len, sizeof(msg) - len, |
"\nMatching host key in %s:%d", |
"\nMatching host key in %s:%lu", |
host_file, host_line); |
host_found->file, host_found->line); |
} |
} |
if (options.strict_host_key_checking == 1) { |
if (options.strict_host_key_checking == 1) { |
logit("%s", msg); |
logit("%s", msg); |
|
|
|
|
xfree(ip); |
xfree(ip); |
xfree(host); |
xfree(host); |
|
if (host_hostkeys != NULL) |
|
free_hostkeys(host_hostkeys); |
|
if (ip_hostkeys != NULL) |
|
free_hostkeys(ip_hostkeys); |
return 0; |
return 0; |
|
|
fail: |
fail: |
|
|
key_free(raw_key); |
key_free(raw_key); |
xfree(ip); |
xfree(ip); |
xfree(host); |
xfree(host); |
|
if (host_hostkeys != NULL) |
|
free_hostkeys(host_hostkeys); |
|
if (ip_hostkeys != NULL) |
|
free_hostkeys(ip_hostkeys); |
return -1; |
return -1; |
} |
} |
|
|
|
|
{ |
{ |
struct stat st; |
struct stat st; |
int flags = 0; |
int flags = 0; |
|
char *fp; |
|
|
|
fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); |
|
debug("Server host key: %s %s", key_type(host_key), fp); |
|
xfree(fp); |
|
|
/* XXX certs are not yet supported for DNS */ |
/* XXX certs are not yet supported for DNS */ |
if (!key_is_cert(host_key) && options.verify_host_key_dns && |
if (!key_is_cert(host_key) && options.verify_host_key_dns && |
verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { |
verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { |
|
|
*/ |
*/ |
void |
void |
ssh_login(Sensitive *sensitive, const char *orighost, |
ssh_login(Sensitive *sensitive, const char *orighost, |
struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms) |
struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) |
{ |
{ |
char *host, *cp; |
char *host, *cp; |
char *server_user, *local_user; |
char *server_user, *local_user; |
|
|
/* key exchange */ |
/* key exchange */ |
/* authenticate user */ |
/* authenticate user */ |
if (compat20) { |
if (compat20) { |
ssh_kex2(host, hostaddr); |
ssh_kex2(host, hostaddr, port); |
ssh_userauth2(local_user, server_user, host, sensitive); |
ssh_userauth2(local_user, server_user, host, sensitive); |
} else { |
} else { |
ssh_kex(host, hostaddr); |
ssh_kex(host, hostaddr); |
|
|
xfree(padded); |
xfree(padded); |
} |
} |
|
|
static int |
|
show_key_from_file(const char *file, const char *host, int keytype) |
|
{ |
|
Key *found; |
|
char *fp, *ra; |
|
int line, ret; |
|
|
|
found = key_new(keytype); |
|
if ((ret = lookup_key_in_hostfile_by_type(file, host, |
|
keytype, found, &line))) { |
|
fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); |
|
ra = key_fingerprint(found, SSH_FP_MD5, SSH_FP_RANDOMART); |
|
logit("WARNING: %s key found for host %s\n" |
|
"in %s:%d\n" |
|
"%s key fingerprint %s.\n%s\n", |
|
key_type(found), host, file, line, |
|
key_type(found), fp, ra); |
|
xfree(ra); |
|
xfree(fp); |
|
} |
|
key_free(found); |
|
return (ret); |
|
} |
|
|
|
/* print all known host keys for a given host, but skip keys of given type */ |
/* print all known host keys for a given host, but skip keys of given type */ |
static int |
static int |
show_other_keys(const char *host, Key *key) |
show_other_keys(struct hostkeys *hostkeys, Key *key) |
{ |
{ |
int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1}; |
int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1}; |
int i, found = 0; |
int i, ret = 0; |
|
char *fp, *ra; |
|
const struct hostkey_entry *found; |
|
|
for (i = 0; type[i] != -1; i++) { |
for (i = 0; type[i] != -1; i++) { |
if (type[i] == key->type) |
if (type[i] == key->type) |
continue; |
continue; |
if (type[i] != KEY_RSA1 && |
if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) |
show_key_from_file(options.user_hostfile2, host, type[i])) { |
|
found = 1; |
|
continue; |
continue; |
} |
fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX); |
if (type[i] != KEY_RSA1 && |
ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART); |
show_key_from_file(options.system_hostfile2, host, type[i])) { |
logit("WARNING: %s key found for host %s\n" |
found = 1; |
"in %s:%lu\n" |
continue; |
"%s key fingerprint %s.", |
} |
key_type(found->key), |
if (show_key_from_file(options.user_hostfile, host, type[i])) { |
found->host, found->file, found->line, |
found = 1; |
key_type(found->key), fp); |
continue; |
if (options.visual_host_key) |
} |
logit("%s", ra); |
if (show_key_from_file(options.system_hostfile, host, type[i])) { |
xfree(ra); |
found = 1; |
xfree(fp); |
continue; |
ret = 1; |
} |
|
debug2("no key of type %d for host %s", type[i], host); |
|
} |
} |
return (found); |
return ret; |
} |
} |
|
|
static void |
static void |