1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * lib/krb5/os/hst_realm.c
5 * Copyright 1990,1991,2002,2008,2009 by the Massachusetts Institute of Technology.
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
28 * krb5_get_host_realm()
29 * krb5_get_fallback_host_realm()
30 * krb5int_clean_hostname()
31 * krb5_free_host_realm()
36 Figures out the Kerberos realm names for host, filling in a
37 pointer to an argv[] style list of names, terminated with a null pointer.
39 If host is NULL, the local host's realms are determined.
41 If there are no known realms for the host, the filled-in pointer is set
44 The pointer array and strings pointed to are all in allocated storage,
45 and should be freed by the caller when finished.
51 * Implementation notes:
53 * this implementation only provides one realm per host, using the same
54 * mapping file used in kerberos v4.
56 * Given a fully-qualified domain-style primary host name,
57 * return the name of the Kerberos realm for the host.
58 * If the hostname contains no discernable domain, or an error occurs,
59 * return the local realm name, as supplied by krb5_get_default_realm().
60 * If the hostname contains a domain, but no translation is found,
61 * the hostname's domain is converted to upper-case and returned.
63 * The format of each line of the translation file is:
64 * domain_name kerberos_realm
66 * host_name kerberos_realm
68 * domain_name should be of the form .XXX.YYY (e.g. .LCS.MIT.EDU)
69 * host names should be in the usual form (e.g. FOO.BAR.BAZ)
83 #include "fake-addrinfo.h"
85 static krb5_error_code
86 domain_heuristic(krb5_context context, const char *domain,
87 char **realm, int limit);
89 #ifdef KRB5_DNS_LOOKUP
93 #define MAXDNAME (16 * MAXHOSTNAMELEN)
95 #endif /* KRB5_DNS_LOOKUP */
97 krb5_error_code krb5int_translate_gai_error (int);
99 static krb5_error_code
100 get_fq_hostname(char *buf, size_t bufsize, const char *name)
102 struct addrinfo *ai, hints;
105 memset (&hints, 0, sizeof (hints));
106 hints.ai_flags = AI_CANONNAME;
107 err = getaddrinfo (name, 0, &hints, &ai);
109 return krb5int_translate_gai_error (err);
110 if (ai->ai_canonname == 0)
111 return KRB5_EAI_FAIL;
112 strncpy (buf, ai->ai_canonname, bufsize);
118 /* Get the local host name, try to make it fully-qualified.
119 Always return a null-terminated string.
120 Might return an error if gethostname fails. */
122 krb5int_get_fq_local_hostname(char *buf, size_t bufsiz)
125 if (gethostname (buf, bufsiz) == -1)
128 return get_fq_hostname (buf, bufsiz, buf);
131 krb5_error_code KRB5_CALLCONV
132 krb5_get_host_realm(krb5_context context, const char *host, char ***realmsp)
135 char *realm, *cp, *temp_realm;
136 krb5_error_code retval;
137 char local_host[MAXDNAME+1];
139 #ifdef DEBUG_REFERRALS
140 printf("get_host_realm(host:%s) called\n",host);
143 retval = krb5int_clean_hostname(context, host, local_host, sizeof local_host);
148 Search for the best match for the host or domain.
149 Example: Given a host a.b.c.d, try to match on:
160 #ifdef DEBUG_REFERRALS
161 printf(" local_host: %s\n",local_host);
163 realm = (char *)NULL;
166 #ifdef DEBUG_REFERRALS
167 printf(" trying to look up %s in the domain_realm map\n",cp);
169 retval = profile_get_string(context->profile, KRB5_CONF_DOMAIN_REALM, cp,
170 0, (char *)NULL, &temp_realm);
173 if (temp_realm != (char *)NULL)
174 break; /* Match found */
176 /* Setup for another test */
180 cp = strchr(cp, '.');
183 #ifdef DEBUG_REFERRALS
184 printf(" done searching the domain_realm map\n");
187 #ifdef DEBUG_REFERRALS
188 printf(" temp_realm is %s\n",temp_realm);
190 realm = strdup(temp_realm);
192 profile_release_string(temp_realm);
195 profile_release_string(temp_realm);
198 if (realm == (char *)NULL) {
199 if (!(cp = strdup(KRB5_REFERRAL_REALM)))
204 if (!(retrealms = (char **)calloc(2, sizeof(*retrealms)))) {
205 if (realm != (char *)NULL)
210 retrealms[0] = realm;
213 *realmsp = retrealms;
217 #if defined(_WIN32) && !defined(__CYGWIN32__)
218 # ifndef EAFNOSUPPORT
219 # define EAFNOSUPPORT WSAEAFNOSUPPORT
224 krb5int_translate_gai_error (int num)
227 #ifdef EAI_ADDRFAMILY
236 return KRB5_EAI_FAIL;
241 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
243 return KRB5_EAI_NODATA;
246 return KRB5_EAI_NONAME;
247 #if defined(EAI_OVERFLOW)
249 return EINVAL; /* XXX */
252 return KRB5_EAI_SERVICE;
266 * Ganked from krb5_get_host_realm; handles determining a fallback realm
267 * to try in the case where referrals have failed and it's time to go
268 * look at TXT records or make a DNS-based assumption.
271 krb5_error_code KRB5_CALLCONV
272 krb5_get_fallback_host_realm(krb5_context context,
273 krb5_data *hdata, char ***realmsp)
277 krb5_error_code retval;
278 char local_host[MAXDNAME+1], host[MAXDNAME+1];
280 /* Convert what we hope is a hostname to a string. */
281 memcpy(host, hdata->data, hdata->length);
282 host[hdata->length]=0;
284 #ifdef DEBUG_REFERRALS
285 printf("get_fallback_host_realm(host >%s<) called\n",host);
288 retval = krb5int_clean_hostname(context, host, local_host, sizeof local_host);
293 * Try looking up a _kerberos.<hostname> TXT record in DNS. This
294 * heuristic is turned off by default since, in the absence of
295 * secure DNS, it can allow an attacker to control the realm used
298 realm = (char *)NULL;
299 #ifdef KRB5_DNS_LOOKUP
300 if (_krb5_use_dns_realm(context)) {
303 retval = krb5_try_realm_txt_rr("_kerberos", cp, &realm);
307 } while (retval && cp && cp[0]);
309 #endif /* KRB5_DNS_LOOKUP */
312 * Next try searching the domain components as realms. This
313 * heuristic is also turned off by default. If DNS lookups for
314 * KDCs are enabled (as they are by default), an attacker could
315 * control which domain component is used as the realm for a host.
317 if (realm == (char *)NULL) {
321 code = profile_get_integer(context->profile, KRB5_CONF_LIBDEFAULTS,
322 KRB5_CONF_REALM_TRY_DOMAINS, 0, -1, &limit);
324 retval = domain_heuristic(context, local_host, &realm, limit);
331 * The next fallback--and the first one to apply with default
332 * configuration--is to use the upper-cased parent domain of the
333 * hostname, regardless of whether we can actually look it up as a
336 if (realm == (char *)NULL) {
337 cp = strchr(local_host, '.');
339 if (!(realm = strdup(cp + 1)))
341 for (cp = realm; *cp; cp++)
342 if (islower((int) (*cp)))
343 *cp = toupper((int) *cp);
348 * The final fallback--used when the fully-qualified hostname has
349 * only one component--is to use the local default realm.
351 if (realm == (char *)NULL) {
352 retval = krb5_get_default_realm(context, &realm);
357 if (!(retrealms = (char **)calloc(2, sizeof(*retrealms)))) {
358 if (realm != (char *)NULL)
363 retrealms[0] = realm;
366 *realmsp = retrealms;
371 * Common code for krb5_get_host_realm and krb5_get_fallback_host_realm
372 * to do basic sanity checks on supplied hostname.
375 krb5int_clean_hostname(krb5_context context,
376 const char *host, char *local_host, size_t lhsize)
379 krb5_error_code retval;
383 #ifdef DEBUG_REFERRALS
384 printf("krb5int_clean_hostname called: host<%s>, local_host<%s>, size %d\n",host,local_host,lhsize);
387 /* Filter out numeric addresses if the caller utterly failed to
388 convert them to names. */
389 /* IPv4 - dotted quads only */
390 if (strspn(host, "01234567890.") == strlen(host)) {
391 /* All numbers and dots... if it's three dots, it's an
392 IP address, and we reject it. But "12345" could be
393 a local hostname, couldn't it? We'll just assume
394 that a name with three dots is not meant to be an
395 all-numeric hostname three all-numeric domains down
396 from the current domain. */
399 for (p = host; *p; p++)
403 return KRB5_ERR_NUMERIC_REALM;
405 if (strchr(host, ':'))
406 /* IPv6 numeric address form? Bye bye. */
407 return KRB5_ERR_NUMERIC_REALM;
409 /* Should probably error out if strlen(host) > MAXDNAME. */
410 strncpy(local_host, host, lhsize);
411 local_host[lhsize - 1] = '\0';
413 retval = krb5int_get_fq_local_hostname (local_host, lhsize);
418 /* fold to lowercase */
419 for (cp = local_host; *cp; cp++) {
420 if (isupper((unsigned char) (*cp)))
421 *cp = tolower((unsigned char) *cp);
423 l = strlen(local_host);
424 /* strip off trailing dot */
425 if (l && local_host[l-1] == '.')
428 #ifdef DEBUG_REFERRALS
429 printf("krb5int_clean_hostname ending: host<%s>, local_host<%s>, size %d\n",host,local_host,lhsize);
435 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
436 * Use is subject to license terms.
440 * Walk through the components of a domain. At each stage determine
441 * if a KDC can be located for that domain. Return a realm
442 * corresponding to the upper-cased domain name for which a KDC was
443 * found or NULL if no KDC was found. Stop searching after limit
444 * labels have been removed from the domain (-1 means don't search at
445 * all, 0 means try only the full domain itself, 1 means also try the
446 * parent domain, etc.) or when we reach a parent with only one label.
448 static krb5_error_code
449 domain_heuristic(krb5_context context, const char *domain,
450 char **realm, int limit)
452 krb5_error_code retval = 0, r;
453 struct addrlist alist;
455 char *cp = NULL, *fqdn, *dot;
461 memset(&drealm, 0, sizeof (drealm));
462 fqdn = strdup(domain);
468 /* Upper case the domain (for use as a realm) */
469 for (cp = fqdn; *cp; cp++) {
470 if (islower((int)(*cp)))
471 *cp = toupper((int)*cp);
474 /* Search up to limit parents, as long as we have multiple labels. */
476 while (limit-- >= 0 && (dot = strchr(cp, '.')) != NULL) {
478 drealm.length = strlen(cp);
481 /* Find a kdc based on this part of the domain name. */
482 r = krb5_locate_kdc(context, &drealm, &alist, 0, SOCK_DGRAM, 0);
483 if (!r) { /* Found a KDC! */
484 krb5int_free_addrlist(&alist);
502 * Frees the storage taken by a realm list returned by krb5_get_host_realm.
505 krb5_error_code KRB5_CALLCONV
506 krb5_free_host_realm(krb5_context context, char *const *realmlist)
508 /* same format, so why duplicate code? */
509 return krb5_free_krbhst(context, realmlist);