2 * lib/krb5/os/locate_kdc.c
4 * Copyright 1990,2000,2001 by the 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.
34 #ifdef KRB5_DNS_LOOKUP
38 #include <arpa/inet.h>
39 #include <arpa/nameser.h>
47 #include "fake-addrinfo.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 (context, name, defalt)
77 code = profile_get_string(context->profile, "libdefaults",
79 if (value == 0 && code == 0)
80 code = profile_get_string(context->profile, "libdefaults",
81 "dns_fallback", 0, 0, &value);
88 use_dns = _krb5_conf_boolean(value);
89 profile_release_string(value);
94 _krb5_use_dns_kdc(context)
97 return maybe_use_dns (context, "dns_lookup_kdc", DEFAULT_LOOKUP_KDC);
101 _krb5_use_dns_realm(context)
102 krb5_context context;
104 return maybe_use_dns (context, "dns_lookup_realm", DEFAULT_LOOKUP_REALM);
107 #endif /* KRB5_DNS_LOOKUP */
109 static int get_port (const char *service, int stream, int defalt)
111 struct addrinfo hints = { 0 };
115 hints.ai_family = PF_INET;
116 hints.ai_socktype = stream ? SOCK_STREAM : SOCK_DGRAM;
117 err = getaddrinfo (NULL, service, &hints, &ai);
118 if (err == 0 && ai != 0) {
119 if (ai->ai_addr->sa_family == AF_INET) {
120 int port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
126 /* Any error - don't complain, just use default. */
127 return htons (defalt);
131 grow_list (struct addrlist *lp, int nmore)
134 int newspace = lp->space + nmore;
135 size_t newsize = newspace * sizeof (struct addrlist);
136 struct sockaddr **newaddrs;
138 /* NULL check a concession to SunOS4 compatibility for now; not
139 required for pure ANSI support. */
141 newaddrs = realloc (lp->addrs, newsize);
143 newaddrs = malloc (newsize);
145 if (newaddrs == NULL)
147 for (i = lp->space; i < newspace; i++)
149 lp->addrs = newaddrs;
150 lp->space = newspace;
154 /* Free up everything pointed to by the addrlist structure, but don't
155 free the structure itself. */
157 krb5int_free_addrlist (struct addrlist *lp)
160 for (i = 0; i < lp->naddrs; i++)
164 lp->naddrs = lp->space = 0;
166 #define free_list krb5int_free_addrlist
169 add_sockaddr_to_list (struct addrlist *lp, const struct sockaddr *addr,
172 struct sockaddr *copy;
176 char name[NI_MAXHOST];
178 fprintf (stderr, "\tadding sockaddr family %2d, len %d", addr->sa_family,
181 err = getnameinfo (addr, len, name, sizeof (name), NULL, 0,
182 NI_NUMERICHOST | NI_NUMERICSERV);
184 fprintf (stderr, "\t%s", name);
185 fprintf (stderr, "\n");
188 if (lp->naddrs == lp->space) {
189 err = grow_list (lp, 1);
192 fprintf (stderr, "grow_list failed %d\n", err);
204 memcpy (copy, addr, len);
205 lp->addrs[lp->naddrs++] = copy;
207 fprintf (stderr, "count is now %d\n", lp->naddrs);
212 static int translate_ai_error (int err)
217 #ifdef EAI_ADDRFAMILY
224 /* All of these indicate bad inputs to getaddrinfo. */
227 /* Translate to standard errno code. */
230 /* Translate to standard errno code. */
232 #if EAI_NODATA != EAI_NONAME
236 /* Name not known or no address data, but no error. Do
241 /* System error, obviously. */
245 /* An error code we haven't handled? */
250 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a)
253 r = add_sockaddr_to_list (lp, a->ai_addr, a->ai_addrlen);
255 switch (a->ai_socktype) {
257 fprintf(stderr, "\tdgram\n");
260 fprintf(stderr, "\tstream\n");
263 fprintf(stderr, "\traw\n");
268 fprintf(stderr, "\tsocket type %d\n", a->ai_socktype);
275 static void set_port_num (struct sockaddr *addr, int num)
277 switch (addr->sa_family) {
279 ((struct sockaddr_in *)addr)->sin_port = num;
281 #ifdef KRB5_USE_INET6
283 ((struct sockaddr_in6 *)addr)->sin6_port = num;
290 add_host_to_list (struct addrlist *lp, const char *hostname,
291 int port, int secport)
293 struct addrinfo *addrs, *a, hint;
297 fprintf (stderr, "adding hostname %s, ports %d,%d\n", hostname,
298 ntohs (port), ntohs (secport));
301 memset(&hint, 0, sizeof(hint));
302 hint.ai_socktype = SOCK_DGRAM;
303 err = getaddrinfo (hostname, NULL, &hint, &addrs);
305 return translate_ai_error (err);
306 for (a = addrs; a; a = a->ai_next) {
307 set_port_num (a->ai_addr, port);
308 err = add_addrinfo_to_list (lp, a);
315 set_port_num (a->ai_addr, secport);
316 err = add_addrinfo_to_list (lp, a);
320 freeaddrinfo (addrs);
325 * returns count of number of addresses found
326 * if master is non-NULL, it is filled in with the index of
330 static krb5_error_code
331 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
332 const char * name, struct addrlist *addrlist,
333 int get_masters, int udpport, int sec_udpport)
335 const char *realm_srv_names[4];
336 char **masterlist, **hostlist, *host, *port, *cp;
337 krb5_error_code code;
338 int i, j, count, ismaster;
342 "looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
343 realm->data, name, ntohs (udpport), ntohs (sec_udpport));
346 if ((host = malloc(realm->length + 1)) == NULL)
349 strncpy(host, realm->data, realm->length);
350 host[realm->length] = '\0';
355 realm_srv_names[0] = "realms";
356 realm_srv_names[1] = host;
357 realm_srv_names[2] = name;
358 realm_srv_names[3] = 0;
360 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
363 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
364 code = KRB5_REALM_UNKNOWN;
370 while (hostlist && hostlist[count])
374 profile_free_list(hostlist);
376 addrlist->naddrs = 0;
381 realm_srv_names[0] = "realms";
382 realm_srv_names[1] = host;
383 realm_srv_names[2] = "admin_server";
384 realm_srv_names[3] = 0;
386 code = profile_get_values(context->profile, realm_srv_names,
392 for (i=0; masterlist[i]; i++) {
393 host = masterlist[i];
396 * Strip off excess whitespace
398 cp = strchr(host, ' ');
401 cp = strchr(host, '\t');
404 cp = strchr(host, ':');
413 /* at this point, if master is non-NULL, then either the master kdc
414 is required, and there is one, or the master kdc is not required,
415 and there may or may not be one. */
417 #ifdef HAVE_NETINET_IN_H
422 for (i=0; hostlist[i]; i++) {
427 * Strip off excess whitespace
429 cp = strchr(host, ' ');
432 cp = strchr(host, '\t');
435 port = strchr(host, ':');
443 for (j=0; masterlist[j]; j++) {
444 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
450 if (get_masters && !ismaster)
457 l = strtoul (port, &endptr, 10);
458 if (endptr == NULL || *endptr != 0)
463 /* L is unsigned, don't need to check <0. */
473 code = add_host_to_list (addrlist, hostlist[i], p1, p2);
476 fprintf (stderr, "error %d returned from add_host_to_list\n", code);
479 profile_free_list (hostlist);
481 profile_free_list (masterlist);
487 profile_free_list(hostlist);
489 profile_free_list(masterlist);
495 static krb5_error_code
496 krb5_locate_srv_conf(context, realm, name, al, get_masters,
497 udpport, sec_udpport)
498 krb5_context context;
499 const krb5_data *realm;
503 int udpport, sec_udpport;
507 ret = krb5_locate_srv_conf_1 (context, realm, name, al,
508 get_masters, udpport, sec_udpport);
511 if (al->naddrs == 0) /* Couldn't resolve any KDC names */
512 return KRB5_REALM_CANT_RESOLVE;
517 #ifdef KRB5_DNS_LOOKUP
520 * Lookup a KDC via DNS SRV records
523 static krb5_error_code
524 krb5_locate_srv_dns_1 (const krb5_data *realm,
526 const char *protocol,
527 struct addrlist *addrlist)
530 unsigned char bytes[2048];
533 unsigned char *p=NULL;
534 char host[MAX_DNS_NAMELEN], *h;
536 int priority, weight, size, len, numanswers, numqueries, rdlen;
538 const int hdrsize = sizeof(HEADER);
539 struct srv_dns_entry {
540 struct srv_dns_entry *next;
547 struct srv_dns_entry *head = NULL;
548 struct srv_dns_entry *srv = NULL, *entry = NULL;
549 krb5_error_code code = 0;
552 * First off, build a query of the form:
554 * service.protocol.realm
556 * which will most likely be something like:
558 * _kerberos._udp.REALM
562 if ( strlen(service) + strlen(protocol) + realm->length + 6
565 sprintf(host, "%s.%s.%.*s", service, protocol, (int) realm->length,
568 /* Realm names don't (normally) end with ".", but if the query
569 doesn't end with "." and doesn't get an answer as is, the
570 resolv code will try appending the local domain. Since the
571 realm names are absolutes, let's stop that.
573 But only if a name has been specified. If we are performing
574 a search on the prefix alone then the intention is to allow
575 the local domain or domain search lists to be expanded. */
577 h = host + strlen (host);
578 if ((h > host) && (h[-1] != '.') && ((h - host + 1) < sizeof(host)))
582 fprintf (stderr, "sending DNS SRV query for %s\n", host);
585 size = res_search(host, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes));
591 * We got an answer! First off, parse the header and figure out how
592 * many answers we got back.
597 numqueries = ntohs(answer.hdr.qdcount);
598 numanswers = ntohs(answer.hdr.ancount);
603 * We need to skip over all of the questions, so we have to iterate
604 * over every query record. dn_expand() is able to tell us the size
605 * of compress DNS names, so we use it.
608 #define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto out
609 #define CHECK(x,y) if (x + y > size + answer.bytes) goto out
610 #define NTOHSP(x,y) x[0] << 8 | x[1]; x += y
612 while (numqueries--) {
613 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
616 INCR_CHECK(p, len + 4);
620 * We're now pointing at the answer records. Only process them if
621 * they're actually T_SRV records (they might be CNAME records,
624 * But in a DNS reply, if you get a CNAME you always get the associated
625 * "real" RR for that CNAME. RFC 1034, 3.6.2:
627 * CNAME RRs cause special action in DNS software. When a name server
628 * fails to find a desired RR in the resource set associated with the
629 * domain name, it checks to see if the resource set consists of a CNAME
630 * record with a matching class. If so, the name server includes the CNAME
631 * record in the response and restarts the query at the domain name
632 * specified in the data field of the CNAME record. The one exception to
633 * this rule is that queries which match the CNAME type are not restarted.
635 * In other words, CNAMEs do not need to be expanded by the client.
638 while (numanswers--) {
640 /* First is the name; use dn_expand to get the compressed size */
641 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
646 /* Next is the query type */
650 /* Next is the query class; also skip over 4 byte TTL */
654 /* Record data length */
660 * If this is an SRV record, process it. Record format is:
668 if (class == C_IN && type == T_SRV) {
670 priority = NTOHSP(p,2);
672 weight = NTOHSP(p,2);
675 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
681 * We got everything! Insert it into our list, but make sure
682 * it's in the right order. Right now we don't do anything
683 * with the weight field
686 srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
690 srv->priority = priority;
691 srv->weight = weight;
693 srv->host = strdup(host);
695 if (head == NULL || head->priority > srv->priority) {
700 * This is confusing. Only insert an entry into this
702 * The next person has a higher priority (lower priorities
705 * There is no next entry (we're at the end)
707 for (entry = head; entry != NULL; entry = entry->next)
709 entry->next->priority > srv->priority) ||
710 entry->next == NULL) {
711 srv->next = entry->next;
716 INCR_CHECK(p, rdlen);
720 * Okay! Now we've got a linked list of entries sorted by
721 * priority. Start looking up A records and returning
729 fprintf (stderr, "walking answer list:\n");
731 for (entry = head; entry != NULL; entry = entry->next) {
733 fprintf (stderr, "\tport=%d host=%s\n", entry->port, entry->host);
735 code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0);
740 fprintf (stderr, "[end]\n");
743 for (entry = head; entry != NULL; ) {
760 static krb5_error_code
761 krb5_locate_srv_dns(const krb5_data *realm,
762 const char *service, const char *protocol,
765 return krb5_locate_srv_dns_1 (realm, service, protocol, al);
768 #endif /* KRB5_DNS_LOOKUP */
771 * Wrapper function for the two backends
775 krb5int_locate_server (krb5_context context, const krb5_data *realm,
776 struct addrlist *addrlist,
778 const char *profname, const char *dnsname,
780 /* network order port numbers! */
781 int dflport1, int dflport2)
783 krb5_error_code code;
784 struct addrlist al = ADDRLIST_INIT;
789 * We always try the local file first
792 code = krb5_locate_srv_conf_1(context, realm, profname, &al, get_masters,
795 #ifdef KRB5_DNS_LOOKUP
796 if (code && dnsname != 0) {
797 int use_dns = _krb5_use_dns_kdc(context);
799 code = krb5_locate_srv_dns_1(realm, dnsname,
800 is_stream ? "_tcp" : "_udp", &al);
802 #endif /* KRB5_DNS_LOOKUP */
805 fprintf (stderr, "krb5int_locate_server found %d addresses\n",
808 fprintf (stderr, "krb5int_locate_server returning error code %d\n",
816 if (al.naddrs == 0) { /* No good servers */
819 return KRB5_REALM_CANT_RESOLVE;
826 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
827 struct addrlist *addrlist,
830 int udpport, sec_udpport;
832 udpport = get_port (KDC_PORTNAME, 0, KRB5_DEFAULT_PORT);
833 sec_udpport = get_port (KDC_SECONDARY_PORTNAME, 0,
834 (udpport == htons (KRB5_DEFAULT_PORT)
835 ? KRB5_DEFAULT_SEC_PORT
836 : KRB5_DEFAULT_PORT));
837 if (sec_udpport == udpport)
840 return krb5int_locate_server (context, realm, addrlist, get_masters, "kdc",
844 0, udpport, sec_udpport);