1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/locate_kdc.c - Get addresses for realm KDCs and other servers */
4 * Copyright 1990,2000,2001,2002,2003,2004,2006,2008 Massachusetts Institute of
5 * Technology. All Rights Reserved.
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 #include "fake-addrinfo.h"
31 #ifdef KRB5_DNS_LOOKUP
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <arpa/nameser.h>
45 /* for old Unixes and friends ... */
46 #ifndef MAXHOSTNAMELEN
47 #define MAXHOSTNAMELEN 64
50 #if KRB5_DNS_LOOKUP_KDC
51 #define DEFAULT_LOOKUP_KDC 1
53 #define DEFAULT_LOOKUP_KDC 0
55 #if KRB5_DNS_LOOKUP_REALM
56 #define DEFAULT_LOOKUP_REALM 1
58 #define DEFAULT_LOOKUP_REALM 0
62 maybe_use_dns (krb5_context context, const char *name, int defalt)
68 code = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
70 if (value == 0 && code == 0)
71 code = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
72 KRB5_CONF_DNS_FALLBACK, 0, 0, &value);
79 use_dns = _krb5_conf_boolean(value);
80 profile_release_string(value);
85 _krb5_use_dns_kdc(krb5_context context)
87 return maybe_use_dns (context, KRB5_CONF_DNS_LOOKUP_KDC, DEFAULT_LOOKUP_KDC);
91 _krb5_use_dns_realm(krb5_context context)
93 return maybe_use_dns (context, KRB5_CONF_DNS_LOOKUP_REALM, DEFAULT_LOOKUP_REALM);
96 #endif /* KRB5_DNS_LOOKUP */
98 /* Free up everything pointed to by the serverlist structure, but don't
99 free the structure itself. */
101 k5_free_serverlist (struct serverlist *list)
105 for (i = 0; i < list->nservers; i++)
106 free(list->servers[i].hostname);
108 list->servers = NULL;
114 Tprintf(const char *fmt, ...)
119 vfprintf(stderr, fmt, ap);
125 extern void krb5int_debug_fprint(const char *, ...);
126 #define dprint krb5int_debug_fprint
128 static inline void dprint(const char *fmt, ...) { }
131 /* Make room for a new server entry in list and return a pointer to the new
132 * entry. (Do not increment list->nservers.) */
133 static struct server_entry *
134 new_server_entry(struct serverlist *list)
136 struct server_entry *newservers, *entry;
137 size_t newspace = (list->nservers + 1) * sizeof(struct server_entry);
139 newservers = realloc(list->servers, newspace);
140 if (newservers == NULL)
142 list->servers = newservers;
143 entry = &newservers[list->nservers];
144 memset(entry, 0, sizeof(*entry));
148 /* Add an address entry to list. */
150 add_addr_to_list(struct serverlist *list, int socktype, int family,
151 size_t addrlen, struct sockaddr *addr)
153 struct server_entry *entry;
155 entry = new_server_entry(list);
158 entry->socktype = socktype;
159 entry->family = family;
160 entry->hostname = NULL;
161 entry->addrlen = addrlen;
162 memcpy(&entry->addr, addr, addrlen);
167 /* Add a hostname entry to list. */
169 add_host_to_list(struct serverlist *list, const char *hostname, int port,
170 int socktype, int family)
172 struct server_entry *entry;
174 entry = new_server_entry(list);
177 entry->socktype = socktype;
178 entry->family = family;
179 entry->hostname = strdup(hostname);
180 if (entry->hostname == NULL)
187 static krb5_error_code
188 locate_srv_conf_1(krb5_context context, const krb5_data *realm,
189 const char * name, struct serverlist *serverlist,
190 int socktype, int udpport, int sec_udpport)
192 const char *realm_srv_names[4];
193 char **hostlist, *host, *port, *cp;
194 krb5_error_code code;
197 Tprintf ("looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
198 realm->data, name, ntohs (udpport), ntohs (sec_udpport));
200 if ((host = malloc(realm->length + 1)) == NULL)
203 strncpy(host, realm->data, realm->length);
204 host[realm->length] = '\0';
207 realm_srv_names[0] = KRB5_CONF_REALMS;
208 realm_srv_names[1] = host;
209 realm_srv_names[2] = name;
210 realm_srv_names[3] = 0;
212 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
216 Tprintf ("config file lookup failed: %s\n",
217 error_message(code));
218 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
219 code = KRB5_REALM_UNKNOWN;
224 while (hostlist && hostlist[count])
226 Tprintf ("found %d entries under 'kdc'\n", count);
229 profile_free_list(hostlist);
230 serverlist->nservers = 0;
234 for (i=0; hostlist[i]; i++) {
238 Tprintf ("entry %d is '%s'\n", i, host);
239 /* Find port number, and strip off any excess characters. */
240 if (*host == '[' && (cp = strchr(host, ']')))
243 cp = host + strcspn(host, " \t:");
244 port = (*cp == ':') ? cp + 1 : NULL;
251 l = strtoul (port, &endptr, 10);
252 if (endptr == NULL || *endptr != 0)
257 /* L is unsigned, don't need to check <0. */
267 /* If the hostname was in brackets, strip those off now. */
268 if (*host == '[' && (cp = strchr(host, ']'))) {
273 code = add_host_to_list(serverlist, host, p1, socktype, AF_UNSPEC);
274 /* Second port is for IPv4 UDP only, and should possibly go away as
275 * it was originally a krb4 compatibility measure. */
276 if (code == 0 && p2 != 0 &&
277 (socktype == 0 || socktype == SOCK_DGRAM))
278 code = add_host_to_list(serverlist, host, p2, SOCK_DGRAM, AF_INET);
284 profile_free_list(hostlist);
289 static krb5_error_code
290 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
291 const char *name, struct serverlist *al, int udpport,
296 ret = locate_srv_conf_1(context, realm, name, al, 0, udpport, sec_udpport);
299 if (al->nservers == 0) /* Couldn't resolve any KDC names */
300 return KRB5_REALM_CANT_RESOLVE;
305 #ifdef KRB5_DNS_LOOKUP
306 static krb5_error_code
307 locate_srv_dns_1(const krb5_data *realm, const char *service,
308 const char *protocol, struct serverlist *serverlist)
310 struct srv_dns_entry *head = NULL, *entry = NULL;
311 krb5_error_code code = 0;
314 code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
321 /* Check for the "." case indicating no support. */
322 if (head->next == NULL && head->host[0] == '\0') {
323 code = KRB5_ERR_NO_SERVICE;
327 for (entry = head; entry != NULL; entry = entry->next) {
328 socktype = (strcmp(protocol, "_tcp") == 0) ? SOCK_STREAM : SOCK_DGRAM;
329 code = add_host_to_list(serverlist, entry->host, htons(entry->port),
330 socktype, AF_UNSPEC);
336 krb5int_free_srv_dns_data(head);
341 #include <krb5/locate_plugin.h>
344 static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/libkrb5", NULL }; /* should be a list */
346 static const char *objdirs[] = { LIBDIR "/krb5/plugins/libkrb5", NULL };
349 struct module_callback_data {
351 struct serverlist *list;
355 module_callback(void *cbdata, int socktype, struct sockaddr *sa)
357 struct module_callback_data *d = cbdata;
360 if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
362 if (sa->sa_family == AF_INET)
363 addrlen = sizeof(struct sockaddr_in);
364 else if (sa->sa_family == AF_INET6)
365 addrlen = sizeof(struct sockaddr_in6);
368 if (add_addr_to_list(d->list, socktype, sa->sa_family, addrlen,
370 /* Assumes only error is ENOMEM. */
377 static krb5_error_code
378 module_locate_server(krb5_context ctx, const krb5_data *realm,
379 struct serverlist *serverlist,
380 enum locate_service_type svc, int socktype)
382 struct krb5plugin_service_locate_result *res = NULL;
383 krb5_error_code code;
384 struct krb5plugin_service_locate_ftable *vtbl = NULL;
386 char *realmz; /* NUL-terminated realm */
388 struct module_callback_data cbdata = { 0, };
391 Tprintf("in module_locate_server\n");
392 cbdata.list = serverlist;
393 if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) {
395 code = krb5int_open_plugin_dirs (objdirs, NULL, &ctx->libkrb5_plugins,
398 return KRB5_PLUGIN_NO_HANDLE;
401 code = krb5int_get_plugin_dir_data (&ctx->libkrb5_plugins,
402 "service_locator", &ptrs, &ctx->err);
404 Tprintf("error looking up plugin symbols: %s\n",
405 (msg = krb5_get_error_message(ctx, code)));
406 krb5_free_error_message(ctx, msg);
407 return KRB5_PLUGIN_NO_HANDLE;
410 if (realm->length >= UINT_MAX) {
411 krb5int_free_plugin_dir_data(ptrs);
414 realmz = malloc(realm->length + 1);
415 if (realmz == NULL) {
416 krb5int_free_plugin_dir_data(ptrs);
419 memcpy(realmz, realm->data, realm->length);
420 realmz[realm->length] = '\0';
421 for (i = 0; ptrs[i]; i++) {
425 Tprintf("element %d is %p\n", i, ptrs[i]);
427 /* For now, don't keep the plugin data alive. For long-lived
428 contexts, it may be desirable to change that later. */
429 code = vtbl->init(ctx, &blob);
433 code = vtbl->lookup(blob, svc, realmz,
434 (socktype != 0) ? socktype : SOCK_DGRAM, AF_UNSPEC,
435 module_callback, &cbdata);
436 /* Also ask for TCP addresses if we got UDP addresses and want both. */
437 if (code == 0 && socktype == 0) {
438 code = vtbl->lookup(blob, svc, realmz, SOCK_STREAM, AF_UNSPEC,
439 module_callback, &cbdata);
440 if (code == KRB5_PLUGIN_NO_HANDLE)
444 if (code == KRB5_PLUGIN_NO_HANDLE) {
445 /* Module passes, keep going. */
447 Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)\n");
451 /* Module encountered an actual error. */
452 Tprintf("plugin lookup routine returned error %d: %s\n",
453 code, error_message(code));
455 krb5int_free_plugin_dir_data (ptrs);
460 if (ptrs[i] == NULL) {
461 Tprintf("ran off end of plugin list\n");
463 krb5int_free_plugin_dir_data (ptrs);
464 return KRB5_PLUGIN_NO_HANDLE;
466 Tprintf("stopped with plugin #%d, res=%p\n", i, res);
468 /* Got something back, yippee. */
469 Tprintf("now have %lu addrs in list %p\n",
470 (unsigned long) serverlist->nservers, serverlist);
472 krb5int_free_plugin_dir_data (ptrs);
476 static krb5_error_code
477 prof_locate_server(krb5_context context, const krb5_data *realm,
478 struct serverlist *serverlist, enum locate_service_type svc,
481 const char *profname;
482 int dflport1, dflport2 = 0;
483 struct servent *serv;
486 case locate_service_kdc:
487 profname = KRB5_CONF_KDC;
488 /* We used to use /etc/services for these, but enough systems
489 have old, crufty, wrong settings that this is probably
492 dflport1 = htons(KRB5_DEFAULT_PORT);
493 dflport2 = htons(KRB5_DEFAULT_SEC_PORT);
495 case locate_service_master_kdc:
496 profname = KRB5_CONF_MASTER_KDC;
498 case locate_service_kadmin:
499 profname = KRB5_CONF_ADMIN_SERVER;
500 dflport1 = htons(DEFAULT_KADM5_PORT);
502 case locate_service_krb524:
503 profname = KRB5_CONF_KRB524_SERVER;
504 serv = getservbyname(KRB524_SERVICE, "udp");
505 dflport1 = serv ? serv->s_port : htons (KRB524_PORT);
507 case locate_service_kpasswd:
508 profname = KRB5_CONF_KPASSWD_SERVER;
509 dflport1 = htons(DEFAULT_KPASSWD_PORT);
512 return EBUSY; /* XXX */
515 return locate_srv_conf_1(context, realm, profname, serverlist, socktype,
519 #ifdef KRB5_DNS_LOOKUP
520 static krb5_error_code
521 dns_locate_server(krb5_context context, const krb5_data *realm,
522 struct serverlist *serverlist, enum locate_service_type svc,
526 int use_dns = _krb5_use_dns_kdc(context);
527 krb5_error_code code;
530 return KRB5_PLUGIN_NO_HANDLE;
533 case locate_service_kdc:
534 dnsname = "_kerberos";
536 case locate_service_master_kdc:
537 dnsname = "_kerberos-master";
539 case locate_service_kadmin:
540 dnsname = "_kerberos-adm";
542 case locate_service_krb524:
545 case locate_service_kpasswd:
546 dnsname = "_kpasswd";
549 return KRB5_PLUGIN_NO_HANDLE;
553 if (socktype == SOCK_DGRAM || socktype == 0) {
554 code = locate_srv_dns_1(realm, dnsname, "_udp", serverlist);
556 Tprintf("dns udp lookup returned error %d\n", code);
558 if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
559 code = locate_srv_dns_1(realm, dnsname, "_tcp", serverlist);
561 Tprintf("dns tcp lookup returned error %d\n", code);
565 #endif /* KRB5_DNS_LOOKUP */
568 * Wrapper function for the various backends
572 k5_locate_server(krb5_context context, const krb5_data *realm,
573 struct serverlist *serverlist, enum locate_service_type svc,
576 krb5_error_code code;
577 struct serverlist al = SERVERLIST_INIT;
581 if (realm == NULL || realm->data == NULL || realm->data[0] == 0) {
582 krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
583 "Cannot find KDC for invalid realm name \"\"");
584 return KRB5_REALM_CANT_RESOLVE;
587 code = module_locate_server(context, realm, &al, svc, socktype);
588 Tprintf("module_locate_server returns %d\n", code);
589 if (code == KRB5_PLUGIN_NO_HANDLE) {
591 * We always try the local file before DNS. Note that there
592 * is no way to indicate "service not available" via the
596 code = prof_locate_server(context, realm, &al, svc, socktype);
598 #ifdef KRB5_DNS_LOOKUP
599 if (code) { /* Try DNS for all profile errors? */
600 krb5_error_code code2;
601 code2 = dns_locate_server(context, realm, &al, svc, socktype);
602 if (code2 != KRB5_PLUGIN_NO_HANDLE)
605 #endif /* KRB5_DNS_LOOKUP */
607 /* We could put more heuristics here, like looking up a hostname
608 of "kerberos."+REALM, etc. */
611 Tprintf ("krb5int_locate_server found %d addresses\n",
614 Tprintf ("krb5int_locate_server returning error code %d/%s\n",
615 code, error_message(code));
617 k5_free_serverlist(&al);
620 if (al.nservers == 0) { /* No good servers */
621 k5_free_serverlist(&al);
622 krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
623 _("Cannot resolve servers for KDC in realm "
624 "\"%.*s\""), realm->length, realm->data);
625 return KRB5_REALM_CANT_RESOLVE;
632 k5_locate_kdc(krb5_context context, const krb5_data *realm,
633 struct serverlist *serverlist, int get_masters, int socktype)
635 enum locate_service_type stype;
637 stype = get_masters ? locate_service_master_kdc : locate_service_kdc;
638 return k5_locate_server(context, realm, serverlist, stype, socktype);