- djm@cvs.openbsd.org 2010/11/29 23:45:51

[auth.c hostfile.c hostfile.h ssh.c ssh_config.5 sshconnect.c]
     [sshconnect.h sshconnect2.c]
     automatically order the hostkeys requested by the client based on
     which hostkeys are already recorded in known_hosts. This avoids
     hostkey warnings when connecting to servers with new ECDSA keys
     that are preferred by default; with markus@
This commit is contained in:
Damien Miller 2010-12-01 12:21:51 +11:00
parent 03c0e533de
commit d925dcd8a5
9 changed files with 455 additions and 294 deletions

View File

@ -38,6 +38,13 @@
[authfile.c]
correctly load comment for encrypted rsa1 keys;
report/fix Joachim Schipper; ok djm@
- djm@cvs.openbsd.org 2010/11/29 23:45:51
[auth.c hostfile.c hostfile.h ssh.c ssh_config.5 sshconnect.c]
[sshconnect.h sshconnect2.c]
automatically order the hostkeys requested by the client based on
which hostkeys are already recorded in known_hosts. This avoids
hostkey warnings when connecting to servers with new ECDSA keys
that are preferred by default; with markus@
20101124
- (dtucker) [platform.c session.c] Move the getluid call out of session.c and

30
auth.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth.c,v 1.90 2010/11/23 02:35:50 djm Exp $ */
/* $OpenBSD: auth.c,v 1.91 2010/11/29 23:45:51 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@ -379,16 +379,15 @@ HostStatus
check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
const char *sysfile, const char *userfile)
{
Key *found;
char *user_hostfile;
struct stat st;
HostStatus host_status;
struct hostkeys *hostkeys;
const struct hostkey_entry *found;
/* Check if we know the host and its host key. */
found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
host_status = check_host_in_hostfile(sysfile, host, key, found, NULL);
if (host_status != HOST_OK && userfile != NULL) {
hostkeys = init_hostkeys();
load_hostkeys(hostkeys, host, sysfile);
if (userfile != NULL) {
user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
if (options.strict_modes &&
(stat(user_hostfile, &st) == 0) &&
@ -401,16 +400,23 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
user_hostfile);
} else {
temporarily_use_uid(pw);
host_status = check_host_in_hostfile(user_hostfile,
host, key, found, NULL);
load_hostkeys(hostkeys, host, user_hostfile);
restore_uid();
}
xfree(user_hostfile);
}
key_free(found);
host_status = check_key_in_hostkeys(hostkeys, key, &found);
if (host_status == HOST_REVOKED)
error("WARNING: revoked key for %s attempted authentication",
found->host);
else if (host_status == HOST_OK)
debug("%s: key for %s found at %s:%ld", __func__,
found->host, found->file, found->line);
else
debug("%s: key for host %s not found", __func__, host);
free_hostkeys(hostkeys);
debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ?
"ok" : "not found", host);
return host_status;
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: hostfile.c,v 1.48 2010/03/04 10:36:03 djm Exp $ */
/* $OpenBSD: hostfile.c,v 1.49 2010/11/29 23:45:51 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -56,6 +56,12 @@
#include "key.h"
#include "hostfile.h"
#include "log.h"
#include "misc.h"
struct hostkeys {
struct hostkey_entry *entries;
u_int num_entries;
};
static int
extract_salt(const char *s, u_int l, char *salt, size_t salt_len)
@ -164,26 +170,28 @@ hostfile_read_key(char **cpp, u_int *bitsp, Key *ret)
/* Return results. */
*cpp = cp;
*bitsp = key_size(ret);
if (bitsp != NULL)
*bitsp = key_size(ret);
return 1;
}
static int
hostfile_check_key(int bits, const Key *key, const char *host, const char *filename, int linenum)
hostfile_check_key(int bits, const Key *key, const char *host,
const char *filename, u_long linenum)
{
if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL)
return 1;
if (bits != BN_num_bits(key->rsa->n)) {
logit("Warning: %s, line %d: keysize mismatch for host %s: "
logit("Warning: %s, line %lu: keysize mismatch for host %s: "
"actual %d vs. announced %d.",
filename, linenum, host, BN_num_bits(key->rsa->n), bits);
logit("Warning: replace %d with %d in %s, line %d.",
logit("Warning: replace %d with %d in %s, line %lu.",
bits, BN_num_bits(key->rsa->n), filename, linenum);
}
return 1;
}
static enum { MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA }
static HostkeyMarker
check_markers(char **cpp)
{
char marker[32], *sp, *cp = *cpp;
@ -218,49 +226,32 @@ check_markers(char **cpp)
return ret;
}
/*
* Checks whether the given host (which must be in all lowercase) is already
* in the list of our known hosts. Returns HOST_OK if the host is known and
* has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED
* if the host is known but used to have a different host key.
*
* If no 'key' has been specified and a key of type 'keytype' is known
* for the specified host, then HOST_FOUND is returned.
*/
struct hostkeys *
init_hostkeys(void)
{
struct hostkeys *ret = xcalloc(1, sizeof(*ret));
static HostStatus
check_host_in_hostfile_by_key_or_type(const char *filename,
const char *host, const Key *key, int keytype, Key *found,
int want_revocation, int *numret)
ret->entries = NULL;
return ret;
}
void
load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path)
{
FILE *f;
char line[8192];
int want, have, linenum = 0, want_cert = key_is_cert(key);
u_int kbits;
u_long linenum = 0, num_loaded = 0;
char *cp, *cp2, *hashed_host;
HostStatus end_return;
HostkeyMarker marker;
Key *key;
int kbits;
debug3("check_host_in_hostfile: host %s filename %s", host, filename);
if (want_revocation && (key == NULL || keytype != 0 || found != NULL))
fatal("%s: invalid arguments", __func__);
/* Open the file containing the list of known hosts. */
f = fopen(filename, "r");
if (!f)
return HOST_NEW;
/*
* Return value when the loop terminates. This is set to
* HOST_CHANGED if we have seen a different key for the host and have
* not found the proper one.
*/
end_return = HOST_NEW;
/* Go through the file. */
while (fgets(line, sizeof(line), f)) {
if ((f = fopen(path, "r")) == NULL)
return;
debug3("%s: loading entries for host \"%.100s\" from file \"%s\"",
__func__, host, path);
while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) {
cp = line;
linenum++;
/* Skip any leading whitespace, comments and empty lines. */
for (; *cp == ' ' || *cp == '\t'; cp++)
@ -268,19 +259,11 @@ check_host_in_hostfile_by_key_or_type(const char *filename,
if (!*cp || *cp == '#' || *cp == '\n')
continue;
if (want_revocation)
want = MRK_REVOKE;
else if (want_cert)
want = MRK_CA;
else
want = MRK_NONE;
if ((have = check_markers(&cp)) == MRK_ERROR) {
verbose("%s: invalid marker at %s:%d",
__func__, filename, linenum);
continue;
} else if (want != have)
if ((marker = check_markers(&cp)) == MRK_ERROR) {
verbose("%s: invalid marker at %s:%lu",
__func__, path, linenum);
continue;
}
/* Find the end of the host name portion. */
for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
@ -292,8 +275,8 @@ check_host_in_hostfile_by_key_or_type(const char *filename,
continue;
hashed_host = host_hash(host, cp, (u_int) (cp2 - cp));
if (hashed_host == NULL) {
debug("Invalid hashed host line %d of %s",
linenum, filename);
debug("Invalid hashed host line %lu of %s",
linenum, path);
continue;
}
if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0)
@ -303,98 +286,166 @@ check_host_in_hostfile_by_key_or_type(const char *filename,
/* Got a match. Skip host name. */
cp = cp2;
if (want_revocation)
found = key_new(KEY_UNSPEC);
/*
* Extract the key from the line. This will skip any leading
* whitespace. Ignore badly formatted lines.
*/
if (!hostfile_read_key(&cp, &kbits, found))
continue;
if (numret != NULL)
*numret = linenum;
if (key == NULL) {
/* we found a key of the requested type */
if (found->type == keytype) {
fclose(f);
return HOST_FOUND;
key = key_new(KEY_UNSPEC);
if (!hostfile_read_key(&cp, &kbits, key)) {
key_free(key);
key = key_new(KEY_RSA1);
if (!hostfile_read_key(&cp, &kbits, key)) {
key_free(key);
continue;
}
continue;
}
if (!hostfile_check_key(kbits, found, host, filename, linenum))
if (!hostfile_check_key(kbits, key, host, path, linenum))
continue;
if (want_revocation) {
if (key_is_cert(key) &&
key_equal_public(key->cert->signature_key, found)) {
verbose("check_host_in_hostfile: revoked CA "
"line %d", linenum);
key_free(found);
return HOST_REVOKED;
}
if (key_equal_public(key, found)) {
verbose("check_host_in_hostfile: revoked key "
"line %d", linenum);
key_free(found);
return HOST_REVOKED;
}
key_free(found);
continue;
}
/* Check if the current key is the same as the given key. */
if (want_cert && key_equal(key->cert->signature_key, found)) {
/* Found CA cert for key */
debug3("check_host_in_hostfile: CA match line %d",
linenum);
fclose(f);
return HOST_OK;
} else if (!want_cert && key_equal(key, found)) {
/* Found identical key */
debug3("check_host_in_hostfile: match line %d", linenum);
fclose(f);
return HOST_OK;
}
/*
* They do not match. We will continue to go through the
* file; however, we note that we will not return that it is
* new.
*/
end_return = HOST_CHANGED;
debug3("%s: found %skey type %s in file %s:%lu", __func__,
marker == MRK_NONE ? "" :
(marker == MRK_CA ? "ca " : "revoked "),
key_type(key), path, linenum);
hostkeys->entries = xrealloc(hostkeys->entries,
hostkeys->num_entries + 1, sizeof(*hostkeys->entries));
hostkeys->entries[hostkeys->num_entries].host = xstrdup(host);
hostkeys->entries[hostkeys->num_entries].file = xstrdup(path);
hostkeys->entries[hostkeys->num_entries].line = linenum;
hostkeys->entries[hostkeys->num_entries].key = key;
hostkeys->entries[hostkeys->num_entries].marker = marker;
hostkeys->num_entries++;
num_loaded++;
}
/* Clear variables and close the file. */
fclose(f);
debug3("%s: loaded %lu keys", __func__, num_loaded);
return;
}
/*
* Return either HOST_NEW or HOST_CHANGED, depending on whether we
* saw a different key for the host.
*/
return end_return;
void
free_hostkeys(struct hostkeys *hostkeys)
{
u_int i;
for (i = 0; i < hostkeys->num_entries; i++) {
xfree(hostkeys->entries[i].host);
xfree(hostkeys->entries[i].file);
key_free(hostkeys->entries[i].key);
bzero(hostkeys->entries + i, sizeof(*hostkeys->entries));
}
if (hostkeys->entries != NULL)
xfree(hostkeys->entries);
hostkeys->entries = NULL;
hostkeys->num_entries = 0;
xfree(hostkeys);
}
static int
check_key_not_revoked(struct hostkeys *hostkeys, Key *k)
{
int is_cert = key_is_cert(k);
u_int i;
for (i = 0; i < hostkeys->num_entries; i++) {
if (hostkeys->entries[i].marker != MRK_REVOKE)
continue;
if (key_equal_public(k, hostkeys->entries[i].key))
return -1;
if (is_cert &&
key_equal_public(k->cert->signature_key,
hostkeys->entries[i].key))
return -1;
}
return 0;
}
/*
* Match keys against a specified key, or look one up by key type.
*
* If looking for a keytype (key == NULL) and one is found then return
* HOST_FOUND, otherwise HOST_NEW.
*
* If looking for a key (key != NULL):
* 1. If the key is a cert and a matching CA is found, return HOST_OK
* 2. If the key is not a cert and a matching key is found, return HOST_OK
* 3. If no key matches but a key with a different type is found, then
* return HOST_CHANGED
* 4. If no matching keys are found, then return HOST_NEW.
*
* Finally, check any found key is not revoked.
*/
static HostStatus
check_hostkeys_by_key_or_type(struct hostkeys *hostkeys,
Key *k, int keytype, const struct hostkey_entry **found)
{
u_int i;
HostStatus end_return = HOST_NEW;
int want_cert = key_is_cert(k);
HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE;
int proto = (k ? k->type : keytype) == KEY_RSA1 ? 1 : 2;
if (found != NULL)
*found = NULL;
for (i = 0; i < hostkeys->num_entries; i++) {
if (proto == 1 && hostkeys->entries[i].key->type != KEY_RSA1)
continue;
if (proto == 2 && hostkeys->entries[i].key->type == KEY_RSA1)
continue;
if (hostkeys->entries[i].marker != want_marker)
continue;
if (k == NULL) {
if (hostkeys->entries[i].key->type != keytype)
continue;
end_return = HOST_FOUND;
if (found != NULL)
*found = hostkeys->entries + i;
k = hostkeys->entries[i].key;
break;
}
if (want_cert) {
if (key_equal_public(k->cert->signature_key,
hostkeys->entries[i].key)) {
/* A matching CA exists */
end_return = HOST_OK;
if (found != NULL)
*found = hostkeys->entries + i;
break;
}
} else {
if (key_equal(k, hostkeys->entries[i].key)) {
end_return = HOST_OK;
if (found != NULL)
*found = hostkeys->entries + i;
break;
}
/* A non-maching key exists */
end_return = HOST_CHANGED;
if (found != NULL)
*found = hostkeys->entries + i;
}
}
if (check_key_not_revoked(hostkeys, k) != 0) {
end_return = HOST_REVOKED;
if (found != NULL)
*found = NULL;
}
return end_return;
}
HostStatus
check_host_in_hostfile(const char *filename, const char *host, const Key *key,
Key *found, int *numret)
check_key_in_hostkeys(struct hostkeys *hostkeys, Key *key,
const struct hostkey_entry **found)
{
if (key == NULL)
fatal("no key to look up");
if (check_host_in_hostfile_by_key_or_type(filename, host,
key, 0, NULL, 1, NULL) == HOST_REVOKED)
return HOST_REVOKED;
return check_host_in_hostfile_by_key_or_type(filename, host, key, 0,
found, 0, numret);
return check_hostkeys_by_key_or_type(hostkeys, key, 0, found);
}
int
lookup_key_in_hostfile_by_type(const char *filename, const char *host,
int keytype, Key *found, int *numret)
lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype,
const struct hostkey_entry **found)
{
return (check_host_in_hostfile_by_key_or_type(filename, host, NULL,
keytype, found, 0, numret) == HOST_FOUND);
return (check_hostkeys_by_key_or_type(hostkeys, NULL, keytype,
found) == HOST_FOUND);
}
/*

View File

@ -1,4 +1,4 @@
/* $OpenBSD: hostfile.h,v 1.18 2010/03/04 10:36:03 djm Exp $ */
/* $OpenBSD: hostfile.h,v 1.19 2010/11/29 23:45:51 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -18,12 +18,30 @@ typedef enum {
HOST_OK, HOST_NEW, HOST_CHANGED, HOST_REVOKED, HOST_FOUND
} HostStatus;
typedef enum {
MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA
} HostkeyMarker;
struct hostkey_entry {
char *host;
char *file;
u_long line;
Key *key;
HostkeyMarker marker;
};
struct hostkeys;
struct hostkeys *init_hostkeys(void);
void load_hostkeys(struct hostkeys *, const char *, const char *);
void free_hostkeys(struct hostkeys *);
HostStatus check_key_in_hostkeys(struct hostkeys *, Key *,
const struct hostkey_entry **);
int lookup_key_in_hostkeys_by_type(struct hostkeys *, int,
const struct hostkey_entry **);
int hostfile_read_key(char **, u_int *, Key *);
HostStatus check_host_in_hostfile(const char *, const char *,
const Key *, Key *, int *);
int add_host_to_hostfile(const char *, const char *, const Key *, int);
int lookup_key_in_hostfile_by_type(const char *, const char *,
int, Key *, int *);
int add_host_to_hostfile(const char *, const char *, const Key *, int);
#define HASH_MAGIC "|1|"
#define HASH_DELIM '|'

4
ssh.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh.c,v 1.354 2010/11/13 23:27:50 djm Exp $ */
/* $OpenBSD: ssh.c,v 1.355 2010/11/29 23:45:51 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -880,7 +880,7 @@ main(int ac, char **av)
/* Log into the remote system. Never returns if the login fails. */
ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr,
pw, timeout_ms);
options.port, pw, timeout_ms);
if (packet_connection_is_on_socket()) {
verbose("Authenticated to %s ([%s]:%d).", host,

View File

@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $OpenBSD: ssh_config.5,v 1.144 2010/11/15 07:40:14 jmc Exp $
.Dd $Mdocdate: November 15 2010 $
.\" $OpenBSD: ssh_config.5,v 1.145 2010/11/29 23:45:51 djm Exp $
.Dd $Mdocdate: November 29 2010 $
.Dt SSH_CONFIG 5
.Os
.Sh NAME
@ -555,6 +555,9 @@ ssh-rsa-cert-v00@openssh.com,ssh-dss-cert-v00@openssh.com,
ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
ssh-rsa,ssh-dss
.Ed
.Pp
If hostkeys are known for the destination host then this default is modified
to prefer their algorithms.
.It Cm HostKeyAlias
Specifies an alias that should be used instead of the
real host name when looking up or saving the host key

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshconnect.c,v 1.228 2010/10/06 21:10:21 djm Exp $ */
/* $OpenBSD: sshconnect.c,v 1.229 2010/11/29 23:45:51 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -75,7 +75,7 @@ extern char *__progname;
extern uid_t original_real_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 *);
/*
@ -607,6 +607,79 @@ check_host_cert(const char *host, const Key *host_key)
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];
socklen_t addrlen;
switch (hostaddr == NULL ? -1 : hostaddr->sa_family) {
case -1:
addrlen = 0;
break;
case AF_INET:
addrlen = sizeof(struct sockaddr_in);
break;
case AF_INET6:
addrlen = sizeof(struct sockaddr_in6);
break;
default:
addrlen = sizeof(struct sockaddr);
break;
}
/*
* 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, addrlen,
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
* is not valid. the user_hostfile will not be updated if 'readonly' is true.
@ -616,21 +689,21 @@ check_host_cert(const char *host, const Key *host_key)
#define ROQUIET 2
static int
check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
Key *host_key, int readonly, const char *user_hostfile,
const char *system_hostfile)
Key *host_key, int readonly, char *user_hostfile,
char *system_hostfile)
{
Key *file_key, *raw_key = NULL;
Key *raw_key = NULL;
const char *type;
char *ip = NULL, *host = NULL;
char hostline[1000], *hostp, *fp, *ra;
HostStatus host_status;
HostStatus ip_status;
int r, want_cert, local = 0, host_ip_differ = 0;
int salen;
char ntop[NI_MAXHOST];
int r, want_cert = key_is_cert(host_key), host_ip_differ = 0;
int local = sockaddr_is_local(hostaddr);
char msg[1024];
int len, host_line, ip_line, cancelled_forwarding = 0;
const char *host_file = NULL, *ip_file = NULL;
int len, cancelled_forwarding = 0;
struct hostkeys *host_hostkeys, *ip_hostkeys;
const struct hostkey_entry *host_found, *ip_found;
/*
* Force accepting of the host key for loopback/localhost. The
@ -640,23 +713,6 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
* essentially disables host authentication for localhost; however,
* 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;
salen = sizeof(struct sockaddr_in);
break;
case AF_INET6:
local = IN6_IS_ADDR_LOOPBACK(
&(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
salen = sizeof(struct sockaddr_in6);
break;
default:
local = 0;
salen = sizeof(struct sockaddr_storage);
break;
}
if (options.no_host_authentication_for_localhost == 1 && local &&
options.host_key_alias == NULL) {
debug("Forcing accepting of host key for "
@ -665,17 +721,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
}
/*
* We don't have the remote ip-address for connections
* using a proxy command
* Prepare the hostname and address strings used for hostkey lookup.
* In some cases, these will have a port number appended.
*/
if (options.proxy_command == NULL) {
if (getnameinfo(hostaddr, salen, 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>");
}
get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip);
/*
* Turn off check_host_ip if the connection is to localhost, via proxy
@ -685,74 +734,52 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
strcmp(hostname, ip) == 0 || options.proxy_command != NULL))
options.check_host_ip = 0;
/*
* 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 (options.host_key_alias != NULL) {
host = xstrdup(options.host_key_alias);
debug("using hostkeyalias: %s", host);
} else {
host = put_host_port(hostname, port);
host_hostkeys = init_hostkeys();
load_hostkeys(host_hostkeys, host, user_hostfile);
load_hostkeys(host_hostkeys, host, system_hostfile);
ip_hostkeys = NULL;
if (!want_cert && options.check_host_ip) {
ip_hostkeys = init_hostkeys();
load_hostkeys(ip_hostkeys, ip, user_hostfile);
load_hostkeys(ip_hostkeys, ip, system_hostfile);
}
retry:
/* Reload these as they may have changed on cert->key downgrade */
want_cert = key_is_cert(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
* hosts or in the systemwide list.
*/
host_file = user_hostfile;
host_status = check_host_in_hostfile(host_file, host, host_key,
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);
}
host_status = check_key_in_hostkeys(host_hostkeys, host_key,
&host_found);
/*
* Also perform check for the ip address, skip the check if we are
* localhost, looking for a certificate, or the hostname was an ip
* address to begin with.
*/
if (!want_cert && options.check_host_ip) {
Key *ip_key = key_new(host_key->type);
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 (!want_cert && ip_hostkeys != NULL) {
ip_status = check_key_in_hostkeys(ip_hostkeys, host_key,
&ip_found);
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;
key_free(ip_key);
} else
ip_status = host_status;
key_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 %s host %s.",
host, type, want_cert ? "certificate" : "key");
debug("Found %s in %s:%d",
want_cert ? "CA key" : "key", host_file, host_line);
debug("Found %s in %s:%lu", want_cert ? "CA key" : "key",
host_found->file, host_found->line);
if (want_cert && !check_host_cert(hostname, host_key))
goto fail;
if (options.check_host_ip && ip_status == HOST_NEW) {
@ -803,7 +830,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
} else if (options.strict_host_key_checking == 2) {
char msg1[1024], msg2[1024];
if (show_other_keys(host, host_key))
if (show_other_keys(host_hostkeys, host_key))
snprintf(msg1, sizeof(msg1),
"\nbut keys of different type are already"
" known for this host.");
@ -844,8 +871,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
* local known_hosts file.
*/
if (options.check_host_ip && ip_status == HOST_NEW) {
snprintf(hostline, sizeof(hostline), "%s,%s",
host, ip);
snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
hostp = hostline;
if (options.hash_known_hosts) {
/* Add hash of host and IP separately */
@ -899,8 +925,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
* all hosts that one might visit.
*/
debug("Host certificate authority does not "
"match %s in %s:%d", CA_MARKER,
host_file, host_line);
"match %s in %s:%lu", CA_MARKER,
host_found->file, host_found->line);
goto fail;
}
if (readonly == ROQUIET)
@ -922,13 +948,15 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
error("DNS SPOOFING is happening or the IP address for the host");
error("and its host key have changed at the same time.");
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. */
warn_changed_key(host_key);
error("Add correct host key in %.100s to get rid of this message.",
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
@ -1013,13 +1041,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
snprintf(msg, sizeof(msg),
"Warning: the %s host key for '%.200s' "
"differs from the key for the IP address '%.128s'"
"\nOffending key for IP in %s:%d",
type, host, ip, ip_file, ip_line);
"\nOffending key for IP in %s:%lu",
type, host, ip, ip_found->file, ip_found->line);
if (host_status == HOST_OK) {
len = strlen(msg);
snprintf(msg + len, sizeof(msg) - len,
"\nMatching host key in %s:%d",
host_file, host_line);
"\nMatching host key in %s:%lu",
host_found->file, host_found->line);
}
if (options.strict_host_key_checking == 1) {
logit("%s", msg);
@ -1037,6 +1065,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
xfree(ip);
xfree(host);
if (host_hostkeys != NULL)
free_hostkeys(host_hostkeys);
if (ip_hostkeys != NULL)
free_hostkeys(ip_hostkeys);
return 0;
fail:
@ -1056,6 +1088,10 @@ fail:
key_free(raw_key);
xfree(ip);
xfree(host);
if (host_hostkeys != NULL)
free_hostkeys(host_hostkeys);
if (ip_hostkeys != NULL)
free_hostkeys(ip_hostkeys);
return -1;
}
@ -1065,6 +1101,11 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
{
struct stat st;
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 */
if (!key_is_cert(host_key) && options.verify_host_key_dns &&
@ -1108,7 +1149,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
*/
void
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 *server_user, *local_user;
@ -1131,7 +1172,7 @@ ssh_login(Sensitive *sensitive, const char *orighost,
/* key exchange */
/* authenticate user */
if (compat20) {
ssh_kex2(host, hostaddr);
ssh_kex2(host, hostaddr, port);
ssh_userauth2(local_user, server_user, host, sensitive);
} else {
ssh_kex(host, hostaddr);
@ -1158,61 +1199,35 @@ ssh_put_password(char *password)
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 */
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 i, found = 0;
int i, ret = 0;
char *fp, *ra;
const struct hostkey_entry *found;
for (i = 0; type[i] != -1; i++) {
if (type[i] == key->type)
continue;
if (type[i] != KEY_RSA1 &&
show_key_from_file(options.user_hostfile2, host, type[i])) {
found = 1;
if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found))
continue;
}
if (type[i] != KEY_RSA1 &&
show_key_from_file(options.system_hostfile2, host, type[i])) {
found = 1;
continue;
}
if (show_key_from_file(options.user_hostfile, host, type[i])) {
found = 1;
continue;
}
if (show_key_from_file(options.system_hostfile, host, type[i])) {
found = 1;
continue;
}
debug2("no key of type %d for host %s", type[i], host);
fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX);
ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART);
logit("WARNING: %s key found for host %s\n"
"in %s:%lu\n"
"%s key fingerprint %s.",
key_type(found->key),
found->host, found->file, found->line,
key_type(found->key), fp);
if (options.visual_host_key)
logit("%s", ra);
xfree(ra);
xfree(fp);
ret = 1;
}
return (found);
return ret;
}
static void

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshconnect.h,v 1.26 2010/10/06 06:39:28 djm Exp $ */
/* $OpenBSD: sshconnect.h,v 1.27 2010/11/29 23:45:51 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
@ -36,15 +36,18 @@ ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int,
int *, int, int, const char *);
void ssh_kill_proxy_command(void);
void
ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *, int);
void ssh_login(Sensitive *, const char *, struct sockaddr *, u_short,
struct passwd *, int);
void ssh_exchange_identification(int);
int verify_host_key(char *, struct sockaddr *, Key *);
void get_hostfile_hostname_ipaddr(char *, struct sockaddr *, u_short,
char **, char **);
void ssh_kex(char *, struct sockaddr *);
void ssh_kex2(char *, struct sockaddr *);
void ssh_kex2(char *, struct sockaddr *, u_short);
void ssh_userauth1(const char *, const char *, char *, Sensitive *);
void ssh_userauth2(const char *, const char *, char *, Sensitive *);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshconnect2.c,v 1.185 2010/09/22 05:01:29 djm Exp $ */
/* $OpenBSD: sshconnect2.c,v 1.186 2010/11/29 23:45:51 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Damien Miller. All rights reserved.
@ -69,6 +69,7 @@
#include "msg.h"
#include "pathnames.h"
#include "uidswap.h"
#include "hostfile.h"
#include "schnorr.h"
#include "jpake.h"
@ -101,8 +102,60 @@ verify_host_key_callback(Key *hostkey)
return 0;
}
static char *
order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port)
{
char *oavail, *avail, *first, *last, *alg, *hostname, *ret;
size_t maxlen;
struct hostkeys *hostkeys;
int ktype;
/* Find all hostkeys for this hostname */
get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL);
hostkeys = init_hostkeys();
load_hostkeys(hostkeys, hostname, options.user_hostfile2);
load_hostkeys(hostkeys, hostname, options.system_hostfile2);
load_hostkeys(hostkeys, hostname, options.user_hostfile);
load_hostkeys(hostkeys, hostname, options.system_hostfile);
oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG);
maxlen = strlen(avail) + 1;
first = xmalloc(maxlen);
last = xmalloc(maxlen);
*first = *last = '\0';
#define ALG_APPEND(to, from) \
do { \
if (*to != '\0') \
strlcat(to, ",", maxlen); \
strlcat(to, from, maxlen); \
} while (0)
while ((alg = strsep(&avail, ",")) && *alg != '\0') {
if ((ktype = key_type_from_name(alg)) == KEY_UNSPEC)
fatal("%s: unknown alg %s", __func__, alg);
if (lookup_key_in_hostkeys_by_type(hostkeys,
key_type_plain(ktype), NULL))
ALG_APPEND(first, alg);
else
ALG_APPEND(last, alg);
}
#undef ALG_APPEND
xasprintf(&ret, "%s%s%s", first, *first == '\0' ? "" : ",", last);
if (*first != '\0')
debug3("%s: prefer hostkeyalgs: %s", __func__, first);
xfree(first);
xfree(last);
xfree(hostname);
xfree(oavail);
free_hostkeys(hostkeys);
return ret;
}
void
ssh_kex2(char *host, struct sockaddr *hostaddr)
ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
{
Kex *kex;
@ -135,6 +188,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr)
if (options.hostkeyalgorithms != NULL)
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
options.hostkeyalgorithms;
else {
/* Prefer algorithms that we already have keys for */
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
order_hostkeyalgs(host, hostaddr, port);
}
if (options.kex_algorithms != NULL)
myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;