2 * lib/krb5/os/locate_kdc.c
4 * Copyright 1990,2000,2001,2002,2003,2004,2006 Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
27 * get socket addresses for KDC.
30 #include "fake-addrinfo.h"
34 #ifdef KRB5_DNS_LOOKUP
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <arpa/nameser.h>
48 /* for old Unixes and friends ... */
49 #ifndef MAXHOSTNAMELEN
50 #define MAXHOSTNAMELEN 64
53 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
55 #if KRB5_DNS_LOOKUP_KDC
56 #define DEFAULT_LOOKUP_KDC 1
58 #define DEFAULT_LOOKUP_KDC 0
60 #if KRB5_DNS_LOOKUP_REALM
61 #define DEFAULT_LOOKUP_REALM 1
63 #define DEFAULT_LOOKUP_REALM 0
67 maybe_use_dns (krb5_context context, const char *name, int defalt)
73 code = profile_get_string(context->profile, "libdefaults",
75 if (value == 0 && code == 0)
76 code = profile_get_string(context->profile, "libdefaults",
77 "dns_fallback", 0, 0, &value);
84 use_dns = _krb5_conf_boolean(value);
85 profile_release_string(value);
90 _krb5_use_dns_kdc(krb5_context context)
92 return maybe_use_dns (context, "dns_lookup_kdc", DEFAULT_LOOKUP_KDC);
96 _krb5_use_dns_realm(krb5_context context)
98 return maybe_use_dns (context, "dns_lookup_realm", DEFAULT_LOOKUP_REALM);
101 #endif /* KRB5_DNS_LOOKUP */
104 krb5int_grow_addrlist (struct addrlist *lp, int nmore)
107 int newspace = lp->space + nmore;
108 size_t newsize = newspace * sizeof (*lp->addrs);
111 newaddrs = realloc (lp->addrs, newsize);
112 if (newaddrs == NULL)
114 lp->addrs = newaddrs;
115 for (i = lp->space; i < newspace; i++) {
116 lp->addrs[i].ai = NULL;
117 lp->addrs[i].freefn = NULL;
118 lp->addrs[i].data = NULL;
120 lp->space = newspace;
123 #define grow_list krb5int_grow_addrlist
125 /* Free up everything pointed to by the addrlist structure, but don't
126 free the structure itself. */
128 krb5int_free_addrlist (struct addrlist *lp)
131 for (i = 0; i < lp->naddrs; i++)
132 if (lp->addrs[i].freefn)
133 (lp->addrs[i].freefn)(lp->addrs[i].data);
136 lp->naddrs = lp->space = 0;
138 #define free_list krb5int_free_addrlist
140 static int translate_ai_error (int err)
149 /* All of these indicate bad inputs to getaddrinfo. */
152 /* Translate to standard errno code. */
155 /* Translate to standard errno code. */
157 #ifdef EAI_ADDRFAMILY
160 #if EAI_NODATA != EAI_NONAME
164 /* Name not known or no address data, but no error. Do
169 /* System error, obviously. */
173 /* An error code we haven't handled? */
179 static inline void Tprintf(const char *fmt, ...)
184 vfprintf(stderr, fmt, ap);
190 extern void krb5int_debug_fprint(const char *, ...);
191 #define dprint krb5int_debug_fprint
192 #define print_addrlist krb5int_print_addrlist
193 extern void print_addrlist (const struct addrlist *a);
195 static inline void dprint(const char *fmt, ...) { }
196 static inline void print_addrlist(const struct addrlist *a) { }
199 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a,
200 void (*freefn)(void *), void *data)
204 dprint("\tadding %p=%A to %p (naddrs=%d space=%d)\n", a, a, lp,
205 lp->naddrs, lp->space);
207 if (lp->naddrs == lp->space) {
208 err = grow_list (lp, 1);
210 Tprintf ("grow_list failed %d\n", err);
214 Tprintf("setting element %d\n", lp->naddrs);
215 lp->addrs[lp->naddrs].ai = a;
216 lp->addrs[lp->naddrs].freefn = freefn;
217 lp->addrs[lp->naddrs].data = data;
219 Tprintf ("\tcount is now %d: ", lp->naddrs);
225 #define add_host_to_list krb5int_add_host_to_list
227 static void call_freeaddrinfo(void *data)
229 /* Strict interpretation of the C standard says we can't assume
230 that the ABI for f(void*) and f(struct foo *) will be
231 compatible. Use this stub just to be paranoid. */
236 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
237 int port, int secport,
238 int socktype, int family)
240 struct addrinfo *addrs, *a, *anext, hint;
242 char portbuf[10], secportbuf[10];
243 void (*freefn)(void *);
245 Tprintf ("adding hostname %s, ports %d,%d, family %d, socktype %d\n",
246 hostname, ntohs (port), ntohs (secport),
249 memset(&hint, 0, sizeof(hint));
250 hint.ai_family = family;
251 hint.ai_socktype = socktype;
252 #ifdef AI_NUMERICSERV
253 hint.ai_flags = AI_NUMERICSERV;
255 sprintf(portbuf, "%d", ntohs(port));
256 sprintf(secportbuf, "%d", ntohs(secport));
257 err = getaddrinfo (hostname, portbuf, &hint, &addrs);
259 Tprintf ("\tgetaddrinfo(\"%s\", \"%s\", ...)\n\treturns %d: %s\n",
260 hostname, portbuf, err, gai_strerror (err));
261 return translate_ai_error (err);
263 freefn = call_freeaddrinfo;
265 for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
267 err = add_addrinfo_to_list (lp, a, freefn, a);
269 if (err || secport == 0)
272 socktype = SOCK_DGRAM;
273 else if (socktype != SOCK_DGRAM)
275 hint.ai_family = AF_INET;
276 err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
278 err = translate_ai_error (err);
281 freefn = call_freeaddrinfo;
282 for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
284 err = add_addrinfo_to_list (lp, a, freefn, a);
287 /* XXX Memory leaks possible here if add_addrinfo_to_list fails. */
292 * returns count of number of addresses found
293 * if master is non-NULL, it is filled in with the index of
297 static krb5_error_code
298 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
299 const char * name, struct addrlist *addrlist,
300 int get_masters, int socktype,
301 int udpport, int sec_udpport, int family)
303 const char *realm_srv_names[4];
304 char **masterlist, **hostlist, *host, *port, *cp;
305 krb5_error_code code;
306 int i, j, count, ismaster;
308 Tprintf ("looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
309 realm->data, name, ntohs (udpport), ntohs (sec_udpport));
311 if ((host = malloc(realm->length + 1)) == NULL)
314 strncpy(host, realm->data, realm->length);
315 host[realm->length] = '\0';
320 realm_srv_names[0] = "realms";
321 realm_srv_names[1] = host;
322 realm_srv_names[2] = name;
323 realm_srv_names[3] = 0;
325 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
328 Tprintf ("config file lookup failed: %s\n",
329 error_message(code));
330 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
331 code = KRB5_REALM_UNKNOWN;
337 while (hostlist && hostlist[count])
339 Tprintf ("found %d entries under 'kdc'\n", count);
342 profile_free_list(hostlist);
344 addrlist->naddrs = 0;
349 realm_srv_names[0] = "realms";
350 realm_srv_names[1] = host;
351 realm_srv_names[2] = "admin_server";
352 realm_srv_names[3] = 0;
354 code = profile_get_values(context->profile, realm_srv_names,
360 for (i=0; masterlist[i]; i++) {
361 host = masterlist[i];
364 * Strip off excess whitespace
366 cp = strchr(host, ' ');
369 cp = strchr(host, '\t');
372 cp = strchr(host, ':');
381 /* at this point, if master is non-NULL, then either the master kdc
382 is required, and there is one, or the master kdc is not required,
383 and there may or may not be one. */
385 #ifdef HAVE_NETINET_IN_H
390 for (i=0; hostlist[i]; i++) {
394 Tprintf ("entry %d is '%s'\n", i, host);
396 * Strip off excess whitespace
398 cp = strchr(host, ' ');
401 cp = strchr(host, '\t');
404 port = strchr(host, ':');
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. */
443 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
446 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
449 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
450 SOCK_STREAM, family);
453 Tprintf ("error %d (%s) returned from add_host_to_list\n", code,
454 error_message (code));
456 profile_free_list (hostlist);
458 profile_free_list (masterlist);
464 profile_free_list(hostlist);
466 profile_free_list(masterlist);
472 static krb5_error_code
473 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
474 const char *name, struct addrlist *al, int get_masters,
475 int udpport, int sec_udpport)
479 ret = krb5_locate_srv_conf_1 (context, realm, name, al,
480 get_masters, 0, udpport, sec_udpport, 0);
483 if (al->naddrs == 0) /* Couldn't resolve any KDC names */
484 return KRB5_REALM_CANT_RESOLVE;
489 #ifdef KRB5_DNS_LOOKUP
490 static krb5_error_code
491 krb5_locate_srv_dns_1 (const krb5_data *realm,
493 const char *protocol,
494 struct addrlist *addrlist,
497 struct srv_dns_entry *head = NULL;
498 struct srv_dns_entry *entry = NULL, *next;
499 krb5_error_code code = 0;
501 code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
506 * Okay! Now we've got a linked list of entries sorted by
507 * priority. Start looking up A records and returning
514 /* Check for the "." case indicating no support. */
515 if (head->next == 0 && head->host[0] == 0) {
518 return KRB5_ERR_NO_SERVICE;
521 Tprintf ("walking answer list:\n");
522 for (entry = head; entry != NULL; entry = next) {
523 Tprintf ("\tport=%d host=%s\n", entry->port, entry->host);
525 code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
526 (strcmp("_tcp", protocol)
528 : SOCK_STREAM), family);
541 krb5int_free_srv_dns_data(head);
546 #include "k5-locate.h"
548 #ifdef KFM_FRAMEWORK_PLUGIN_DIR
549 static const char objdir[] = KFM_FRAMEWORK_PLUGIN_DIR ;
551 static const char objdir[] = LIBDIR "/krb5/plugins/libkrb5";
554 struct module_callback_data {
560 module_callback (void *cbdata, int socktype, struct sockaddr *sa)
562 struct module_callback_data *d = cbdata;
566 struct sockaddr_in sin;
567 struct sockaddr_in6 sin6;
571 if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
573 if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
575 x = malloc (sizeof (*x));
580 memset(x, 0, sizeof (*x));
581 x->ai.ai_addr = (struct sockaddr *) &x->u;
582 x->ai.ai_socktype = socktype;
583 x->ai.ai_family = sa->sa_family;
584 if (sa->sa_family == AF_INET) {
585 x->u.sin = *(struct sockaddr_in *)sa;
586 x->ai.ai_addrlen = sizeof(struct sockaddr_in);
588 if (sa->sa_family == AF_INET6) {
589 x->u.sin6 = *(struct sockaddr_in6 *)sa;
590 x->ai.ai_addrlen = sizeof(struct sockaddr_in6);
592 if (add_addrinfo_to_list (d->lp, &x->ai, free, x) != 0) {
593 /* Assumes only error is ENOMEM. */
600 static krb5_error_code
601 module_locate_server (krb5_context ctx, const krb5_data *realm,
602 struct addrlist *addrlist,
603 enum locate_service_type svc, int socktype, int family)
605 struct krb5plugin_service_locate_result *res = NULL;
606 krb5_error_code code;
607 struct krb5plugin_service_locate_ftable *vtbl = NULL;
610 struct module_callback_data cbdata = { 0, };
612 Tprintf("in module_locate_server\n");
613 cbdata.lp = addrlist;
614 if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) {
615 code = krb5int_open_plugin_dir (objdir, &ctx->libkrb5_plugins,
618 return KRB5_PLUGIN_NO_HANDLE;
621 code = krb5int_get_plugin_dir_data (&ctx->libkrb5_plugins,
622 "service_locator", &ptrs, &ctx->err);
624 Tprintf("error looking up plugin symbols: %s\n",
625 krb5_get_error_message(ctx, code));
626 return KRB5_PLUGIN_NO_HANDLE;
629 for (i = 0; ptrs[i]; i++) {
633 Tprintf("element %d is %p\n", i, ptrs[i]);
635 /* For now, don't keep the plugin data alive. For long-lived
636 contexts, it may be desirable to change that later. */
637 code = vtbl->init(ctx, &blob);
641 code = vtbl->lookup(blob, svc, realm->data, socktype, family,
642 module_callback, &cbdata);
644 if (code == KRB5_PLUGIN_NO_HANDLE) {
645 /* Module passes, keep going. */
647 Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)\n");
651 /* Module encountered an actual error. */
652 Tprintf("plugin lookup routine returned error %d: %s\n",
653 code, error_message(code));
654 krb5int_free_plugin_dir_data (ptrs);
659 if (ptrs[i] == NULL) {
660 Tprintf("ran off end of plugin list\n");
661 krb5int_free_plugin_dir_data (ptrs);
662 return KRB5_PLUGIN_NO_HANDLE;
664 Tprintf("stopped with plugin #%d, res=%p\n", i, res);
666 /* Got something back, yippee. */
667 Tprintf("now have %d addrs in list %p\n", addrlist->naddrs, addrlist);
668 print_addrlist(addrlist);
669 krb5int_free_plugin_dir_data (ptrs);
673 static krb5_error_code
674 prof_locate_server (krb5_context context, const krb5_data *realm,
675 struct addrlist *addrlist,
676 enum locate_service_type svc, int socktype, int family)
678 const char *profname;
679 int dflport1, dflport2 = 0;
680 struct servent *serv;
683 case locate_service_kdc:
685 /* We used to use /etc/services for these, but enough systems
686 have old, crufty, wrong settings that this is probably
689 dflport1 = htons(KRB5_DEFAULT_PORT);
690 dflport2 = htons(KRB5_DEFAULT_SEC_PORT);
692 case locate_service_master_kdc:
693 profname = "master_kdc";
695 case locate_service_kadmin:
696 profname = "admin_server";
697 dflport1 = htons(DEFAULT_KADM5_PORT);
699 case locate_service_krb524:
700 profname = "krb524_server";
701 serv = getservbyname(KRB524_SERVICE, "udp");
702 dflport1 = serv ? serv->s_port : htons (KRB524_PORT);
704 case locate_service_kpasswd:
705 profname = "kpasswd_server";
706 dflport1 = htons(DEFAULT_KPASSWD_PORT);
709 return EBUSY; /* XXX */
712 return krb5_locate_srv_conf_1 (context, realm, profname, addrlist,
714 dflport1, dflport2, family);
717 static krb5_error_code
718 dns_locate_server (krb5_context context, const krb5_data *realm,
719 struct addrlist *addrlist,
720 enum locate_service_type svc, int socktype, int family)
723 int use_dns = _krb5_use_dns_kdc(context);
724 krb5_error_code code;
727 return KRB5_PLUGIN_NO_HANDLE;
730 case locate_service_kdc:
731 dnsname = "_kerberos";
733 case locate_service_master_kdc:
734 dnsname = "_kerberos-master";
736 case locate_service_kadmin:
737 dnsname = "_kerberos-adm";
739 case locate_service_krb524:
742 case locate_service_kpasswd:
743 dnsname = "_kpasswd";
746 return KRB5_PLUGIN_NO_HANDLE;
750 if (socktype == SOCK_DGRAM || socktype == 0) {
751 code = krb5_locate_srv_dns_1(realm, dnsname, "_udp", addrlist, family);
753 Tprintf("dns udp lookup returned error %d\n", code);
755 if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
756 code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp", addrlist, family);
758 Tprintf("dns tcp lookup returned error %d\n", code);
764 * Wrapper function for the various backends
768 krb5int_locate_server (krb5_context context, const krb5_data *realm,
769 struct addrlist *addrlist,
770 enum locate_service_type svc,
771 int socktype, int family)
773 krb5_error_code code;
774 struct addrlist al = ADDRLIST_INIT;
778 code = module_locate_server(context, realm, &al, svc, socktype, family);
779 Tprintf("module_locate_server returns %d\n", code);
780 if (code != KRB5_PLUGIN_NO_HANDLE) {
786 * We always try the local file before DNS
789 code = prof_locate_server(context, realm, &al, svc, socktype, family);
791 /* We could put more heuristics here, like looking up a hostname
792 of "kerberos."+REALM, etc. */
794 #ifdef KRB5_DNS_LOOKUP
796 krb5_error_code code2;
797 code2 = dns_locate_server(context, realm, &al, svc, socktype, family);
798 if (code2 != KRB5_PLUGIN_NO_HANDLE)
801 #endif /* KRB5_DNS_LOOKUP */
803 Tprintf ("krb5int_locate_server found %d addresses\n",
806 Tprintf ("krb5int_locate_server returning error code %d/%s\n",
807 code, error_message(code));
813 if (al.naddrs == 0) { /* No good servers */
816 return KRB5_REALM_CANT_RESOLVE;
823 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
824 struct addrlist *addrlist,
825 int get_masters, int socktype, int family)
827 return krb5int_locate_server(context, realm, addrlist,
829 ? locate_service_master_kdc
830 : locate_service_kdc),