[PATCH] Use canonical hostname for DNS SSHFP lookup

Jan Andres jandres at gmx.net
Mon Nov 29 04:57:57 EST 2010


Here is a patch for the second approach. This one required some
butchering of the getrrsetbyname() code in order to

- add an option to use res_search() instead of res_query() (i.e. use the
  search path from resolv.conf)

- return the actual canonical name when confronted with CNAME/DNAME RRs
  (without this change, it would return the name of the CNAME as the
  canonical name).

Regards,
Jan


diff -ur openssh/dns.c openssh-sshfp/dns.c
--- openssh/dns.c	2010-08-31 14:41:14.000000000 +0200
+++ openssh-sshfp/dns.c	2010-11-28 18:45:41.594577525 +0100
@@ -173,7 +173,7 @@
  */
 int
 verify_host_key_dns(const char *hostname, struct sockaddr *address,
-    Key *hostkey, int *flags)
+    Key *hostkey, int *flags, const char *canohost)
 {
 	u_int counter;
 	int result;
@@ -201,16 +201,25 @@
 	}
 
 	result = getrrsetbyname(hostname, DNS_RDATACLASS_IN,
-	    DNS_RDATATYPE_SSHFP, 0, &fingerprints);
+	    DNS_RDATATYPE_SSHFP, RRSET_SEARCH, &fingerprints);
 	if (result) {
 		verbose("DNS lookup error: %s", dns_result_totext(result));
 		return -1;
 	}
 
+	debug3("canonical hostname of SSHFP RRset is %s",
+	    fingerprints->rri_name);
+
 	if (fingerprints->rri_flags & RRSET_VALIDATED) {
-		*flags |= DNS_VERIFY_SECURE;
-		debug("found %d secure fingerprints in DNS",
-		    fingerprints->rri_nrdatas);
+		if (strcasecmp(fingerprints->rri_name, canohost) == 0) {
+			*flags |= DNS_VERIFY_SECURE;
+			debug("found %d secure fingerprints in DNS",
+			    fingerprints->rri_nrdatas);
+		} else {
+			debug("found %d insecure fingerprints in DNS, "
+			    "not trusted due to host name mismatch",
+			    fingerprints->rri_nrdatas);
+		}
 	} else {
 		debug("found %d insecure fingerprints in DNS",
 		    fingerprints->rri_nrdatas);
diff -ur openssh/dns.h openssh-sshfp/dns.h
--- openssh/dns.h	2010-02-26 21:55:05.000000000 +0100
+++ openssh-sshfp/dns.h	2010-11-28 10:34:56.536431386 +0100
@@ -46,7 +46,8 @@
 #define DNS_VERIFY_MATCH	0x00000002
 #define DNS_VERIFY_SECURE	0x00000004
 
-int	verify_host_key_dns(const char *, struct sockaddr *, Key *, int *);
+int	verify_host_key_dns(const char *, struct sockaddr *, Key *, int *,
+    const char *);
 int	export_dns_rr(const char *, Key *, FILE *, int);
 
 #endif /* DNS_H */
diff -ur openssh/openbsd-compat/fake-rfc2553.c openssh-sshfp/openbsd-compat/fake-rfc2553.c
--- openssh/openbsd-compat/fake-rfc2553.c	2008-07-14 13:37:37.000000000 +0200
+++ openssh-sshfp/openbsd-compat/fake-rfc2553.c	2010-11-28 12:01:24.574267973 +0100
@@ -121,15 +121,23 @@
 
 #ifndef HAVE_GETADDRINFO
 static struct
-addrinfo *malloc_ai(int port, u_long addr, const struct addrinfo *hints)
+addrinfo *malloc_ai(int port, u_long addr, const struct addrinfo *hints,
+    const char *canonname)
 {
 	struct addrinfo *ai;
+	int len = sizeof(*ai) + sizeof(struct sockaddr_in);
+	int canonlen = 0;
 
-	ai = malloc(sizeof(*ai) + sizeof(struct sockaddr_in));
+	if (canonname != NULL) {
+		canonlen = strlen (canonname);
+		len += canonlen + 1;
+	}
+
+	ai = malloc(len);
 	if (ai == NULL)
 		return (NULL);
 	
-	memset(ai, '\0', sizeof(*ai) + sizeof(struct sockaddr_in));
+	memset(ai, '\0', len);
 	
 	ai->ai_addr = (struct sockaddr *)(ai + 1);
 	/* XXX -- ssh doesn't use sa_len */
@@ -138,6 +146,11 @@
 
 	((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
 	((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
+
+	if (canonname != NULL) {
+		ai->ai_canonname = ((char *)ai->ai_addr) + sizeof(struct sockaddr_in);
+		strlcpy(ai->ai_canonname, canonname, canonlen + 1);
+	}
 	
 	/* XXX: the following is not generally correct, but does what we want */
 	if (hints->ai_socktype)
@@ -182,21 +195,24 @@
 		addr = htonl(0x00000000);
 		if (hostname && inet_aton(hostname, &in) != 0)
 			addr = in.s_addr;
-		*res = malloc_ai(port, addr, hints);
+		*res = malloc_ai(port, addr, hints, NULL);
 		if (*res == NULL) 
 			return (EAI_MEMORY);
 		return (0);
 	}
 		
 	if (!hostname) {
-		*res = malloc_ai(port, htonl(0x7f000001), hints);
+		*res = malloc_ai(port, htonl(0x7f000001), hints, NULL);
 		if (*res == NULL) 
 			return (EAI_MEMORY);
 		return (0);
 	}
 	
 	if (inet_aton(hostname, &in)) {
-		*res = malloc_ai(port, in.s_addr, hints);
+		const char *canonname = NULL;
+		if (hints && hints->ai_flags & AI_CANONNAME)
+			canonname = hostname;
+		*res = malloc_ai(port, in.s_addr, hints, canonname);
 		if (*res == NULL) 
 			return (EAI_MEMORY);
 		return (0);
@@ -213,8 +229,12 @@
 		cur = prev = *res = NULL;
 		for (i = 0; hp->h_addr_list[i]; i++) {
 			struct in_addr *in = (struct in_addr *)hp->h_addr_list[i];
+			const char *canonname = NULL;
+
+			if (!prev && hints && hints->ai_flags & AI_CANONNAME)
+			    canonname = hp->h_name;
 
-			cur = malloc_ai(port, in->s_addr, hints);
+			cur = malloc_ai(port, in->s_addr, hints, canonname);
 			if (cur == NULL) {
 				if (*res != NULL)
 					freeaddrinfo(*res);
diff -ur openssh/openbsd-compat/getrrsetbyname.c openssh-sshfp/openbsd-compat/getrrsetbyname.c
--- openssh/openbsd-compat/getrrsetbyname.c	2009-07-13 03:38:23.000000000 +0200
+++ openssh-sshfp/openbsd-compat/getrrsetbyname.c	2010-11-28 18:49:13.886195349 +0100
@@ -209,8 +209,8 @@
 		goto fail;
 	}
 
-	/* don't allow flags yet, unimplemented */
-	if (flags) {
+	/* check for unsupported flags */
+	if (flags & ~RRSET_SEARCH) {
 		result = ERRSET_INVAL;
 		goto fail;
 	}
@@ -232,8 +232,13 @@
 #endif /* RES_USE_DNSEC */
 
 	/* make query */
-	length = res_query(hostname, (signed int) rdclass, (signed int) rdtype,
-	    answer, sizeof(answer));
+	if (flags & RRSET_SEARCH)
+		length = res_search(hostname, (signed int) rdclass,
+		    (signed int) rdtype, answer, sizeof(answer));
+	else
+		length = res_query(hostname, (signed int) rdclass,
+		    (signed int) rdtype, answer, sizeof(answer));
+
 	if (length < 0) {
 		switch(h_errno) {
 		case HOST_NOT_FOUND:
@@ -277,13 +282,6 @@
 		rrset->rri_flags |= RRSET_VALIDATED;
 #endif
 
-	/* copy name from answer section */
-	rrset->rri_name = strdup(response->answer->name);
-	if (rrset->rri_name == NULL) {
-		result = ERRSET_NOMEMORY;
-		goto fail;
-	}
-
 	/* count answers */
 	rrset->rri_nrdatas = count_dns_rr(response->answer, rrset->rri_rdclass,
 	    rrset->rri_rdtype);
@@ -314,8 +312,17 @@
 		rdata = NULL;
 
 		if (rr->class == rrset->rri_rdclass &&
-		    rr->type  == rrset->rri_rdtype)
+		    rr->type  == rrset->rri_rdtype) {
+			/* extract canonical name from first data RR */
+			if (index_ans == 0) {
+				rrset->rri_name = strdup(rr->name);
+				if (rrset->rri_name == NULL) {
+					result = ERRSET_NOMEMORY;
+					goto fail;
+				}
+			}
 			rdata = &rrset->rri_rdatas[index_ans++];
+		}
 
 		if (rr->class == rrset->rri_rdclass &&
 		    rr->type  == T_RRSIG)
Only in openssh-sshfp/openbsd-compat: .getrrsetbyname.c.swp
diff -ur openssh/openbsd-compat/getrrsetbyname.h openssh-sshfp/openbsd-compat/getrrsetbyname.h
--- openssh/openbsd-compat/getrrsetbyname.h	2007-10-26 08:26:50.000000000 +0200
+++ openssh-sshfp/openbsd-compat/getrrsetbyname.h	2010-11-28 14:48:32.393987287 +0100
@@ -69,6 +69,13 @@
 /*
  * Flags for getrrsetbyname()
  */
+#ifndef RRSET_SEARCH
+# define RRSET_SEARCH		1
+#endif
+
+/*
+ * Flags for rrsetinfo.rri_flags
+ */
 #ifndef RRSET_VALIDATED
 # define RRSET_VALIDATED	1
 #endif
diff -ur openssh/roaming_client.c openssh-sshfp/roaming_client.c
--- openssh/roaming_client.c	2010-01-26 02:53:06.000000000 +0100
+++ openssh-sshfp/roaming_client.c	2010-11-28 09:49:06.626052834 +0100
@@ -263,7 +263,7 @@
 		if (ssh_connect(host, &hostaddr, options.port,
 		    options.address_family, 1, &timeout_ms,
 		    options.tcp_keep_alive, options.use_privileged_port,
-		    options.proxy_command) == 0 && roaming_resume() == 0) {
+		    options.proxy_command, NULL) == 0 && roaming_resume() == 0) {
 			packet_restore_state();
 			reenter_guard = 0;
 			fprintf(stderr, "[connection resumed]\n");
diff -ur openssh/ssh.c openssh-sshfp/ssh.c
--- openssh/ssh.c	2010-11-20 05:19:38.000000000 +0100
+++ openssh-sshfp/ssh.c	2010-11-27 23:43:12.843314405 +0100
@@ -229,6 +229,7 @@
 	extern char *optarg;
 	struct servent *sp;
 	Forward fwd;
+	char *canohost;
 
 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
 	sanitise_stdfd();
@@ -760,7 +761,7 @@
 #else
 	    original_effective_uid == 0 && options.use_privileged_port,
 #endif
-	    options.proxy_command) != 0)
+	    options.proxy_command, &canohost) != 0)
 		exit(255);
 
 	if (timeout_ms > 0)
@@ -880,7 +881,7 @@
 
 	/* Log into the remote system.  Never returns if the login fails. */
 	ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr,
-	    pw, timeout_ms);
+	    pw, timeout_ms, canohost);
 
 	if (packet_connection_is_on_socket()) {
 		verbose("Authenticated to %s ([%s]:%d).", host,
@@ -889,6 +890,8 @@
 		verbose("Authenticated to %s (via proxy).", host);
 	}
 
+	xfree (canohost);
+
 	/* We no longer need the private host keys.  Clear them now. */
 	if (sensitive_data.nkeys != 0) {
 		for (i = 0; i < sensitive_data.nkeys; i++) {
diff -ur openssh/sshconnect1.c openssh-sshfp/sshconnect1.c
--- openssh/sshconnect1.c	2006-11-07 13:14:42.000000000 +0100
+++ openssh-sshfp/sshconnect1.c	2010-11-27 23:57:11.267747490 +0100
@@ -535,7 +535,7 @@
 	debug("Received server public key (%d bits) and host key (%d bits).",
 	    BN_num_bits(server_key->rsa->n), BN_num_bits(host_key->rsa->n));
 
-	if (verify_host_key(host, hostaddr, host_key) == -1)
+	if (verify_host_key(host, hostaddr, host_key, NULL) == -1)
 		fatal("Host key verification failed.");
 
 	client_flags = SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN;
diff -ur openssh/sshconnect2.c openssh-sshfp/sshconnect2.c
--- openssh/sshconnect2.c	2010-09-24 14:11:14.000000000 +0200
+++ openssh-sshfp/sshconnect2.c	2010-11-27 23:38:36.154046251 +0100
@@ -90,24 +90,26 @@
 
 char *xxx_host;
 struct sockaddr *xxx_hostaddr;
+const char *xxx_canohost;
 
 Kex *xxx_kex = NULL;
 
 static int
 verify_host_key_callback(Key *hostkey)
 {
-	if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) == -1)
+	if (verify_host_key(xxx_host, xxx_hostaddr, hostkey, xxx_canohost) == -1)
 		fatal("Host key verification failed.");
 	return 0;
 }
 
 void
-ssh_kex2(char *host, struct sockaddr *hostaddr)
+ssh_kex2(char *host, struct sockaddr *hostaddr, const char *canohost)
 {
 	Kex *kex;
 
 	xxx_host = host;
 	xxx_hostaddr = hostaddr;
+	xxx_canohost = canohost;
 
 	if (options.ciphers == (char *)-1) {
 		logit("No valid ciphers for protocol version 2 given, using defaults.");
diff -ur openssh/sshconnect.c openssh-sshfp/sshconnect.c
--- openssh/sshconnect.c	2010-11-28 12:04:32.127050308 +0100
+++ openssh-sshfp/sshconnect.c	2010-11-28 10:32:12.357225689 +0100
@@ -335,7 +335,8 @@
 int
 ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
     u_short port, int family, int connection_attempts, int *timeout_ms,
-    int want_keepalive, int needpriv, const char *proxy_command)
+    int want_keepalive, int needpriv, const char *proxy_command,
+    char **canohost)
 {
 	int gaierr;
 	int on = 1;
@@ -352,6 +353,8 @@
 	/* No proxy command. */
 
 	memset(&hints, 0, sizeof(hints));
+	if (canohost != NULL)
+		hints.ai_flags = AI_CANONNAME;
 	hints.ai_family = family;
 	hints.ai_socktype = SOCK_STREAM;
 	snprintf(strport, sizeof strport, "%u", port);
@@ -359,6 +362,9 @@
 		fatal("%s: Could not resolve hostname %.100s: %s", __progname,
 		    host, ssh_gai_strerror(gaierr));
 
+	if (canohost != NULL)
+		*canohost = xstrdup (aitop->ai_canonname);
+
 	for (attempt = 0; attempt < connection_attempts; attempt++) {
 		if (attempt > 0) {
 			/* Sleep a moment before retrying. */
@@ -1061,14 +1067,16 @@
 
 /* returns 0 if key verifies or -1 if key does NOT verify */
 int
-verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
+verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key,
+    const char *canohost)
 {
 	struct stat st;
 	int flags = 0;
 
 	/* 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) {
+	    canohost != NULL &&
+	    verify_host_key_dns(host, hostaddr, host_key, &flags, canohost) == 0) {
 
 		if (flags & DNS_VERIFY_FOUND) {
 
@@ -1108,7 +1116,8 @@
  */
 void
 ssh_login(Sensitive *sensitive, const char *orighost,
-    struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms)
+    struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms,
+    const char *canohost)
 {
 	char *host, *cp;
 	char *server_user, *local_user;
@@ -1131,7 +1140,7 @@
 	/* key exchange */
 	/* authenticate user */
 	if (compat20) {
-		ssh_kex2(host, hostaddr);
+		ssh_kex2(host, hostaddr, canohost);
 		ssh_userauth2(local_user, server_user, host, sensitive);
 	} else {
 		ssh_kex(host, hostaddr);
diff -ur openssh/sshconnect.h openssh-sshfp/sshconnect.h
--- openssh/sshconnect.h	2010-10-07 13:07:33.000000000 +0200
+++ openssh-sshfp/sshconnect.h	2010-11-28 10:34:37.941783851 +0100
@@ -33,18 +33,19 @@
 
 int
 ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int,
-    int *, int, int, const char *);
+    int *, int, int, const char *, char **);
 void	 ssh_kill_proxy_command(void);
 
 void
-ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *, int);
+ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *, int,
+    const char *);
 
 void	 ssh_exchange_identification(int);
 
-int	 verify_host_key(char *, struct sockaddr *, Key *);
+int	 verify_host_key(char *, struct sockaddr *, Key *, const char *);
 
 void	 ssh_kex(char *, struct sockaddr *);
-void	 ssh_kex2(char *, struct sockaddr *);
+void	 ssh_kex2(char *, struct sockaddr *, const char *);
 
 void	 ssh_userauth1(const char *, const char *, char *, Sensitive *);
 void	 ssh_userauth2(const char *, const char *, char *, Sensitive *);

-- 
Jan Andres <jandres at gmx.net>


More information about the openssh-unix-dev mailing list