/* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* * lib/krb5/os/locate_kdc.c * * Copyright 1990,2000,2001,2002,2003,2004,2006 Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * * get socket addresses for KDC. */ /* * Solaris Kerberos * Re-factored the following routines to get a clear separation of locating * KDC entries (krb5.conf/DNS-SRVrecs) versus mapping them to net addresses * to allow us to output better error msgs: * krb5int_locate_server * prof_locate_server * dns_locate_server * krb5_locate_srv_conf_1 (removed) * krb5_locate_srv_dns_1 (removed) * prof_hostnames2netaddrs (new) * hostlist2str (new) * dns_hostnames2netaddrs (new) * dnslist2str (new) * Also, for the profile get_master==1 case, the algorithm has been * simplified to just do a profile_get_values on "admin_server" and * not try to match those against "kdc" entries (does not seem necessary * and the DNS-SRVrecs code does not do that). */ #include "fake-addrinfo.h" #include "k5-int.h" #include "os-proto.h" #include #ifdef KRB5_DNS_LOOKUP #ifdef WSHELPER #include #else /* WSHELPER */ #include #include #include #include #include #endif /* WSHELPER */ #ifndef T_SRV #define T_SRV 33 #endif /* T_SRV */ #include #include #if USE_DLOPEN #include #endif /* for old Unixes and friends ... */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1) /* Solaris Kerberos: default to dns lookup for the KDC but not the realm */ #define DEFAULT_LOOKUP_KDC 1 #define DEFAULT_LOOKUP_REALM 0 static int maybe_use_dns (krb5_context context, const char *name, int def_val) { krb5_error_code code; char * value = NULL; int use_dns = 0; code = profile_get_string(context->profile, "libdefaults", name, 0, 0, &value); if (value == 0 && code == 0) code = profile_get_string(context->profile, "libdefaults", "dns_fallback", 0, 0, &value); if (code) return def_val; if (value == 0) return def_val; use_dns = _krb5_conf_boolean(value); profile_release_string(value); return use_dns; } int _krb5_use_dns_kdc(krb5_context context) { return maybe_use_dns (context, "dns_lookup_kdc", DEFAULT_LOOKUP_KDC); } int _krb5_use_dns_realm(krb5_context context) { return maybe_use_dns (context, "dns_lookup_realm", DEFAULT_LOOKUP_REALM); } #endif /* KRB5_DNS_LOOKUP */ int krb5int_grow_addrlist (struct addrlist *lp, int nmore) { int i; int newspace = lp->space + nmore; size_t newsize = newspace * sizeof (*lp->addrs); void *newaddrs; newaddrs = realloc (lp->addrs, newsize); if (newaddrs == NULL) return errno; lp->addrs = newaddrs; for (i = lp->space; i < newspace; i++) { lp->addrs[i].ai = NULL; lp->addrs[i].freefn = NULL; lp->addrs[i].data = NULL; } lp->space = newspace; return 0; } #define grow_list krb5int_grow_addrlist /* Free up everything pointed to by the addrlist structure, but don't free the structure itself. */ void krb5int_free_addrlist (struct addrlist *lp) { int i; for (i = 0; i < lp->naddrs; i++) if (lp->addrs[i].freefn) (lp->addrs[i].freefn)(lp->addrs[i].data); free (lp->addrs); lp->addrs = NULL; lp->naddrs = lp->space = 0; } #define free_list krb5int_free_addrlist static int translate_ai_error (int err) { switch (err) { case 0: return 0; case EAI_BADFLAGS: case EAI_FAMILY: case EAI_SOCKTYPE: case EAI_SERVICE: /* All of these indicate bad inputs to getaddrinfo. */ return EINVAL; case EAI_AGAIN: /* Translate to standard errno code. */ return EAGAIN; case EAI_MEMORY: /* Translate to standard errno code. */ return ENOMEM; #ifdef EAI_ADDRFAMILY case EAI_ADDRFAMILY: #endif #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME case EAI_NODATA: #endif case EAI_NONAME: /* Name not known or no address data, but no error. Do nothing more. */ return 0; #ifdef EAI_OVERFLOW case EAI_OVERFLOW: /* An argument buffer overflowed. */ return EINVAL; /* XXX */ #endif #ifdef EAI_SYSTEM case EAI_SYSTEM: /* System error, obviously. */ return errno; #endif default: /* An error code we haven't handled? */ return EINVAL; } } /* Solaris Kerberos: want dbg messages to syslog */ #include static inline void Tprintf(const char *fmt, ...) { #ifdef TEST va_list ap; char err_str[2048]; va_start(ap, fmt); vsnprintf(err_str, sizeof (err_str), fmt, args); syslog(LOG_DEBUG, err_str); va_end(ap); #endif } #if 0 extern void krb5int_debug_fprint(const char *, ...); #define dprint krb5int_debug_fprint #define print_addrlist krb5int_print_addrlist extern void print_addrlist (const struct addrlist *a); #else static inline void dprint(const char *fmt, ...) { } static inline void print_addrlist(const struct addrlist *a) { } #endif static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a, void (*freefn)(void *), void *data) { int err; dprint("\tadding %p=%A to %p (naddrs=%d space=%d)\n", a, a, lp, lp->naddrs, lp->space); if (lp->naddrs == lp->space) { err = grow_list (lp, 1); if (err) { Tprintf ("grow_list failed %d\n", err); return err; } } Tprintf("setting element %d\n", lp->naddrs); lp->addrs[lp->naddrs].ai = a; lp->addrs[lp->naddrs].freefn = freefn; lp->addrs[lp->naddrs].data = data; lp->naddrs++; Tprintf ("\tcount is now %d: ", lp->naddrs); print_addrlist(lp); Tprintf("\n"); return 0; } #define add_host_to_list krb5int_add_host_to_list static void call_freeaddrinfo(void *data) { /* Strict interpretation of the C standard says we can't assume that the ABI for f(void*) and f(struct foo *) will be compatible. Use this stub just to be paranoid. */ freeaddrinfo(data); } int krb5int_add_host_to_list (struct addrlist *lp, const char *hostname, int port, int secport, int socktype, int family) { struct addrinfo *addrs, *a, *anext, hint; int err; char portbuf[10], secportbuf[10]; void (*freefn)(void *); Tprintf ("adding hostname %s, ports %d,%d, family %d, socktype %d\n", hostname, ntohs (port), ntohs (secport), family, socktype); memset(&hint, 0, sizeof(hint)); hint.ai_family = family; hint.ai_socktype = socktype; #ifdef AI_NUMERICSERV hint.ai_flags = AI_NUMERICSERV; #endif sprintf(portbuf, "%d", ntohs(port)); sprintf(secportbuf, "%d", ntohs(secport)); err = getaddrinfo (hostname, portbuf, &hint, &addrs); if (err) { Tprintf ("\tgetaddrinfo(\"%s\", \"%s\", ...)\n\treturns %d: %s\n", hostname, portbuf, err, gai_strerror (err)); return translate_ai_error (err); } freefn = call_freeaddrinfo; anext = 0; for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) { anext = a->ai_next; err = add_addrinfo_to_list (lp, a, freefn, a); } if (err || secport == 0) goto egress; if (socktype == 0) socktype = SOCK_DGRAM; else if (socktype != SOCK_DGRAM) goto egress; hint.ai_family = AF_INET; err = getaddrinfo (hostname, secportbuf, &hint, &addrs); if (err) { err = translate_ai_error (err); goto egress; } freefn = call_freeaddrinfo; for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) { anext = a->ai_next; err = add_addrinfo_to_list (lp, a, freefn, a); } egress: /* Solaris Kerberos */ if (anext) freeaddrinfo (anext); return err; } #include #if TARGET_OS_MAC static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/libkrb5", NULL }; /* should be a list */ #else static const char *objdirs[] = { LIBDIR "/krb5/plugins/libkrb5", NULL }; #endif struct module_callback_data { int out_of_mem; struct addrlist *lp; }; static int module_callback (void *cbdata, int socktype, struct sockaddr *sa) { struct module_callback_data *d = cbdata; struct { struct addrinfo ai; union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u; } *x; if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM) return 0; if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) return 0; x = malloc (sizeof (*x)); if (x == 0) { d->out_of_mem = 1; return 1; } memset(x, 0, sizeof (*x)); x->ai.ai_addr = (struct sockaddr *) &x->u; x->ai.ai_socktype = socktype; x->ai.ai_family = sa->sa_family; if (sa->sa_family == AF_INET) { x->u.sin = *(struct sockaddr_in *)sa; x->ai.ai_addrlen = sizeof(struct sockaddr_in); } if (sa->sa_family == AF_INET6) { x->u.sin6 = *(struct sockaddr_in6 *)sa; x->ai.ai_addrlen = sizeof(struct sockaddr_in6); } if (add_addrinfo_to_list (d->lp, &x->ai, free, x) != 0) { /* Assumes only error is ENOMEM. */ d->out_of_mem = 1; return 1; } return 0; } static krb5_error_code module_locate_server (krb5_context ctx, const krb5_data *realm, struct addrlist *addrlist, enum locate_service_type svc, int socktype, int family) { struct krb5plugin_service_locate_result *res = NULL; krb5_error_code code; struct krb5plugin_service_locate_ftable *vtbl = NULL; void **ptrs; int i; struct module_callback_data cbdata = { 0, }; Tprintf("in module_locate_server\n"); cbdata.lp = addrlist; if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) { code = krb5int_open_plugin_dirs (objdirs, NULL, &ctx->libkrb5_plugins, &ctx->err); if (code) return KRB5_PLUGIN_NO_HANDLE; } code = krb5int_get_plugin_dir_data (&ctx->libkrb5_plugins, "service_locator", &ptrs, &ctx->err); if (code) { Tprintf("error looking up plugin symbols: %s\n", krb5_get_error_message(ctx, code)); return KRB5_PLUGIN_NO_HANDLE; } for (i = 0; ptrs[i]; i++) { void *blob; vtbl = ptrs[i]; Tprintf("element %d is %p\n", i, ptrs[i]); /* For now, don't keep the plugin data alive. For long-lived contexts, it may be desirable to change that later. */ code = vtbl->init(ctx, &blob); if (code) continue; code = vtbl->lookup(blob, svc, realm->data, socktype, family, module_callback, &cbdata); vtbl->fini(blob); if (code == KRB5_PLUGIN_NO_HANDLE) { /* Module passes, keep going. */ /* XXX */ Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)\n"); continue; } if (code != 0) { /* Module encountered an actual error. */ Tprintf("plugin lookup routine returned error %d: %s\n", code, error_message(code)); krb5int_free_plugin_dir_data (ptrs); return code; } break; } if (ptrs[i] == NULL) { Tprintf("ran off end of plugin list\n"); krb5int_free_plugin_dir_data (ptrs); return KRB5_PLUGIN_NO_HANDLE; } Tprintf("stopped with plugin #%d, res=%p\n", i, res); /* Got something back, yippee. */ Tprintf("now have %d addrs in list %p\n", addrlist->naddrs, addrlist); print_addrlist(addrlist); krb5int_free_plugin_dir_data (ptrs); return 0; } /* XXX - move to locate_plugin.h? */ typedef krb5_error_code (*krb5_lookup_func)( void *, enum locate_service_type svc, const char *realm, int socktype, int family, int (*cbfunc)(void *,int,struct sockaddr *), void *cbdata); /* * Solaris Kerberos (illumos) * * Allow main programs to provide an override function for _locate_server, * named _krb5_override_service_locator(). If that function is found in * the main program, it's called like a service locator plugin function. * If it returns KRB5_PLUGIN_NO_HANDLE, continue with other _locate_server * functions. If it returns anything else (zero or some other error), * that return is "final" (no other _locate_server functions are called). * This mechanism is used by programs like "idmapd" that want to completely * control service location. */ static krb5_error_code override_locate_server (krb5_context ctx, const krb5_data *realm, struct addrlist *addrlist, enum locate_service_type svc, int socktype, int family) { struct module_callback_data cbdata = { 0, }; krb5_error_code code; void *dlh; krb5_lookup_func lookup_func; Tprintf("in override_locate_server\n"); cbdata.lp = addrlist; if ((dlh = dlopen(0, RTLD_FIRST | RTLD_LAZY)) == NULL) { Tprintf("dlopen failed\n"); return KRB5_PLUGIN_NO_HANDLE; } lookup_func = (krb5_lookup_func) dlsym( dlh, "_krb5_override_service_locator"); dlclose(dlh); if (lookup_func == NULL) { Tprintf("dlsym failed\n"); return KRB5_PLUGIN_NO_HANDLE; } code = lookup_func(ctx, svc, realm->data, socktype, family, module_callback, &cbdata); if (code == KRB5_PLUGIN_NO_HANDLE) { Tprintf("override lookup routine returned KRB5_PLUGIN_NO_HANDLE\n"); return code; } if (code != 0) { /* Module encountered an actual error. */ Tprintf("override lookup routine returned error %d: %s\n", code, error_message(code)); return code; } /* Got something back, yippee. */ Tprintf("now have %d addrs in list %p\n", addrlist->naddrs, addrlist); print_addrlist(addrlist); return 0; } /* Solaris Kerberos (illumos) */ static krb5_error_code prof_locate_server (krb5_context context, const krb5_data *realm, char ***hostlist, enum locate_service_type svc) { const char *realm_srv_names[4]; char **hl, *host, *profname; krb5_error_code code; int i, j, count; *hostlist = NULL; /* default - indicate no KDCs found */ switch (svc) { case locate_service_kdc: profname = "kdc"; break; case locate_service_master_kdc: profname = "master_kdc"; break; case locate_service_kadmin: profname = "admin_server"; break; case locate_service_krb524: profname = "krb524_server"; break; case locate_service_kpasswd: profname = "kpasswd_server"; break; default: return EINVAL; } if ((host = malloc(realm->length + 1)) == NULL) return ENOMEM; (void) strncpy(host, realm->data, realm->length); host[realm->length] = '\0'; hl = 0; realm_srv_names[0] = "realms"; realm_srv_names[1] = host; realm_srv_names[2] = profname; realm_srv_names[3] = 0; code = profile_get_values(context->profile, realm_srv_names, &hl); if (code) { Tprintf ("config file lookup failed: %s\n", error_message(code)); if (code == PROF_NO_SECTION || code == PROF_NO_RELATION) code = KRB5_REALM_UNKNOWN; krb5_xfree(host); return code; } krb5_xfree(host); *hostlist = hl; return 0; } static krb5_error_code dns_locate_server (krb5_context context, const krb5_data *realm, struct srv_dns_entry **dns_list_head, enum locate_service_type svc, int socktype, int family) { const char *dnsname; int use_dns = _krb5_use_dns_kdc(context); krb5_error_code code; struct srv_dns_entry *head = NULL; *dns_list_head = NULL; /* default: indicate we have found no KDCs */ if (!use_dns) return KRB5_PLUGIN_NO_HANDLE; switch (svc) { case locate_service_kdc: dnsname = "_kerberos"; break; case locate_service_master_kdc: dnsname = "_kerberos-master"; break; case locate_service_kadmin: dnsname = "_kerberos-adm"; break; case locate_service_krb524: dnsname = "_krb524"; break; case locate_service_kpasswd: dnsname = "_kpasswd"; break; default: return KRB5_PLUGIN_NO_HANDLE; } code = 0; if (socktype == SOCK_DGRAM || socktype == 0) { code = krb5int_make_srv_query_realm(realm, dnsname, "_udp", &head); if (code) Tprintf("dns udp lookup returned error %d\n", code); } if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) { code = krb5int_make_srv_query_realm(realm, dnsname, "_tcp", &head); if (code) Tprintf("dns tcp lookup returned error %d\n", code); } if (head == NULL) return 0; /* Check for the "." case indicating no support. */ if (head->next == 0 && head->host[0] == 0) { free(head->host); free(head); return KRB5_ERR_NO_SERVICE; } /* * Okay! Now we've got a linked list of entries sorted by * priority. Return it so later we can map hostnames to net addresses. */ *dns_list_head = head; return 0; } /* * Given the list of hostnames of KDCs found in DNS SRV recs, lets go * thru NSS (name svc switch) to get the net addrs. */ static krb5_error_code dns_hostnames2netaddrs( struct srv_dns_entry *head, enum locate_service_type svc, int socktype, int family, struct addrlist *addrlist) { struct srv_dns_entry *entry = NULL, *next; krb5_error_code code; Tprintf ("walking answer list:\n"); for (entry = head; entry != NULL; entry = entry->next) { code = 0; if (socktype) code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0, socktype, family); else { (void) add_host_to_list (addrlist, entry->host, htons (entry->port), 0, SOCK_DGRAM, family); code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0, SOCK_STREAM, family); } if (code) { Tprintf(" fail add_host code=%d %s\n", code, entry->host); } } Tprintf ("[end]\n"); return code; } /* * Given the DNS SRV recs list, return a string of all the hosts like so: * "fqdn0[,fqdn1][,fqdnN]" */ static char * dnslist2str(struct srv_dns_entry *dns_list_head) { struct srv_dns_entry *head = dns_list_head; struct srv_dns_entry *entry = NULL, *next; unsigned int size = 0, c = 0, buf_size; char *s = NULL; for (entry = head; entry; entry = entry->next, c++) { size += strlen(entry->host); } if (!c) return NULL; /* hostnames + commas + NULL */ buf_size = size + (c - 1) + 1; s = malloc(buf_size); if (!s) return NULL; (void) strlcpy(s, head->host, buf_size); for (entry = head->next; entry; entry = entry->next) { (void) strlcat(s, ",", buf_size); (void) strlcat(s, entry->host, buf_size); } return s; } /* * Given the profile hostlist, return a string of all the hosts like so: * "fqdn0[,fqdn1][,fqdnN]" */ static char * hostlist2str(char **hostlist) { unsigned int c = 0, size = 0, buf_size; char **hl = hostlist, *s = NULL; while (hl && *hl) { size += strlen(*hl); hl++; c++; } if (!c) return NULL; /* hostnames + commas + NULL */ buf_size = size + (c - 1) + 1; s = malloc(buf_size); if (!s) return NULL; hl = hostlist; (void) strlcpy(s, *hl, buf_size); hl++; while (hl && *hl) { (void) strlcat(s, ",", buf_size); (void) strlcat(s, *hl, buf_size); hl++; } return s; } /* * Take the profile KDC list and return a list of net addrs. */ static krb5_error_code prof_hostnames2netaddrs( char **hostlist, enum locate_service_type svc, int socktype, int family, struct addrlist *addrlist) /* output */ { int udpport = 0 , sec_udpport = 0; int code, i; struct servent *serv; int count = 0; while (hostlist && hostlist[count]) count++; if (count == 0) { return 0; } switch (svc) { case locate_service_kdc: case locate_service_master_kdc: /* We used to use /etc/services for these, but enough systems have old, crufty, wrong settings that this is probably better. */ udpport = htons(KRB5_DEFAULT_PORT); sec_udpport = htons(KRB5_DEFAULT_SEC_PORT); break; case locate_service_kadmin: udpport = htons(DEFAULT_KADM5_PORT); break; case locate_service_krb524: serv = getservbyname(KRB524_SERVICE, "udp"); udpport = serv ? serv->s_port : htons (KRB524_PORT); break; case locate_service_kpasswd: udpport = htons(DEFAULT_KPASSWD_PORT); break; default: return EINVAL; } for (i=0; hostlist[i]; i++) { int p1, p2; char *cp, *port, *host; host = hostlist[i]; /* * Strip off excess whitespace */ cp = strchr(host, ' '); if (cp) *cp = 0; cp = strchr(host, '\t'); if (cp) *cp = 0; port = strchr(host, ':'); if (port) { *port = 0; port++; } if (port) { unsigned long l; #ifdef HAVE_STROUL char *endptr; l = strtoul (port, &endptr, 10); if (endptr == NULL || *endptr != 0) return EINVAL; #else l = atoi (port); #endif /* L is unsigned, don't need to check <0. */ if (l == 0 || l > 65535) return EINVAL; p1 = htons (l); p2 = 0; } else { p1 = udpport; p2 = sec_udpport; } if (socktype != 0) { code = add_host_to_list (addrlist, hostlist[i], p1, p2, socktype, family); } else { code = add_host_to_list (addrlist, hostlist[i], p1, p2, SOCK_DGRAM, family); if (code == 0) code = add_host_to_list (addrlist, hostlist[i], p1, p2, SOCK_STREAM, family); } } return code; } /* * Wrapper function for the various backends */ krb5_error_code krb5int_locate_server (krb5_context context, const krb5_data *realm, struct addrlist *addrlist, enum locate_service_type svc, int socktype, int family) { krb5_error_code code; struct addrlist al = ADDRLIST_INIT; char **hostlist = NULL; struct srv_dns_entry *dns_list_head = NULL; *addrlist = al; /* * Solaris Kerberos (illumos) * Allow main programs to override _locate_server() */ code = override_locate_server(context, realm, &al, svc, socktype, family); if (code != KRB5_PLUGIN_NO_HANDLE) { if (code == 0) *addrlist = al; else if (al.space) free_list (&al); return (code); } /* Solaris Kerberos (illumos) */ code = module_locate_server(context, realm, &al, svc, socktype, family); Tprintf("module_locate_server returns %d\n", code); if (code == KRB5_PLUGIN_NO_HANDLE) { /* * We always try the local file before DNS. Note that there * is no way to indicate "service not available" via the * config file. */ code = prof_locate_server(context, realm, &hostlist, svc); /* * Solaris Kerberos: * If kpasswd_server has not been configured and dns_lookup_kdc - * dns_fallback are not configured then admin_server should * be inferred, per krb5.conf(5). */ if (code && svc == locate_service_kpasswd && !maybe_use_dns(context, "dns_lookup_kdc", 0)) { code = prof_locate_server(context, realm, &hostlist, locate_service_kadmin); } #ifdef KRB5_DNS_LOOKUP /* * Solaris Kerberos: * There is no point in trying to locate the KDC in DNS if "realm" * is empty. */ /* Try DNS for all profile errors? */ if (code && !krb5_is_referral_realm(realm)) { krb5_error_code code2; code2 = dns_locate_server(context, realm, &dns_list_head, svc, socktype, family); if (code2 != KRB5_PLUGIN_NO_HANDLE) code = code2; } #endif /* KRB5_DNS_LOOKUP */ /* We could put more heuristics here, like looking up a hostname of "kerberos."+REALM, etc. */ } if (code != 0) { if (al.space) free_list (&al); if (hostlist) profile_free_list(hostlist); if (dns_list_head) krb5int_free_srv_dns_data(dns_list_head); return code; } /* * At this point we have no errors, let's check to see if we have * any KDC entries from krb5.conf or DNS. */ if (!hostlist && !dns_list_head) { switch(svc) { case locate_service_master_kdc: krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE, dgettext(TEXT_DOMAIN, "Cannot find a master KDC entry in krb5.conf(5) or DNS Service Location records for realm '%.*s'"), realm->length, realm->data); break; case locate_service_kadmin: krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE, dgettext(TEXT_DOMAIN, "Cannot find a kadmin KDC entry in krb5.conf(5) or DNS Service Location records for realm '%.*s'"), realm->length, realm->data); break; case locate_service_kpasswd: krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE, dgettext(TEXT_DOMAIN, "Cannot find a kpasswd KDC entry in krb5.conf(5) or DNS Service Location records for realm '%.*s'"), realm->length, realm->data); break; default: /* locate_service_kdc: */ krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE, dgettext(TEXT_DOMAIN, "Cannot find any KDC entries in krb5.conf(5) or DNS Service Location records for realm '%.*s'"), realm->length, realm->data); } return KRB5_REALM_CANT_RESOLVE; } /* We have KDC entries, let see if we can get their net addrs. */ if (hostlist) code = prof_hostnames2netaddrs(hostlist, svc, socktype, family, &al); else if (dns_list_head) code = dns_hostnames2netaddrs(dns_list_head, svc, socktype, family, &al); if (code) { if (hostlist) profile_free_list(hostlist); if (dns_list_head) krb5int_free_srv_dns_data(dns_list_head); return code; } /* * Solaris Kerberos: * If an entry for _kerberos-master. does not exist (checked for * above) but _kpasswd. does then treat that as an entry for the * master KDC (but use port 88 not the kpasswd port). MS AD creates * kpasswd entries by default in DNS. */ if (!dns_list_head && svc == locate_service_master_kdc && al.naddrs == 0) { /* Look for _kpasswd._tcp|udp */ code = dns_locate_server(context, realm, &dns_list_head, locate_service_kpasswd, socktype, family); if (code == 0 && dns_list_head) { int i; struct addrinfo *a; code = dns_hostnames2netaddrs(dns_list_head, svc, socktype, family, &al); /* Set the port to 88 instead of the kpasswd port */ if (code == 0 && al.naddrs > 0) { for (i = 0; i < al.naddrs; i++) { if (al.addrs[i].ai->ai_family == AF_INET) for (a = al.addrs[i].ai; a != NULL; a = a->ai_next) ((struct sockaddr_in *)a->ai_addr)->sin_port = htons(KRB5_DEFAULT_PORT); if (al.addrs[i].ai->ai_family == AF_INET6) for (a = al.addrs[i].ai; a != NULL; a = a->ai_next) ((struct sockaddr_in6 *)a->ai_addr)->sin6_port = htons(KRB5_DEFAULT_PORT); } } } } /* No errors so far, lets see if we have KDC net addrs */ if (al.naddrs == 0) { char *hostlist_str = NULL, *dnslist_str = NULL; if (al.space) free_list (&al); if (hostlist) { hostlist_str = hostlist2str(hostlist); krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE, dgettext(TEXT_DOMAIN, "Cannot resolve network address for KDCs '%s' specified in krb5.conf(5) for realm %.*s"), hostlist_str ? hostlist_str : "unknown", realm->length, realm->data); if (hostlist_str) free(hostlist_str); } else if (dns_list_head) { dnslist_str = dnslist2str(dns_list_head); krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE, dgettext(TEXT_DOMAIN, "Cannot resolve network address for KDCs '%s' discovered via DNS Service Location records for realm '%.*s'"), dnslist_str ? dnslist_str : "unknown", realm->length, realm->data); if (dnslist_str) free(dnslist_str); } if (hostlist) profile_free_list(hostlist); if (dns_list_head) krb5int_free_srv_dns_data(dns_list_head); return KRB5_REALM_CANT_RESOLVE; } if (hostlist) profile_free_list(hostlist); if (dns_list_head) krb5int_free_srv_dns_data(dns_list_head); *addrlist = al; return 0; } krb5_error_code krb5_locate_kdc(krb5_context context, const krb5_data *realm, struct addrlist *addrlist, int get_masters, int socktype, int family) { return krb5int_locate_server(context, realm, addrlist, (get_masters ? locate_service_master_kdc : locate_service_kdc), socktype, family); } /* * Solaris Kerberos: for backward compat. Avoid using this * function! */ krb5_error_code krb5_get_servername(krb5_context context, const krb5_data *realm, const char *name, const char *proto, char *srvhost, unsigned short *port) { krb5_error_code code = KRB5_REALM_UNKNOWN; #ifdef KRB5_DNS_LOOKUP { int use_dns = _krb5_use_dns_kdc(context); if (use_dns) { struct srv_dns_entry *head = NULL; code = krb5int_make_srv_query_realm(realm, name, proto, &head); if (code) return (code); if (head == NULL) return KRB5_REALM_CANT_RESOLVE; *port = head->port; (void) strlcpy(srvhost, head->host, MAX_DNS_NAMELEN); #ifdef DEBUG fprintf (stderr, "krb5_get_servername svrhost %s, port %d\n", srvhost, *port); #endif krb5int_free_srv_dns_data(head); } } #endif /* KRB5_DNS_LOOKUP */ return (code); }