1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * lib/krb5/os/locate_kdc.c
5 * Copyright 1990,2000,2001,2002,2003,2004,2006,2008 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 * get socket addresses for KDC.
31 #include "fake-addrinfo.h"
35 #ifdef KRB5_DNS_LOOKUP
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <arpa/nameser.h>
49 /* for old Unixes and friends ... */
50 #ifndef MAXHOSTNAMELEN
51 #define MAXHOSTNAMELEN 64
54 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
56 #if KRB5_DNS_LOOKUP_KDC
57 #define DEFAULT_LOOKUP_KDC 1
59 #define DEFAULT_LOOKUP_KDC 0
61 #if KRB5_DNS_LOOKUP_REALM
62 #define DEFAULT_LOOKUP_REALM 1
64 #define DEFAULT_LOOKUP_REALM 0
68 maybe_use_dns (krb5_context context, const char *name, int defalt)
74 code = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
76 if (value == 0 && code == 0)
77 code = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
78 KRB5_CONF_DNS_FALLBACK, 0, 0, &value);
85 use_dns = _krb5_conf_boolean(value);
86 profile_release_string(value);
91 _krb5_use_dns_kdc(krb5_context context)
93 return maybe_use_dns (context, KRB5_CONF_DNS_LOOKUP_KDC, DEFAULT_LOOKUP_KDC);
97 _krb5_use_dns_realm(krb5_context context)
99 return maybe_use_dns (context, KRB5_CONF_DNS_LOOKUP_REALM, DEFAULT_LOOKUP_REALM);
102 #endif /* KRB5_DNS_LOOKUP */
105 krb5int_grow_addrlist (struct addrlist *lp, int nmore)
108 size_t newspace = lp->space + nmore;
109 size_t newsize = newspace * sizeof (*lp->addrs);
112 newaddrs = realloc (lp->addrs, newsize);
113 if (newaddrs == NULL)
115 lp->addrs = newaddrs;
116 for (i = lp->space; i < newspace; i++) {
117 lp->addrs[i].ai = NULL;
118 lp->addrs[i].freefn = NULL;
119 lp->addrs[i].data = NULL;
121 lp->space = newspace;
124 #define grow_list krb5int_grow_addrlist
126 /* Free up everything pointed to by the addrlist structure, but don't
127 free the structure itself. */
129 krb5int_free_addrlist (struct addrlist *lp)
132 for (i = 0; i < lp->naddrs; i++)
133 if (lp->addrs[i].freefn)
134 (lp->addrs[i].freefn)(lp->addrs[i].data);
137 lp->naddrs = lp->space = 0;
139 #define free_list krb5int_free_addrlist
142 translate_ai_error (int err)
151 /* All of these indicate bad inputs to getaddrinfo. */
154 /* Translate to standard errno code. */
157 /* Translate to standard errno code. */
159 #ifdef EAI_ADDRFAMILY
162 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
166 /* Name not known or no address data, but no error. Do
171 /* An argument buffer overflowed. */
172 return EINVAL; /* XXX */
176 /* System error, obviously. */
180 /* An error code we haven't handled? */
187 Tprintf(const char *fmt, ...)
192 vfprintf(stderr, fmt, ap);
198 extern void krb5int_debug_fprint(const char *, ...);
199 #define dprint krb5int_debug_fprint
200 #define print_addrlist krb5int_print_addrlist
201 extern void print_addrlist (const struct addrlist *a);
203 static inline void dprint(const char *fmt, ...) { }
204 static inline void print_addrlist(const struct addrlist *a) { }
208 add_addrinfo_to_list(struct addrlist *lp, struct addrinfo *a,
209 void (*freefn)(void *), void *data)
213 dprint("\tadding %p=%A to %p (naddrs=%d space=%d)\n", a, a, lp,
214 lp->naddrs, lp->space);
216 if (lp->naddrs == lp->space) {
217 err = grow_list (lp, 1);
219 Tprintf ("grow_list failed %d\n", err);
223 Tprintf("setting element %d\n", lp->naddrs);
224 lp->addrs[lp->naddrs].ai = a;
225 lp->addrs[lp->naddrs].freefn = freefn;
226 lp->addrs[lp->naddrs].data = data;
228 Tprintf ("\tcount is now %lu: ", (unsigned long) lp->naddrs);
234 #define add_host_to_list krb5int_add_host_to_list
237 call_freeaddrinfo(void *data)
239 /* Strict interpretation of the C standard says we can't assume
240 that the ABI for f(void*) and f(struct foo *) will be
241 compatible. Use this stub just to be paranoid. */
246 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
247 int port, int secport,
248 int socktype, int family)
250 struct addrinfo *addrs, *a, *anext, hint;
252 char portbuf[10], secportbuf[10];
253 void (*freefn)(void *);
255 Tprintf ("adding hostname %s, ports %d,%d, family %d, socktype %d\n",
256 hostname, ntohs (port), ntohs (secport),
259 memset(&hint, 0, sizeof(hint));
260 hint.ai_family = family;
261 hint.ai_socktype = socktype;
262 #ifdef AI_NUMERICSERV
263 hint.ai_flags = AI_NUMERICSERV;
265 result = snprintf(portbuf, sizeof(portbuf), "%d", ntohs(port));
266 if (SNPRINTF_OVERFLOW(result, sizeof(portbuf)))
269 result = snprintf(secportbuf, sizeof(secportbuf), "%d", ntohs(secport));
270 if (SNPRINTF_OVERFLOW(result, sizeof(secportbuf)))
272 err = getaddrinfo (hostname, portbuf, &hint, &addrs);
274 Tprintf ("\tgetaddrinfo(\"%s\", \"%s\", ...)\n\treturns %d: %s\n",
275 hostname, portbuf, err, gai_strerror (err));
276 return translate_ai_error (err);
278 freefn = call_freeaddrinfo;
280 for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
282 err = add_addrinfo_to_list (lp, a, freefn, a);
284 if (err || secport == 0)
287 socktype = SOCK_DGRAM;
288 else if (socktype != SOCK_DGRAM)
290 hint.ai_family = AF_INET;
291 err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
293 err = translate_ai_error (err);
296 freefn = call_freeaddrinfo;
297 for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
299 err = add_addrinfo_to_list (lp, a, freefn, a);
302 /* XXX Memory leaks possible here if add_addrinfo_to_list fails. */
307 * returns count of number of addresses found
308 * if master is non-NULL, it is filled in with the index of
312 static krb5_error_code
313 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
314 const char * name, struct addrlist *addrlist,
315 int get_masters, int socktype,
316 int udpport, int sec_udpport, int family)
318 const char *realm_srv_names[4];
319 char **masterlist, **hostlist, *host, *port, *cp;
320 krb5_error_code code;
321 int i, j, count, ismaster;
323 Tprintf ("looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
324 realm->data, name, ntohs (udpport), ntohs (sec_udpport));
326 if ((host = malloc(realm->length + 1)) == NULL)
329 strncpy(host, realm->data, realm->length);
330 host[realm->length] = '\0';
335 realm_srv_names[0] = KRB5_CONF_REALMS;
336 realm_srv_names[1] = host;
337 realm_srv_names[2] = name;
338 realm_srv_names[3] = 0;
340 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
343 Tprintf ("config file lookup failed: %s\n",
344 error_message(code));
345 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
346 code = KRB5_REALM_UNKNOWN;
352 while (hostlist && hostlist[count])
354 Tprintf ("found %d entries under 'kdc'\n", count);
357 profile_free_list(hostlist);
359 addrlist->naddrs = 0;
364 realm_srv_names[0] = KRB5_CONF_REALMS;
365 realm_srv_names[1] = host;
366 realm_srv_names[2] = KRB5_CONF_ADMIN_SERVER;
367 realm_srv_names[3] = 0;
369 code = profile_get_values(context->profile, realm_srv_names,
375 for (i=0; masterlist[i]; i++) {
376 host = masterlist[i];
377 /* Strip off excess characters. */
378 if (*host == '[' && (cp = strchr(host, ']')))
381 *(host + strcspn(host, " \t:")) = '\0';
388 /* at this point, if master is non-NULL, then either the master kdc
389 is required, and there is one, or the master kdc is not required,
390 and there may or may not be one. */
392 #ifdef HAVE_NETINET_IN_H
397 for (i=0; hostlist[i]; i++) {
401 Tprintf ("entry %d is '%s'\n", i, host);
402 /* Find port number, and strip off any excess characters. */
403 if (*host == '[' && (cp = strchr(host, ']')))
406 cp = host + strcspn(host, " \t:");
407 port = (*cp == ':') ? cp + 1 : NULL;
412 for (j=0; masterlist[j]; j++) {
413 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
419 if (get_masters && !ismaster)
426 l = strtoul (port, &endptr, 10);
427 if (endptr == NULL || *endptr != 0)
432 /* L is unsigned, don't need to check <0. */
442 /* If the hostname was in brackets, strip those off now. */
443 if (*host == '[' && (cp = strchr(host, ']'))) {
449 code = add_host_to_list(addrlist, host, p1, p2, socktype, family);
451 code = add_host_to_list(addrlist, host, p1, p2, SOCK_DGRAM,
454 code = add_host_to_list(addrlist, host, p1, p2, SOCK_STREAM,
458 Tprintf ("error %d (%s) returned from add_host_to_list\n", code,
459 error_message (code));
461 profile_free_list (hostlist);
463 profile_free_list (masterlist);
469 profile_free_list(hostlist);
471 profile_free_list(masterlist);
477 static krb5_error_code
478 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
479 const char *name, struct addrlist *al, int get_masters,
480 int udpport, int sec_udpport)
484 ret = krb5_locate_srv_conf_1 (context, realm, name, al,
485 get_masters, 0, udpport, sec_udpport, 0);
488 if (al->naddrs == 0) /* Couldn't resolve any KDC names */
489 return KRB5_REALM_CANT_RESOLVE;
494 #ifdef KRB5_DNS_LOOKUP
495 static krb5_error_code
496 krb5_locate_srv_dns_1 (const krb5_data *realm,
498 const char *protocol,
499 struct addrlist *addrlist,
502 struct srv_dns_entry *head = NULL;
503 struct srv_dns_entry *entry = NULL, *next;
504 krb5_error_code code = 0;
506 code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
511 * Okay! Now we've got a linked list of entries sorted by
512 * priority. Start looking up A records and returning
519 /* Check for the "." case indicating no support. */
520 if (head->next == 0 && head->host[0] == 0) {
523 return KRB5_ERR_NO_SERVICE;
526 Tprintf ("walking answer list:\n");
527 for (entry = head; entry != NULL; entry = next) {
528 Tprintf ("\tport=%d host=%s\n", entry->port, entry->host);
530 code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
531 (strcmp("_tcp", protocol)
533 : SOCK_STREAM), family);
546 krb5int_free_srv_dns_data(head);
551 #include <krb5/locate_plugin.h>
554 static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/libkrb5", NULL }; /* should be a list */
556 static const char *objdirs[] = { LIBDIR "/krb5/plugins/libkrb5", NULL };
559 struct module_callback_data {
565 module_callback (void *cbdata, int socktype, struct sockaddr *sa)
567 struct module_callback_data *d = cbdata;
571 struct sockaddr_in sin;
572 #ifdef KRB5_USE_INET6
573 struct sockaddr_in6 sin6;
578 if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
580 if (sa->sa_family != AF_INET
581 #ifdef KRB5_USE_INET6
582 && sa->sa_family != AF_INET6
586 x = calloc (1, sizeof (*x));
591 x->ai.ai_addr = (struct sockaddr *) &x->u;
592 x->ai.ai_socktype = socktype;
593 x->ai.ai_family = sa->sa_family;
594 if (sa->sa_family == AF_INET) {
595 x->u.sin = *(struct sockaddr_in *)sa;
596 x->ai.ai_addrlen = sizeof(struct sockaddr_in);
598 #ifdef KRB5_USE_INET6
599 if (sa->sa_family == AF_INET6) {
600 x->u.sin6 = *(struct sockaddr_in6 *)sa;
601 x->ai.ai_addrlen = sizeof(struct sockaddr_in6);
604 if (add_addrinfo_to_list (d->lp, &x->ai, free, x) != 0) {
605 /* Assumes only error is ENOMEM. */
612 static krb5_error_code
613 module_locate_server (krb5_context ctx, const krb5_data *realm,
614 struct addrlist *addrlist,
615 enum locate_service_type svc, int socktype, int family)
617 struct krb5plugin_service_locate_result *res = NULL;
618 krb5_error_code code;
619 struct krb5plugin_service_locate_ftable *vtbl = NULL;
621 char *realmz; /* NUL-terminated realm */
623 struct module_callback_data cbdata = { 0, };
626 Tprintf("in module_locate_server\n");
627 cbdata.lp = addrlist;
628 if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) {
630 code = krb5int_open_plugin_dirs (objdirs, NULL, &ctx->libkrb5_plugins,
633 return KRB5_PLUGIN_NO_HANDLE;
636 code = krb5int_get_plugin_dir_data (&ctx->libkrb5_plugins,
637 "service_locator", &ptrs, &ctx->err);
639 Tprintf("error looking up plugin symbols: %s\n",
640 (msg = krb5_get_error_message(ctx, code)));
641 krb5_free_error_message(ctx, msg);
642 return KRB5_PLUGIN_NO_HANDLE;
645 if (realm->length >= UINT_MAX) {
646 krb5int_free_plugin_dir_data(ptrs);
649 realmz = malloc(realm->length + 1);
650 if (realmz == NULL) {
651 krb5int_free_plugin_dir_data(ptrs);
654 memcpy(realmz, realm->data, realm->length);
655 realmz[realm->length] = '\0';
656 for (i = 0; ptrs[i]; i++) {
660 Tprintf("element %d is %p\n", i, ptrs[i]);
662 /* For now, don't keep the plugin data alive. For long-lived
663 contexts, it may be desirable to change that later. */
664 code = vtbl->init(ctx, &blob);
668 code = vtbl->lookup(blob, svc, realmz, socktype, family,
669 module_callback, &cbdata);
671 if (code == KRB5_PLUGIN_NO_HANDLE) {
672 /* Module passes, keep going. */
674 Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)\n");
678 /* Module encountered an actual error. */
679 Tprintf("plugin lookup routine returned error %d: %s\n",
680 code, error_message(code));
682 krb5int_free_plugin_dir_data (ptrs);
687 if (ptrs[i] == NULL) {
688 Tprintf("ran off end of plugin list\n");
690 krb5int_free_plugin_dir_data (ptrs);
691 return KRB5_PLUGIN_NO_HANDLE;
693 Tprintf("stopped with plugin #%d, res=%p\n", i, res);
695 /* Got something back, yippee. */
696 Tprintf("now have %lu addrs in list %p\n",
697 (unsigned long) addrlist->naddrs, addrlist);
698 print_addrlist(addrlist);
700 krb5int_free_plugin_dir_data (ptrs);
704 static krb5_error_code
705 prof_locate_server (krb5_context context, const krb5_data *realm,
706 struct addrlist *addrlist,
707 enum locate_service_type svc, int socktype, int family)
709 const char *profname;
710 int dflport1, dflport2 = 0;
711 struct servent *serv;
714 case locate_service_kdc:
715 profname = KRB5_CONF_KDC;
716 /* We used to use /etc/services for these, but enough systems
717 have old, crufty, wrong settings that this is probably
720 dflport1 = htons(KRB5_DEFAULT_PORT);
721 dflport2 = htons(KRB5_DEFAULT_SEC_PORT);
723 case locate_service_master_kdc:
724 profname = KRB5_CONF_MASTER_KDC;
726 case locate_service_kadmin:
727 profname = KRB5_CONF_ADMIN_SERVER;
728 dflport1 = htons(DEFAULT_KADM5_PORT);
730 case locate_service_krb524:
731 profname = KRB5_CONF_KRB524_SERVER;
732 serv = getservbyname(KRB524_SERVICE, "udp");
733 dflport1 = serv ? serv->s_port : htons (KRB524_PORT);
735 case locate_service_kpasswd:
736 profname = KRB5_CONF_KPASSWD_SERVER;
737 dflport1 = htons(DEFAULT_KPASSWD_PORT);
740 return EBUSY; /* XXX */
743 return krb5_locate_srv_conf_1 (context, realm, profname, addrlist,
745 dflport1, dflport2, family);
748 static krb5_error_code
749 dns_locate_server (krb5_context context, const krb5_data *realm,
750 struct addrlist *addrlist,
751 enum locate_service_type svc, int socktype, int family)
754 int use_dns = _krb5_use_dns_kdc(context);
755 krb5_error_code code;
758 return KRB5_PLUGIN_NO_HANDLE;
761 case locate_service_kdc:
762 dnsname = "_kerberos";
764 case locate_service_master_kdc:
765 dnsname = "_kerberos-master";
767 case locate_service_kadmin:
768 dnsname = "_kerberos-adm";
770 case locate_service_krb524:
773 case locate_service_kpasswd:
774 dnsname = "_kpasswd";
777 return KRB5_PLUGIN_NO_HANDLE;
781 if (socktype == SOCK_DGRAM || socktype == 0) {
782 code = krb5_locate_srv_dns_1(realm, dnsname, "_udp", addrlist, family);
784 Tprintf("dns udp lookup returned error %d\n", code);
786 if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
787 code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp", addrlist, family);
789 Tprintf("dns tcp lookup returned error %d\n", code);
795 * Wrapper function for the various backends
799 krb5int_locate_server (krb5_context context, const krb5_data *realm,
800 struct addrlist *addrlist,
801 enum locate_service_type svc,
802 int socktype, int family)
804 krb5_error_code code;
805 struct addrlist al = ADDRLIST_INIT;
809 if (realm == NULL || realm->data == NULL || realm->data[0] == 0) {
810 krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
811 "Cannot find KDC for invalid realm name \"\"");
812 return KRB5_REALM_CANT_RESOLVE;
815 code = module_locate_server(context, realm, &al, svc, socktype, family);
816 Tprintf("module_locate_server returns %d\n", code);
817 if (code == KRB5_PLUGIN_NO_HANDLE) {
819 * We always try the local file before DNS. Note that there
820 * is no way to indicate "service not available" via the
824 code = prof_locate_server(context, realm, &al, svc, socktype, family);
826 #ifdef KRB5_DNS_LOOKUP
827 if (code) { /* Try DNS for all profile errors? */
828 krb5_error_code code2;
829 code2 = dns_locate_server(context, realm, &al, svc, socktype,
831 if (code2 != KRB5_PLUGIN_NO_HANDLE)
834 #endif /* KRB5_DNS_LOOKUP */
836 /* We could put more heuristics here, like looking up a hostname
837 of "kerberos."+REALM, etc. */
840 Tprintf ("krb5int_locate_server found %d addresses\n",
843 Tprintf ("krb5int_locate_server returning error code %d/%s\n",
844 code, error_message(code));
850 if (al.naddrs == 0) { /* No good servers */
853 krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
854 "Cannot resolve network address for KDC in realm \"%.*s\"",
855 realm->length, realm->data);
857 return KRB5_REALM_CANT_RESOLVE;
864 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
865 struct addrlist *addrlist,
866 int get_masters, int socktype, int family)
868 return krb5int_locate_server(context, realm, addrlist,
870 ? locate_service_master_kdc
871 : locate_service_kdc),