1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/hostrealm.c - realm-of-host and default-realm APIs */
4 * Copyright (C) 2013 by the Massachusetts Institute of Technology.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include "fake-addrinfo.h"
36 #include <krb5/hostrealm_plugin.h>
39 #if defined(_WIN32) && !defined(__CYGWIN32__)
41 #define EAFNOSUPPORT WSAEAFNOSUPPORT
45 struct hostrealm_module_handle {
46 struct krb5_hostrealm_vtable_st vt;
47 krb5_hostrealm_moddata data;
50 /* Release a list of hostrealm module handles. */
52 free_handles(krb5_context context, struct hostrealm_module_handle **handles)
54 struct hostrealm_module_handle *h, **hp;
58 for (hp = handles; *hp != NULL; hp++) {
60 if (h->vt.fini != NULL)
61 h->vt.fini(context, h->data);
67 /* Get the registered hostrealm modules including all built-in modules, in the
69 static krb5_error_code
70 get_modules(krb5_context context, krb5_plugin_initvt_fn **modules_out)
73 const int intf = PLUGIN_INTERFACE_HOSTREALM;
77 /* Register built-in modules. */
78 ret = k5_plugin_register(context, intf, "registry",
79 hostrealm_registry_initvt);
82 ret = k5_plugin_register(context, intf, "profile",
83 hostrealm_profile_initvt);
86 ret = k5_plugin_register(context, intf, "dns", hostrealm_dns_initvt);
89 ret = k5_plugin_register(context, intf, "domain", hostrealm_domain_initvt);
93 return k5_plugin_load_all(context, intf, modules_out);
96 /* Initialize context->hostrealm_handles with a list of module handles. */
97 static krb5_error_code
98 load_hostrealm_modules(krb5_context context)
101 struct hostrealm_module_handle **list = NULL, *handle;
102 krb5_plugin_initvt_fn *modules = NULL, *mod;
105 ret = get_modules(context, &modules);
109 /* Allocate a large enough list of handles. */
110 for (count = 0; modules[count] != NULL; count++);
111 list = k5alloc((count + 1) * sizeof(*list), &ret);
115 /* Initialize each module, ignoring ones that fail. */
117 for (mod = modules; *mod != NULL; mod++) {
118 handle = k5alloc(sizeof(*handle), &ret);
121 ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&handle->vt);
123 TRACE_HOSTREALM_VTINIT_FAIL(context, ret);
129 if (handle->vt.init != NULL) {
130 ret = handle->vt.init(context, &handle->data);
132 TRACE_HOSTREALM_INIT_FAIL(context, handle->vt.name, ret);
137 list[count++] = handle;
143 context->hostrealm_handles = list;
147 k5_plugin_free_modules(context, modules);
148 free_handles(context, list);
152 /* Invoke a module's host_realm method, if it has one. */
153 static krb5_error_code
154 host_realm(krb5_context context, struct hostrealm_module_handle *h,
155 const char *host, char ***realms_out)
157 if (h->vt.host_realm == NULL)
158 return KRB5_PLUGIN_NO_HANDLE;
159 return h->vt.host_realm(context, h->data, host, realms_out);
162 /* Invoke a module's fallback_realm method, if it has one. */
163 static krb5_error_code
164 fallback_realm(krb5_context context, struct hostrealm_module_handle *h,
165 const char *host, char ***realms_out)
167 if (h->vt.fallback_realm == NULL)
168 return KRB5_PLUGIN_NO_HANDLE;
169 return h->vt.fallback_realm(context, h->data, host, realms_out);
172 /* Invoke a module's default_realm method, if it has one. */
173 static krb5_error_code
174 default_realm(krb5_context context, struct hostrealm_module_handle *h,
177 if (h->vt.default_realm == NULL)
178 return KRB5_PLUGIN_NO_HANDLE;
179 return h->vt.default_realm(context, h->data, realms_out);
182 /* Invoke a module's free_list method. */
184 free_list(krb5_context context, struct hostrealm_module_handle *h,
187 h->vt.free_list(context, h->data, list);
190 /* Copy a null-terminated list of strings. */
191 static krb5_error_code
192 copy_list(char **in, char ***out)
198 for (count = 0; in[count] != NULL; count++);
199 list = calloc(count + 1, sizeof(*list));
202 for (i = 0; i < count; i++) {
203 list[i] = strdup(in[i]);
204 if (list[i] == NULL) {
205 krb5_free_host_realm(NULL, list);
213 static krb5_error_code
214 translate_gai_error(int num)
217 #ifdef EAI_ADDRFAMILY
226 return KRB5_EAI_FAIL;
231 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
233 return KRB5_EAI_NODATA;
236 return KRB5_EAI_NONAME;
237 #if defined(EAI_OVERFLOW)
239 return EINVAL; /* XXX */
242 return KRB5_EAI_SERVICE;
254 /* Get the canonical form of the local host name, using forward
255 * canonicalization only. */
257 krb5int_get_fq_local_hostname(char **hostname_out)
259 struct addrinfo *ai, hints;
260 char buf[MAXHOSTNAMELEN];
263 *hostname_out = NULL;
265 if (gethostname(buf, sizeof(buf)) == -1)
268 memset(&hints, 0, sizeof(hints));
269 hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
270 err = getaddrinfo(buf, NULL, &hints, &ai);
272 return translate_gai_error(err);
273 if (ai->ai_canonname == NULL) {
275 return KRB5_EAI_FAIL;
277 *hostname_out = strdup(ai->ai_canonname);
279 return (*hostname_out == NULL) ? ENOMEM : 0;
282 static krb5_error_code
283 clean_hostname(krb5_context context, const char *host, char **cleanname_out)
289 *cleanname_out = NULL;
292 cleanname = strdup(host);
293 if (cleanname == NULL)
296 ret = krb5int_get_fq_local_hostname(&cleanname);
301 /* Fold to lowercase. */
302 for (p = cleanname; *p; p++) {
303 if (isupper((unsigned char)*p))
304 *p = tolower((unsigned char)*p);
307 /* Strip off trailing dot. */
308 l = strlen(cleanname);
309 if (l > 0 && cleanname[l - 1] == '.')
310 cleanname[l - 1] = '\0';
312 *cleanname_out = cleanname;
316 /* Return true if name appears to be an IPv4 or IPv6 address. */
318 k5_is_numeric_address(const char *name)
323 /* If name contains only numbers and three dots, consider it to be an IPv4
325 if (strspn(name, "01234567890.") == strlen(name)) {
326 for (p = name; *p; p++) {
334 /* If name contains a colon, consider it to be an IPv6 address. */
335 if (strchr(name, ':') != NULL)
341 /* Construct a one-element realm list containing a copy of realm. */
343 k5_make_realmlist(const char *realm, char ***realms_out)
348 realms = calloc(2, sizeof(*realms));
351 realms[0] = strdup(realm);
352 if (realms[0] == NULL) {
356 *realms_out = realms;
360 krb5_error_code KRB5_CALLCONV
361 krb5_get_host_realm(krb5_context context, const char *host, char ***realms_out)
364 struct hostrealm_module_handle **hp;
365 char **realms, *cleanname = NULL;
369 if (context->hostrealm_handles == NULL) {
370 ret = load_hostrealm_modules(context);
375 ret = clean_hostname(context, host, &cleanname);
379 /* Give each module a chance to determine the host's realms. */
380 for (hp = context->hostrealm_handles; *hp != NULL; hp++) {
381 ret = host_realm(context, *hp, cleanname, &realms);
383 ret = copy_list(realms, realms_out);
384 free_list(context, *hp, realms);
386 } else if (ret != KRB5_PLUGIN_NO_HANDLE) {
391 /* Return a list containing the "referral realm" (an empty realm), as a
392 * cue to try referrals. */
393 ret = k5_make_realmlist(KRB5_REFERRAL_REALM, realms_out);
400 krb5_error_code KRB5_CALLCONV
401 krb5_get_fallback_host_realm(krb5_context context, krb5_data *hdata,
405 struct hostrealm_module_handle **hp;
406 char **realms, *defrealm, *host, *cleanname = NULL;
410 /* Convert hdata into a string and clean it. */
411 host = k5memdup0(hdata->data, hdata->length, &ret);
414 ret = clean_hostname(context, host, &cleanname);
419 if (context->hostrealm_handles == NULL) {
420 ret = load_hostrealm_modules(context);
425 /* Give each module a chance to determine the fallback realms. */
426 for (hp = context->hostrealm_handles; *hp != NULL; hp++) {
427 ret = fallback_realm(context, *hp, cleanname, &realms);
429 ret = copy_list(realms, realms_out);
430 free_list(context, *hp, realms);
432 } else if (ret != KRB5_PLUGIN_NO_HANDLE) {
437 /* Return a list containing the default realm. */
438 ret = krb5_get_default_realm(context, &defrealm);
441 ret = k5_make_realmlist(defrealm, realms_out);
442 krb5_free_default_realm(context, defrealm);
449 krb5_error_code KRB5_CALLCONV
450 krb5_free_host_realm(krb5_context context, char *const *list)
454 for (p = list; p != NULL && *p != NULL; p++)
460 /* Get the system default realm using hostrealm modules. */
461 static krb5_error_code
462 get_default_realm(krb5_context context, char **realm_out)
465 struct hostrealm_module_handle **hp;
469 if (context->hostrealm_handles == NULL) {
470 ret = load_hostrealm_modules(context);
475 /* Give each module a chance to determine the default realm. */
476 for (hp = context->hostrealm_handles; *hp != NULL; hp++) {
477 ret = default_realm(context, *hp, &realms);
479 if (*realms == NULL) {
480 ret = KRB5_CONFIG_NODEFREALM;
482 *realm_out = strdup(realms[0]);
483 if (*realm_out == NULL)
486 free_list(context, *hp, realms);
488 } else if (ret != KRB5_PLUGIN_NO_HANDLE) {
493 return KRB5_CONFIG_NODEFREALM;
496 krb5_error_code KRB5_CALLCONV
497 krb5_get_default_realm(krb5_context context, char **realm_out)
503 if (context == NULL || context->magic != KV5M_CONTEXT)
506 if (context->default_realm == NULL) {
507 ret = get_default_realm(context, &context->default_realm);
511 *realm_out = strdup(context->default_realm);
512 return (*realm_out == NULL) ? ENOMEM : 0;
515 krb5_error_code KRB5_CALLCONV
516 krb5_set_default_realm(krb5_context context, const char *realm)
518 if (context == NULL || context->magic != KV5M_CONTEXT)
521 if (context->default_realm != NULL) {
522 free(context->default_realm);
523 context->default_realm = NULL;
526 /* Allow the caller to clear the default realm setting by passing NULL. */
528 context->default_realm = strdup(realm);
529 if (context->default_realm == NULL)
537 krb5_free_default_realm(krb5_context context, char *realm)
543 k5_hostrealm_free_context(krb5_context context)
545 free_handles(context, context->hostrealm_handles);
546 context->hostrealm_handles = NULL;