[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