Presumably, a CNAME will be returned as the canonical name, meaning
you're asking the network what name to expect. That's generally quite
verboten. However, if we presume that SSHFP is wildly insecure anyway
without DNSSEC, then this might be OK, because end-to-end DNSSEC will
prevent a malicious CNAME from being accepted.
On Sun, Nov 28, 2010 at 3:33 AM, Jan Andres <jandres at gmx.net>
wrote:> 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>
> _______________________________________________
> openssh-unix-dev mailing list
> openssh-unix-dev at mindrot.org
> https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
>