[PATCH] Use canonical hostname for DNS SSHFP lookup

Jan Andres jandres at gmx.net
Sun Nov 28 22:33:12 EST 2010


In the current implementation, ssh always uses the hostname supplied by
the user directly for the SSHFP DNS record lookup. This causes problems
when using the domain search path, e.g. I have "search example.com" in my 
resolv.conf and then do a "ssh host", I will connect to host.example.com,
but ssh will query the DNS for an SSHFP record of "host.", not
"host.example.com.".

The patch below attempts to fix this issue by having getaddrinfo()
return the canonical host name from the lookup, and passes this on so it
can be used in the SSHFP record query.

As a side-effect, the patch will completely suppress the SSHFP lookup if
establishing an SSH1 connection, as RSA1 keys cannot be stored in SSHFP
records anyway.

The getaddrinfo() implementation in openbsd-compat/fake-rfc2553.c is
also updated to support the AI_CANONNAME flag.

I don't use OpenBSD, so the patch was prepared against the latest
snapshot of the portable OpenSSH version. Sorry if this causes any
inconvenience.

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-27 23:36:30.775455403 +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;
@@ -200,7 +200,7 @@
 		return -1;
 	}
 
-	result = getrrsetbyname(hostname, DNS_RDATACLASS_IN,
+	result = getrrsetbyname(canohost, DNS_RDATACLASS_IN,
 	    DNS_RDATATYPE_SSHFP, 0, &fingerprints);
 	if (result) {
 		verbose("DNS lookup error: %s", dns_result_totext(result));
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/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