summaryrefslogtreecommitdiff
path: root/usr.bin/ssh/sshconnect.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/ssh/sshconnect.c')
-rw-r--r--usr.bin/ssh/sshconnect.c78
1 files changed, 65 insertions, 13 deletions
diff --git a/usr.bin/ssh/sshconnect.c b/usr.bin/ssh/sshconnect.c
index a568d6557e4..df5a9bdcb5a 100644
--- a/usr.bin/ssh/sshconnect.c
+++ b/usr.bin/ssh/sshconnect.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect.c,v 1.218 2010/01/13 00:19:04 dtucker Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.219 2010/02/26 20:29:54 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -49,6 +49,7 @@
#include "misc.h"
#include "dns.h"
#include "roaming.h"
+#include "ssh2.h"
#include "version.h"
char *client_version_string = NULL;
@@ -567,6 +568,23 @@ confirm(const char *prompt)
}
}
+static int
+check_host_cert(const char *host, const Key *host_key)
+{
+ const char *reason;
+
+ if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) {
+ error("%s", reason);
+ return 0;
+ }
+ if (buffer_len(&host_key->cert->constraints) != 0) {
+ error("Certificate for %s contains unsupported constraint(s)",
+ host);
+ return 0;
+ }
+ return 1;
+}
+
/*
* 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.
@@ -579,13 +597,13 @@ 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 *file_key;
- const char *type = key_type(host_key);
+ Key *file_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, local = 0, host_ip_differ = 0;
+ int r, want_cert, local = 0, host_ip_differ = 0;
char ntop[NI_MAXHOST];
char msg[1024];
int len, host_line, ip_line, cancelled_forwarding = 0;
@@ -654,11 +672,15 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
host = put_host_port(hostname, port);
}
+ retry:
+ 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(host_key->type);
+ 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
@@ -674,9 +696,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
}
/*
* Also perform check for the ip address, skip the check if we are
- * localhost or the hostname was an ip address to begin with
+ * localhost, looking for a certificate, or the hostname was an ip
+ * address to begin with.
*/
- if (options.check_host_ip) {
+ if (!want_cert && options.check_host_ip) {
Key *ip_key = key_new(host_key->type);
ip_file = user_hostfile;
@@ -700,11 +723,14 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
switch (host_status) {
case HOST_OK:
/* The host is known and the key matches. */
- debug("Host '%.200s' is known and matches the %s host key.",
- host, type);
- debug("Found key in %s:%d", host_file, host_line);
+ 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 ? "certificate" : "key", host_file, host_line);
+ if (want_cert && !check_host_cert(hostname, host_key))
+ goto fail;
if (options.check_host_ip && ip_status == HOST_NEW) {
- if (readonly)
+ if (readonly || want_cert)
logit("%s host key for IP address "
"'%.128s' not in list of known hosts.",
type, ip);
@@ -736,7 +762,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
break;
}
}
- if (readonly)
+ if (readonly || want_cert)
goto fail;
/* The host is new. */
if (options.strict_host_key_checking == 1) {
@@ -821,6 +847,17 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
"list of known hosts.", hostp, type);
break;
case HOST_CHANGED:
+ if (want_cert) {
+ /*
+ * This is only a debug() since it is valid to have
+ * CAs with wildcard DNS matches that don't match
+ * all hosts that one might visit.
+ */
+ debug("Host certificate authority does not "
+ "match %s in %s:%d", CA_MARKER,
+ host_file, host_line);
+ goto fail;
+ }
if (readonly == ROQUIET)
goto fail;
if (options.check_host_ip && host_ip_differ) {
@@ -957,6 +994,20 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
return 0;
fail:
+ if (want_cert) {
+ /*
+ * No matching certificate. Downgrade cert to raw key and
+ * search normally.
+ */
+ debug("No matching CA found. Retry with plain key");
+ raw_key = key_from_private(host_key);
+ if (key_drop_cert(raw_key) != 0)
+ fatal("Couldn't drop certificate");
+ host_key = raw_key;
+ goto retry;
+ }
+ if (raw_key != NULL)
+ key_free(raw_key);
xfree(ip);
xfree(host);
return -1;
@@ -969,7 +1020,8 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
struct stat st;
int flags = 0;
- if (options.verify_host_key_dns &&
+ /* XXX certs are not yet supported for DNS */
+ if (!key_is_cert(host_key) && options.verify_host_key_dns &&
verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) {
if (flags & DNS_VERIFY_FOUND) {