From: Greg Hudson Date: Wed, 24 Dec 2008 16:51:33 +0000 (+0000) Subject: Add a new fallback host-to-realm heuristic to try the components of the X-Git-Tag: krb5-1.7-alpha1~138 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=2fd916940dbe98a2e7c000480979d5a37ef72265;p=krb5.git Add a new fallback host-to-realm heuristic to try the components of the hostname as domains. The heuristic is off by default and is controlled by the realm_try_domains variable under libdefaults. Based on a patch submitted by Mark Phalan from Sun. ticket: 6031 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@21588 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/README b/README index 251f7dcc8..ed8c77c3f 100644 --- a/README +++ b/README @@ -425,6 +425,10 @@ the following new or changed files: slave/kpropd_rpc.c slave/kproplog.c +and marked portions of the following files: + + lib/krb5/os/hst_realm.c + are subject to the following license: Copyright (c) 2004 Sun Microsystems, Inc. diff --git a/src/config-files/krb5.conf.M b/src/config-files/krb5.conf.M index 95a3f773a..1cfb1444e 100644 --- a/src/config-files/krb5.conf.M +++ b/src/config-files/krb5.conf.M @@ -201,6 +201,16 @@ realm of a host. The default is not to use these records. General flag controlling the use of DNS for Kerberos information. If both of the preceding options are specified, this option has no effect. +.IP realm_try_domains +Indicate whether a host's domain components should be used to +determine the Kerberos realm of the host. The value of this variable +is an integer: -1 means not to search, 0 means to try the host's +domain itself, 1 means to also try the domain's immediate parent, and +so forth. The library's usual mechanism for locating Kerberos realms +is used to determine whether a domain is a valid realm--which may +involve consulting DNS if dns_lookup_kdc is set. The default is not +to search domain components. + .IP extra_addresses This allows a computer to use multiple local addresses, in order to allow Kerberos to work in a network that uses NATs. The addresses should diff --git a/src/lib/krb5/os/hst_realm.c b/src/lib/krb5/os/hst_realm.c index 2150ab38a..36c0e4860 100644 --- a/src/lib/krb5/os/hst_realm.c +++ b/src/lib/krb5/os/hst_realm.c @@ -78,6 +78,10 @@ #include "fake-addrinfo.h" +static krb5_error_code +domain_heuristic(krb5_context context, const char *domain, + char **realm, int limit); + #ifdef KRB5_DNS_LOOKUP #include "dnsglue.h" @@ -334,7 +338,7 @@ krb5_error_code KRB5_CALLCONV krb5_get_fallback_host_realm(krb5_context context, krb5_data *hdata, char ***realmsp) { char **retrealms; - char *default_realm, *realm, *cp, *temp_realm; + char *realm, *cp; krb5_error_code retval; char local_host[MAXDNAME+1], host[MAXDNAME+1]; @@ -348,71 +352,71 @@ krb5_get_fallback_host_realm(krb5_context context, krb5_data *hdata, char ***rea krb5int_clean_hostname(context, host, local_host, sizeof local_host); - /* Scan hostname for DNS realm, and save as last-ditch realm - assumption. */ - cp = local_host; -#ifdef DEBUG_REFERRALS - printf(" local_host: %s\n",local_host); -#endif - realm = default_realm = (char *)NULL; - temp_realm = 0; - while (cp && !default_realm) { - if (*cp == '.') { - cp++; - if (default_realm == (char *)NULL) { - /* If nothing else works, use the host's domain */ - default_realm = cp; - } - } else { - cp = strchr(cp, '.'); - } - } -#ifdef DEBUG_REFERRALS - printf(" done finding DNS-based default realm: >%s<\n",default_realm); -#endif - + /* + * Try looking up a _kerberos. TXT record in DNS. This + * heuristic is turned off by default since, in the absence of + * secure DNS, it can allow an attacker to control the realm used + * for a host. + */ + realm = (char *)NULL; #ifdef KRB5_DNS_LOOKUP - if (realm == (char *)NULL) { - int use_dns = _krb5_use_dns_realm(context); - if ( use_dns ) { - /* - * Since this didn't appear in our config file, try looking - * it up via DNS. Look for a TXT records of the form: - * - * _kerberos. - * - */ - cp = local_host; - do { - retval = krb5_try_realm_txt_rr("_kerberos", cp, &realm); - cp = strchr(cp,'.'); - if (cp) - cp++; - } while (retval && cp && cp[0]); - } + if (_krb5_use_dns_realm(context)) { + cp = local_host; + do { + retval = krb5_try_realm_txt_rr("_kerberos", cp, &realm); + cp = strchr(cp,'.'); + if (cp) + cp++; + } while (retval && cp && cp[0]); } #endif /* KRB5_DNS_LOOKUP */ - + /* + * Next try searching the domain components as realms. This + * heuristic is also turned off by default. If DNS lookups for + * KDCs are enabled (as they are by default), an attacker could + * control which domain component is used as the realm for a host. + */ if (realm == (char *)NULL) { - if (default_realm != (char *)NULL) { - /* We are defaulting to the realm of the host */ - if (!(cp = strdup(default_realm))) - return ENOMEM; - realm = cp; + int limit; + errcode_t code; + + code = profile_get_integer(context->profile, "libdefaults", + "realm_try_domains", 0, -1, &limit); + if (code == 0) { + retval = domain_heuristic(context, local_host, &realm, limit); + if (retval) + return retval; + } + } - /* Assume the realm name is upper case */ + /* + * The next fallback--and the first one to apply with default + * configuration--is to use the upper-cased parent domain of the + * hostname, regardless of whether we can actually look it up as a + * realm. + */ + if (realm == (char *)NULL) { + cp = strchr(local_host, '.'); + if (cp) { + if (!(realm = strdup(cp + 1))) + return ENOMEM; for (cp = realm; *cp; cp++) if (islower((int) (*cp))) *cp = toupper((int) *cp); - } else { - /* We are defaulting to the local realm */ - retval = krb5_get_default_realm(context, &realm); - if (retval) { - return retval; - } - } + } + } + + /* + * The final fallback--used when the fully-qualified hostname has + * only one component--is to use the local default realm. + */ + if (realm == (char *)NULL) { + retval = krb5_get_default_realm(context, &realm); + if (retval) + return retval; } + if (!(retrealms = (char **)calloc(2, sizeof(*retrealms)))) { if (realm != (char *)NULL) free(realm); @@ -488,3 +492,70 @@ krb5int_clean_hostname(krb5_context context, const char *host, char *local_host, #endif return 0; } + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Walk through the components of a domain. At each stage determine + * if a KDC can be located for that domain. Return a realm + * corresponding to the upper-cased domain name for which a KDC was + * found or NULL if no KDC was found. Stop searching after limit + * labels have been removed from the domain (-1 means don't search at + * all, 0 means try only the full domain itself, 1 means also try the + * parent domain, etc.) or when we reach a parent with only one label. + */ +static krb5_error_code +domain_heuristic(krb5_context context, const char *domain, + char **realm, int limit) +{ + krb5_error_code retval = 0, r; + struct addrlist alist; + krb5_data drealm; + char *cp = NULL; + char *fqdn = NULL; + + *realm = NULL; + if (limit < 0) + return 0; + + memset(&drealm, 0, sizeof (drealm)); + if (!(fqdn = strdup(domain))) { + retval = ENOMEM; + goto cleanup; + } + + /* Upper case the domain (for use as a realm) */ + for (cp = fqdn; *cp; cp++) + if (islower((int)(*cp))) + *cp = toupper((int)*cp); + + /* Search up to limit parents, as long as we have multiple labels. */ + cp = fqdn; + while (limit-- >= 0 && strchr(cp, '.') != NULL) { + + drealm.length = strlen(cp); + drealm.data = cp; + + /* Find a kdc based on this part of the domain name. */ + r = krb5_locate_kdc(context, &drealm, &alist, 0, SOCK_DGRAM, 0); + if (!r) { /* Found a KDC! */ + krb5int_free_addrlist(&alist); + if (!(*realm = strdup(cp))) { + retval = ENOMEM; + goto cleanup; + } + break; + } + + cp = strchr(cp, '.'); + cp++; + } + +cleanup: + if (fqdn) + free(fqdn); + return retval; +}