| http://bugs.gentoo.org/165444 |
| https://bugzilla.mindrot.org/show_bug.cgi?id=1008 |
| |
| --- a/readconf.c |
| +++ b/readconf.c |
| @@ -148,6 +148,7 @@ |
| oClearAllForwardings, oNoHostAuthenticationForLocalhost, |
| oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, |
| oAddressFamily, oGssAuthentication, oGssDelegateCreds, |
| + oGssTrustDns, |
| oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, |
| oSendEnv, oControlPath, oControlMaster, oControlPersist, |
| oHashKnownHosts, |
| @@ -194,9 +195,11 @@ |
| #if defined(GSSAPI) |
| { "gssapiauthentication", oGssAuthentication }, |
| { "gssapidelegatecredentials", oGssDelegateCreds }, |
| + { "gssapitrustdns", oGssTrustDns }, |
| # else |
| { "gssapiauthentication", oUnsupported }, |
| { "gssapidelegatecredentials", oUnsupported }, |
| + { "gssapitrustdns", oUnsupported }, |
| #endif |
| #ifdef ENABLE_PKCS11 |
| { "smartcarddevice", oPKCS11Provider }, |
| @@ -930,6 +933,10 @@ |
| intptr = &options->gss_deleg_creds; |
| goto parse_flag; |
| |
| + case oGssTrustDns: |
| + intptr = &options->gss_trust_dns; |
| + goto parse_flag; |
| + |
| case oBatchMode: |
| intptr = &options->batch_mode; |
| goto parse_flag; |
| @@ -1649,6 +1656,7 @@ |
| options->challenge_response_authentication = -1; |
| options->gss_authentication = -1; |
| options->gss_deleg_creds = -1; |
| + options->gss_trust_dns = -1; |
| options->password_authentication = -1; |
| options->kbd_interactive_authentication = -1; |
| options->kbd_interactive_devices = NULL; |
| @@ -1779,6 +1787,8 @@ |
| options->gss_authentication = 0; |
| if (options->gss_deleg_creds == -1) |
| options->gss_deleg_creds = 0; |
| + if (options->gss_trust_dns == -1) |
| + options->gss_trust_dns = 0; |
| if (options->password_authentication == -1) |
| options->password_authentication = 1; |
| if (options->kbd_interactive_authentication == -1) |
| --- a/readconf.h |
| +++ b/readconf.h |
| @@ -46,6 +46,7 @@ |
| /* Try S/Key or TIS, authentication. */ |
| int gss_authentication; /* Try GSS authentication */ |
| int gss_deleg_creds; /* Delegate GSS credentials */ |
| + int gss_trust_dns; /* Trust DNS for GSS canonicalization */ |
| int password_authentication; /* Try password |
| * authentication. */ |
| int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ |
| --- a/ssh_config.5 |
| +++ b/ssh_config.5 |
| @@ -830,6 +830,16 @@ |
| Forward (delegate) credentials to the server. |
| The default is |
| .Cm no . |
| +Note that this option applies to protocol version 2 connections using GSSAPI. |
| +.It Cm GSSAPITrustDns |
| +Set to |
| +.Dq yes to indicate that the DNS is trusted to securely canonicalize |
| +the name of the host being connected to. If |
| +.Dq no, the hostname entered on the |
| +command line will be passed untouched to the GSSAPI library. |
| +The default is |
| +.Dq no . |
| +This option only applies to protocol version 2 connections using GSSAPI. |
| .It Cm HashKnownHosts |
| Indicates that |
| .Xr ssh 1 |
| --- a/sshconnect2.c |
| +++ b/sshconnect2.c |
| @@ -656,6 +656,13 @@ |
| static u_int mech = 0; |
| OM_uint32 min; |
| int ok = 0; |
| + const char *gss_host; |
| + |
| + if (options.gss_trust_dns) { |
| + extern const char *auth_get_canonical_hostname(struct ssh *ssh, int use_dns); |
| + gss_host = auth_get_canonical_hostname(active_state, 1); |
| + } else |
| + gss_host = authctxt->host; |
| |
| /* Try one GSSAPI method at a time, rather than sending them all at |
| * once. */ |
| @@ -668,7 +674,7 @@ |
| /* My DER encoding requires length<128 */ |
| if (gss_supported->elements[mech].length < 128 && |
| ssh_gssapi_check_mechanism(&gssctxt, |
| - &gss_supported->elements[mech], authctxt->host)) { |
| + &gss_supported->elements[mech], gss_host)) { |
| ok = 1; /* Mechanism works */ |
| } else { |
| mech++; |
| |
| need to move these two funcs back to canohost so they're available to clients |
| and the server. auth.c is only used in the server. |
| |
| --- a/auth.c |
| +++ b/auth.c |
| @@ -784,117 +784,3 @@ fakepw(void) |
| |
| return (&fake); |
| } |
| - |
| -/* |
| - * Returns the remote DNS hostname as a string. The returned string must not |
| - * be freed. NB. this will usually trigger a DNS query the first time it is |
| - * called. |
| - * This function does additional checks on the hostname to mitigate some |
| - * attacks on legacy rhosts-style authentication. |
| - * XXX is RhostsRSAAuthentication vulnerable to these? |
| - * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) |
| - */ |
| - |
| -static char * |
| -remote_hostname(struct ssh *ssh) |
| -{ |
| - struct sockaddr_storage from; |
| - socklen_t fromlen; |
| - struct addrinfo hints, *ai, *aitop; |
| - char name[NI_MAXHOST], ntop2[NI_MAXHOST]; |
| - const char *ntop = ssh_remote_ipaddr(ssh); |
| - |
| - /* Get IP address of client. */ |
| - fromlen = sizeof(from); |
| - memset(&from, 0, sizeof(from)); |
| - if (getpeername(ssh_packet_get_connection_in(ssh), |
| - (struct sockaddr *)&from, &fromlen) < 0) { |
| - debug("getpeername failed: %.100s", strerror(errno)); |
| - return strdup(ntop); |
| - } |
| - |
| - ipv64_normalise_mapped(&from, &fromlen); |
| - if (from.ss_family == AF_INET6) |
| - fromlen = sizeof(struct sockaddr_in6); |
| - |
| - debug3("Trying to reverse map address %.100s.", ntop); |
| - /* Map the IP address to a host name. */ |
| - if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), |
| - NULL, 0, NI_NAMEREQD) != 0) { |
| - /* Host name not found. Use ip address. */ |
| - return strdup(ntop); |
| - } |
| - |
| - /* |
| - * if reverse lookup result looks like a numeric hostname, |
| - * someone is trying to trick us by PTR record like following: |
| - * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 |
| - */ |
| - memset(&hints, 0, sizeof(hints)); |
| - hints.ai_socktype = SOCK_DGRAM; /*dummy*/ |
| - hints.ai_flags = AI_NUMERICHOST; |
| - if (getaddrinfo(name, NULL, &hints, &ai) == 0) { |
| - logit("Nasty PTR record \"%s\" is set up for %s, ignoring", |
| - name, ntop); |
| - freeaddrinfo(ai); |
| - return strdup(ntop); |
| - } |
| - |
| - /* Names are stored in lowercase. */ |
| - lowercase(name); |
| - |
| - /* |
| - * Map it back to an IP address and check that the given |
| - * address actually is an address of this host. This is |
| - * necessary because anyone with access to a name server can |
| - * define arbitrary names for an IP address. Mapping from |
| - * name to IP address can be trusted better (but can still be |
| - * fooled if the intruder has access to the name server of |
| - * the domain). |
| - */ |
| - memset(&hints, 0, sizeof(hints)); |
| - hints.ai_family = from.ss_family; |
| - hints.ai_socktype = SOCK_STREAM; |
| - if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { |
| - logit("reverse mapping checking getaddrinfo for %.700s " |
| - "[%s] failed.", name, ntop); |
| - return strdup(ntop); |
| - } |
| - /* Look for the address from the list of addresses. */ |
| - for (ai = aitop; ai; ai = ai->ai_next) { |
| - if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, |
| - sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && |
| - (strcmp(ntop, ntop2) == 0)) |
| - break; |
| - } |
| - freeaddrinfo(aitop); |
| - /* If we reached the end of the list, the address was not there. */ |
| - if (ai == NULL) { |
| - /* Address not found for the host name. */ |
| - logit("Address %.100s maps to %.600s, but this does not " |
| - "map back to the address.", ntop, name); |
| - return strdup(ntop); |
| - } |
| - return strdup(name); |
| -} |
| - |
| -/* |
| - * Return the canonical name of the host in the other side of the current |
| - * connection. The host name is cached, so it is efficient to call this |
| - * several times. |
| - */ |
| - |
| -const char * |
| -auth_get_canonical_hostname(struct ssh *ssh, int use_dns) |
| -{ |
| - static char *dnsname; |
| - |
| - if (!use_dns) |
| - return ssh_remote_ipaddr(ssh); |
| - else if (dnsname != NULL) |
| - return dnsname; |
| - else { |
| - dnsname = remote_hostname(ssh); |
| - return dnsname; |
| - } |
| -} |
| --- a/canohost.c |
| +++ b/canohost.c |
| @@ -202,3 +202,117 @@ get_local_port(int sock) |
| { |
| return get_sock_port(sock, 1); |
| } |
| + |
| +/* |
| + * Returns the remote DNS hostname as a string. The returned string must not |
| + * be freed. NB. this will usually trigger a DNS query the first time it is |
| + * called. |
| + * This function does additional checks on the hostname to mitigate some |
| + * attacks on legacy rhosts-style authentication. |
| + * XXX is RhostsRSAAuthentication vulnerable to these? |
| + * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) |
| + */ |
| + |
| +static char * |
| +remote_hostname(struct ssh *ssh) |
| +{ |
| + struct sockaddr_storage from; |
| + socklen_t fromlen; |
| + struct addrinfo hints, *ai, *aitop; |
| + char name[NI_MAXHOST], ntop2[NI_MAXHOST]; |
| + const char *ntop = ssh_remote_ipaddr(ssh); |
| + |
| + /* Get IP address of client. */ |
| + fromlen = sizeof(from); |
| + memset(&from, 0, sizeof(from)); |
| + if (getpeername(ssh_packet_get_connection_in(ssh), |
| + (struct sockaddr *)&from, &fromlen) < 0) { |
| + debug("getpeername failed: %.100s", strerror(errno)); |
| + return strdup(ntop); |
| + } |
| + |
| + ipv64_normalise_mapped(&from, &fromlen); |
| + if (from.ss_family == AF_INET6) |
| + fromlen = sizeof(struct sockaddr_in6); |
| + |
| + debug3("Trying to reverse map address %.100s.", ntop); |
| + /* Map the IP address to a host name. */ |
| + if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), |
| + NULL, 0, NI_NAMEREQD) != 0) { |
| + /* Host name not found. Use ip address. */ |
| + return strdup(ntop); |
| + } |
| + |
| + /* |
| + * if reverse lookup result looks like a numeric hostname, |
| + * someone is trying to trick us by PTR record like following: |
| + * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 |
| + */ |
| + memset(&hints, 0, sizeof(hints)); |
| + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ |
| + hints.ai_flags = AI_NUMERICHOST; |
| + if (getaddrinfo(name, NULL, &hints, &ai) == 0) { |
| + logit("Nasty PTR record \"%s\" is set up for %s, ignoring", |
| + name, ntop); |
| + freeaddrinfo(ai); |
| + return strdup(ntop); |
| + } |
| + |
| + /* Names are stored in lowercase. */ |
| + lowercase(name); |
| + |
| + /* |
| + * Map it back to an IP address and check that the given |
| + * address actually is an address of this host. This is |
| + * necessary because anyone with access to a name server can |
| + * define arbitrary names for an IP address. Mapping from |
| + * name to IP address can be trusted better (but can still be |
| + * fooled if the intruder has access to the name server of |
| + * the domain). |
| + */ |
| + memset(&hints, 0, sizeof(hints)); |
| + hints.ai_family = from.ss_family; |
| + hints.ai_socktype = SOCK_STREAM; |
| + if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { |
| + logit("reverse mapping checking getaddrinfo for %.700s " |
| + "[%s] failed.", name, ntop); |
| + return strdup(ntop); |
| + } |
| + /* Look for the address from the list of addresses. */ |
| + for (ai = aitop; ai; ai = ai->ai_next) { |
| + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, |
| + sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && |
| + (strcmp(ntop, ntop2) == 0)) |
| + break; |
| + } |
| + freeaddrinfo(aitop); |
| + /* If we reached the end of the list, the address was not there. */ |
| + if (ai == NULL) { |
| + /* Address not found for the host name. */ |
| + logit("Address %.100s maps to %.600s, but this does not " |
| + "map back to the address.", ntop, name); |
| + return strdup(ntop); |
| + } |
| + return strdup(name); |
| +} |
| + |
| +/* |
| + * Return the canonical name of the host in the other side of the current |
| + * connection. The host name is cached, so it is efficient to call this |
| + * several times. |
| + */ |
| + |
| +const char * |
| +auth_get_canonical_hostname(struct ssh *ssh, int use_dns) |
| +{ |
| + static char *dnsname; |
| + |
| + if (!use_dns) |
| + return ssh_remote_ipaddr(ssh); |
| + else if (dnsname != NULL) |
| + return dnsname; |
| + else { |
| + dnsname = remote_hostname(ssh); |
| + return dnsname; |
| + } |
| +} |