- jakob@cvs.openbsd.org 2003/11/12 16:39:58
     [dns.c dns.h readconf.c ssh_config.5 sshconnect.c]
     update SSHFP validation. ok markus@
diff --git a/ChangeLog b/ChangeLog
index 40a3931..b5667e7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -31,6 +31,9 @@
    - dtucker@cvs.openbsd.org 2003/11/12 10:12:15
      [scp.c]
      When called with -q, pass -q to ssh; suppresses SSH2 banner.  ok markus@
+   - jakob@cvs.openbsd.org 2003/11/12 16:39:58
+     [dns.c dns.h readconf.c ssh_config.5 sshconnect.c]
+     update SSHFP validation. ok markus@
 
 20031115
  - (dtucker) [regress/agent-ptrace.sh] Test for GDB output from Solaris and
@@ -1451,4 +1454,4 @@
  - Fix sshd BindAddress and -b options for systems using fake-getaddrinfo.
    Report from murple@murple.net, diagnosis from dtucker@zip.com.au
 
-$Id: ChangeLog,v 1.3105 2003/11/17 10:19:05 djm Exp $
+$Id: ChangeLog,v 1.3106 2003/11/17 10:19:29 djm Exp $
diff --git a/dns.c b/dns.c
index 2fff1b8..2342b66 100644
--- a/dns.c
+++ b/dns.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: dns.c,v 1.7 2003/10/14 19:42:10 jakob Exp $	*/
+/*	$OpenBSD: dns.c,v 1.8 2003/11/12 16:39:58 jakob Exp $	*/
 
 /*
  * Copyright (c) 2003 Wesley Griffin. All rights reserved.
@@ -43,7 +43,7 @@
 #include "uuencode.h"
 
 extern char *__progname;
-RCSID("$OpenBSD: dns.c,v 1.7 2003/10/14 19:42:10 jakob Exp $");
+RCSID("$OpenBSD: dns.c,v 1.8 2003/11/12 16:39:58 jakob Exp $");
 
 #ifndef LWRES
 static const char *errset_text[] = {
@@ -83,7 +83,7 @@
  */
 static int
 dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
-    u_char **digest, u_int *digest_len, Key *key)
+    u_char **digest, u_int *digest_len, const Key *key)
 {
 	int success = 0;
 
@@ -145,16 +145,15 @@
 
 /*
  * Verify the given hostname, address and host key using DNS.
- * Returns 0 if key verifies or -1 if key does NOT verify
+ * Returns 0 if lookup succeeds, -1 otherwise 
  */
 int
 verify_host_key_dns(const char *hostname, struct sockaddr *address,
-    Key *hostkey)
+    const Key *hostkey, int *flags)
 {
 	int counter;
 	int result;
 	struct rrsetinfo *fingerprints = NULL;
-	int failures = 0;
 
 	u_int8_t hostkey_algorithm;
 	u_int8_t hostkey_digest_type;
@@ -166,6 +165,7 @@
 	u_char *dnskey_digest;
 	u_int dnskey_digest_len;
 
+	*flags = 0;
 
 	debug3("verify_hostkey_dns");
 	if (hostkey == NULL)
@@ -175,28 +175,29 @@
 	    DNS_RDATATYPE_SSHFP, 0, &fingerprints);
 	if (result) {
 		verbose("DNS lookup error: %s", dns_result_totext(result));
-		return DNS_VERIFY_ERROR;
+		return -1;
 	}
 
-#ifdef DNSSEC
-	/* Only accept validated answers */
-	if (!fingerprints->rri_flags & RRSET_VALIDATED) {
-		error("Ignored unvalidated fingerprint from DNS.");
-		freerrset(fingerprints);
-		return DNS_VERIFY_ERROR;
+	if (fingerprints->rri_flags & RRSET_VALIDATED) {
+		*flags |= DNS_VERIFY_SECURE;
+		debug("found %d secure fingerprints in DNS",
+		    fingerprints->rri_nrdatas);
+	} else {
+		debug("found %d insecure fingerprints in DNS",
+		    fingerprints->rri_nrdatas);
 	}
-#endif
-
-	debug("found %d fingerprints in DNS", fingerprints->rri_nrdatas);
 
 	/* Initialize host key parameters */
 	if (!dns_read_key(&hostkey_algorithm, &hostkey_digest_type,
 	    &hostkey_digest, &hostkey_digest_len, hostkey)) {
 		error("Error calculating host key fingerprint.");
 		freerrset(fingerprints);
-		return DNS_VERIFY_ERROR;
+		return -1;
 	}
 
+	if (fingerprints->rri_nrdatas)
+		*flags |= DNS_VERIFY_FOUND;
+
 	for (counter = 0 ; counter < fingerprints->rri_nrdatas ; counter++)  {
 		/*
 		 * Extract the key from the answer. Ignore any badly
@@ -218,35 +219,22 @@
 			    memcmp(hostkey_digest, dnskey_digest,
 			    hostkey_digest_len) == 0) {
 
-				/* Matching algoritm and digest. */
-				freerrset(fingerprints);
-				debug("matching host key fingerprint found in DNS");
-				return DNS_VERIFY_OK;
-			} else {
-				/* Correct algorithm but bad digest */
-				debug("verify_hostkey_dns: failed");
-				failures++;
+				*flags |= DNS_VERIFY_MATCH;
 			}
 		}
 	}
 
 	freerrset(fingerprints);
 
-	if (failures) {
-		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
-		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)!");
-		error("It is also possible that the %s host key has just been changed.",
-		    key_type(hostkey));
-		error("Please contact your system administrator.");
-		return DNS_VERIFY_FAILED;
-	}
+	if (*flags & DNS_VERIFY_FOUND)
+		if (*flags & DNS_VERIFY_MATCH)
+			debug("matching host key fingerprint found in DNS");
+		else
+			debug("mismatching host key fingerprint found in DNS");
+	else
+		debug("no host key fingerprint found in DNS");
 
-	debug("fingerprints found in DNS, but none of them matched");
-
-	return DNS_VERIFY_ERROR;
+	return 0;
 }
 
 
@@ -254,7 +242,7 @@
  * Export the fingerprint of a key as a DNS resource record
  */
 int
-export_dns_rr(const char *hostname, Key *key, FILE *f, int generic)
+export_dns_rr(const char *hostname, const Key *key, FILE *f, int generic)
 {
 	u_int8_t rdata_pubkey_algorithm = 0;
 	u_int8_t rdata_digest_type = SSHFP_HASH_SHA1;
diff --git a/dns.h b/dns.h
index 1eb07d9..c5da22e 100644
--- a/dns.h
+++ b/dns.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: dns.h,v 1.4 2003/10/14 19:42:10 jakob Exp $	*/
+/*	$OpenBSD: dns.h,v 1.5 2003/11/12 16:39:58 jakob Exp $	*/
 
 /*
  * Copyright (c) 2003 Wesley Griffin. All rights reserved.
@@ -45,11 +45,12 @@
 #define DNS_RDATACLASS_IN	1
 #define DNS_RDATATYPE_SSHFP	44
 
-#define DNS_VERIFY_FAILED	-1
-#define DNS_VERIFY_OK		0
-#define DNS_VERIFY_ERROR	1
+#define DNS_VERIFY_FOUND	0x00000001
+#define DNS_VERIFY_MATCH	0x00000002
+#define DNS_VERIFY_SECURE	0x00000004
 
-int	verify_host_key_dns(const char *, struct sockaddr *, Key *);
-int	export_dns_rr(const char *, Key *, FILE *, int);
+
+int	verify_host_key_dns(const char *, struct sockaddr *, const Key *, int *);
+int	export_dns_rr(const char *, const Key *, FILE *, int);
 
 #endif /* DNS_H */
diff --git a/readconf.c b/readconf.c
index 86d28bc..da49a39 100644
--- a/readconf.c
+++ b/readconf.c
@@ -12,7 +12,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: readconf.c,v 1.124 2003/10/14 19:42:10 jakob Exp $");
+RCSID("$OpenBSD: readconf.c,v 1.125 2003/11/12 16:39:58 jakob Exp $");
 
 #include "ssh.h"
 #include "xmalloc.h"
@@ -401,10 +401,11 @@
 
 	case oVerifyHostKeyDNS:
 		intptr = &options->verify_host_key_dns;
-		goto parse_flag;
+		goto parse_yesnoask;
 
 	case oStrictHostKeyChecking:
 		intptr = &options->strict_host_key_checking;
+parse_yesnoask:
 		arg = strdelim(&s);
 		if (!arg || *arg == '\0')
 			fatal("%.200s line %d: Missing yes/no/ask argument.",
diff --git a/ssh_config.5 b/ssh_config.5
index 9073ce5..55ca907 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -34,7 +34,7 @@
 .\" (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.23 2003/10/12 13:12:13 jmc Exp $
+.\" $OpenBSD: ssh_config.5,v 1.24 2003/11/12 16:39:58 jakob Exp $
 .Dd September 25, 1999
 .Dt SSH_CONFIG 5
 .Os
@@ -642,6 +642,23 @@
 .It Cm VerifyHostKeyDNS
 Specifies whether to verify the remote key using DNS and SSHFP resource
 records.
+If this option is set to
+.Dq yes ,
+the client will implicitly trust keys that matches a secure fingerprint
+from DNS.
+Insecure fingerprints will be handled as if this option was set to
+.Dq ask .
+If this option is set to
+.Dq ask ,
+information on fingerprint match will be displayed, but the user will still
+need to confirm new host keys according to the
+.Cm StrictHostKeyChecking
+option.
+The argument must be
+.Dq yes ,
+.Dq no
+or  
+.Dq ask  .
 The default is
 .Dq no .
 Note that this option applies to protocol version 2 only.
diff --git a/sshconnect.c b/sshconnect.c
index bf8c23d..5972e2b 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -13,7 +13,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect.c,v 1.152 2003/11/10 16:23:41 jakob Exp $");
+RCSID("$OpenBSD: sshconnect.c,v 1.153 2003/11/12 16:39:58 jakob Exp $");
 
 #include <openssl/bn.h>
 
@@ -38,7 +38,7 @@
 char *client_version_string = NULL;
 char *server_version_string = NULL;
 
-int verified_host_key_dns = 0;
+int matching_host_key_dns = 0;
 
 /* import */
 extern Options options;
@@ -728,7 +728,7 @@
 			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
 			msg2[0] = '\0';
 			if (options.verify_host_key_dns) {
-				if (verified_host_key_dns)
+				if (matching_host_key_dns)
 					snprintf(msg2, sizeof(msg2),
 					    "Matching host key fingerprint"
 					    " found in DNS.\n");
@@ -892,23 +892,25 @@
 verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
 {
 	struct stat st;
+	int flags = 0;
 
-	if (options.verify_host_key_dns) {
-		switch(verify_host_key_dns(host, hostaddr, host_key)) {
-		case DNS_VERIFY_OK:
-#ifdef DNSSEC
-			return 0;
-#else
-			verified_host_key_dns = 1;
-			break;
-#endif
-		case DNS_VERIFY_FAILED:
-			return -1;
-		case DNS_VERIFY_ERROR:
-			break;
-		default:
-			debug3("bad return value from verify_host_key_dns");
-			break;
+	if (options.verify_host_key_dns &&
+	    verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) {
+
+		if (flags & DNS_VERIFY_FOUND) {
+
+			if (options.verify_host_key_dns == 1 &&
+			    flags & DNS_VERIFY_MATCH &&
+			    flags & DNS_VERIFY_SECURE)
+				return 0;
+
+			if (flags & DNS_VERIFY_MATCH) {
+				matching_host_key_dns = 1;
+			} else {
+				warn_changed_key(host_key);
+				error("Update the SSHFP RR in DNS with the new "
+				    "host key to get rid of this message.");
+			}
 		}
 	}