[PATCH] Use canonical hostname for DNS SSHFP lookup

Dan Kaminsky dan at doxpara.com
Sun Nov 28 22:37:56 EST 2010


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
>


More information about the openssh-unix-dev mailing list