2 * lib/krb5/os/locate_kdc.c
4 * Copyright 1990,2000,2001,2002 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.
31 #include "fake-addrinfo.h"
35 #ifdef KRB5_DNS_LOOKUP
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 */
103 static int get_port (const char *service, int stream, int defalt)
105 #if 0 /* Only used for "kerberos" and "kerberos-sec", and we want the
106 right port numbers even on the OSes that botch the entries in
107 /etc/services. So don't bother with the lookup, except maybe
108 to produce a warning. */
109 struct addrinfo hints = { 0 };
113 hints.ai_family = PF_INET;
114 hints.ai_socktype = stream ? SOCK_STREAM : SOCK_DGRAM;
115 err = getaddrinfo (NULL, service, &hints, &ai);
116 if (err == 0 && ai != 0) {
117 if (ai->ai_addr->sa_family == AF_INET) {
118 int port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
125 /* Any error - don't complain, just use default. */
126 return htons (defalt);
130 krb5int_grow_addrlist (struct addrlist *lp, int nmore)
133 int newspace = lp->space + nmore;
134 size_t newsize = newspace * sizeof (struct addrlist);
135 struct addrinfo **newaddrs;
137 /* NULL check a concession to SunOS4 compatibility for now; not
138 required for pure ANSI support. */
140 newaddrs = realloc (lp->addrs, newsize);
142 newaddrs = malloc (newsize);
144 if (newaddrs == NULL)
146 for (i = lp->space; i < newspace; i++)
148 lp->addrs = newaddrs;
149 lp->space = newspace;
152 #define grow_list krb5int_grow_addrlist
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++)
161 freeaddrinfo (lp->addrs[i]);
164 lp->naddrs = lp->space = 0;
166 #define free_list krb5int_free_addrlist
168 static int translate_ai_error (int err)
173 #ifdef EAI_ADDRFAMILY
180 /* All of these indicate bad inputs to getaddrinfo. */
183 /* Translate to standard errno code. */
186 /* Translate to standard errno code. */
188 #if EAI_NODATA != EAI_NONAME
192 /* Name not known or no address data, but no error. Do
197 /* System error, obviously. */
201 /* An error code we haven't handled? */
206 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a)
211 switch (a->ai_socktype) {
213 fprintf(stderr, "\tdgram\n");
216 fprintf(stderr, "\tstream\n");
219 fprintf(stderr, "\traw\n");
224 fprintf(stderr, "\tsocket type %d\n", a->ai_socktype);
229 if (lp->naddrs == lp->space) {
230 err = grow_list (lp, 1);
233 fprintf (stderr, "grow_list failed %d\n", err);
238 lp->addrs[lp->naddrs++] = a;
241 fprintf (stderr, "count is now %d\n", lp->naddrs);
246 #define add_host_to_list krb5int_add_host_to_list
249 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
250 int port, int secport,
251 int socktype, int family)
253 struct addrinfo *addrs, *a, *anext, hint;
255 char portbuf[10], secportbuf[10];
258 fprintf (stderr, "adding hostname %s, ports %d,%d\n", hostname,
259 ntohs (port), ntohs (secport));
262 memset(&hint, 0, sizeof(hint));
263 hint.ai_family = family;
264 hint.ai_socktype = socktype;
265 sprintf(portbuf, "%d", ntohs(port));
266 sprintf(secportbuf, "%d", ntohs(secport));
267 err = getaddrinfo (hostname, portbuf, &hint, &addrs);
269 return translate_ai_error (err);
271 for (a = addrs; a != 0 && err == 0; a = anext) {
273 err = add_addrinfo_to_list (lp, a);
275 if (err || secport == 0)
278 socktype = SOCK_DGRAM;
279 else if (socktype != SOCK_DGRAM)
281 hint.ai_family = AF_INET;
282 err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
285 return translate_ai_error (err);
290 for (a = addrs; a != 0 && err == 0; a = anext) {
292 err = add_addrinfo_to_list (lp, a);
296 freeaddrinfo (anext);
301 * returns count of number of addresses found
302 * if master is non-NULL, it is filled in with the index of
306 static krb5_error_code
307 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
308 const char * name, struct addrlist *addrlist,
309 int get_masters, int socktype,
310 int udpport, int sec_udpport, int family)
312 const char *realm_srv_names[4];
313 char **masterlist, **hostlist, *host, *port, *cp;
314 krb5_error_code code;
315 int i, j, count, ismaster;
319 "looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
320 realm->data, name, ntohs (udpport), ntohs (sec_udpport));
323 if ((host = malloc(realm->length + 1)) == NULL)
326 strncpy(host, realm->data, realm->length);
327 host[realm->length] = '\0';
332 realm_srv_names[0] = "realms";
333 realm_srv_names[1] = host;
334 realm_srv_names[2] = name;
335 realm_srv_names[3] = 0;
337 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
341 fprintf (stderr, "config file lookup failed: %s\n",
342 error_message(code));
344 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
345 code = KRB5_REALM_UNKNOWN;
351 while (hostlist && hostlist[count])
354 fprintf (stderr, "found %d entries under 'kdc'\n", count);
358 profile_free_list(hostlist);
360 addrlist->naddrs = 0;
365 realm_srv_names[0] = "realms";
366 realm_srv_names[1] = host;
367 realm_srv_names[2] = "admin_server";
368 realm_srv_names[3] = 0;
370 code = profile_get_values(context->profile, realm_srv_names,
376 for (i=0; masterlist[i]; i++) {
377 host = masterlist[i];
380 * Strip off excess whitespace
382 cp = strchr(host, ' ');
385 cp = strchr(host, '\t');
388 cp = strchr(host, ':');
397 /* at this point, if master is non-NULL, then either the master kdc
398 is required, and there is one, or the master kdc is not required,
399 and there may or may not be one. */
401 #ifdef HAVE_NETINET_IN_H
406 for (i=0; hostlist[i]; i++) {
411 fprintf (stderr, "entry %d is '%s'\n", i, host);
414 * Strip off excess whitespace
416 cp = strchr(host, ' ');
419 cp = strchr(host, '\t');
422 port = strchr(host, ':');
430 for (j=0; masterlist[j]; j++) {
431 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
437 if (get_masters && !ismaster)
444 l = strtoul (port, &endptr, 10);
445 if (endptr == NULL || *endptr != 0)
450 /* L is unsigned, don't need to check <0. */
461 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
464 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
467 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
468 SOCK_STREAM, family);
472 fprintf (stderr, "error %d returned from add_host_to_list\n", code);
475 profile_free_list (hostlist);
477 profile_free_list (masterlist);
483 profile_free_list(hostlist);
485 profile_free_list(masterlist);
491 static krb5_error_code
492 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
493 const char *name, struct addrlist *al, int get_masters,
494 int udpport, int sec_udpport)
498 ret = krb5_locate_srv_conf_1 (context, realm, name, al,
499 get_masters, 0, udpport, sec_udpport, 0);
502 if (al->naddrs == 0) /* Couldn't resolve any KDC names */
503 return KRB5_REALM_CANT_RESOLVE;
508 #ifdef KRB5_DNS_LOOKUP
511 * Lookup a KDC via DNS SRV records
514 static krb5_error_code
515 krb5_locate_srv_dns_1 (const krb5_data *realm,
517 const char *protocol,
518 struct addrlist *addrlist,
522 unsigned char bytes[2048];
525 unsigned char *p=NULL;
526 char host[MAX_DNS_NAMELEN], *h;
528 int priority, weight, size, len, numanswers, numqueries, rdlen;
530 const int hdrsize = sizeof(HEADER);
531 struct srv_dns_entry {
532 struct srv_dns_entry *next;
539 struct srv_dns_entry *head = NULL;
540 struct srv_dns_entry *srv = NULL, *entry = NULL;
541 krb5_error_code code = 0;
544 * First off, build a query of the form:
546 * service.protocol.realm
548 * which will most likely be something like:
550 * _kerberos._udp.REALM
554 if ( strlen(service) + strlen(protocol) + realm->length + 6
557 sprintf(host, "%s.%s.%.*s", service, protocol, (int) realm->length,
560 /* Realm names don't (normally) end with ".", but if the query
561 doesn't end with "." and doesn't get an answer as is, the
562 resolv code will try appending the local domain. Since the
563 realm names are absolutes, let's stop that.
565 But only if a name has been specified. If we are performing
566 a search on the prefix alone then the intention is to allow
567 the local domain or domain search lists to be expanded. */
569 h = host + strlen (host);
570 if ((h > host) && (h[-1] != '.') && ((h - host + 1) < sizeof(host)))
574 fprintf (stderr, "sending DNS SRV query for %s\n", host);
577 size = res_search(host, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes));
579 if ((size < hdrsize) || (size > sizeof(answer.bytes)))
583 * We got an answer! First off, parse the header and figure out how
584 * many answers we got back.
589 numqueries = ntohs(answer.hdr.qdcount);
590 numanswers = ntohs(answer.hdr.ancount);
595 * We need to skip over all of the questions, so we have to iterate
596 * over every query record. dn_expand() is able to tell us the size
597 * of compress DNS names, so we use it.
600 #define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto out
601 #define CHECK(x,y) if (x + y > size + answer.bytes) goto out
602 #define NTOHSP(x,y) x[0] << 8 | x[1]; x += y
604 while (numqueries--) {
605 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
608 INCR_CHECK(p, len + 4);
612 * We're now pointing at the answer records. Only process them if
613 * they're actually T_SRV records (they might be CNAME records,
616 * But in a DNS reply, if you get a CNAME you always get the associated
617 * "real" RR for that CNAME. RFC 1034, 3.6.2:
619 * CNAME RRs cause special action in DNS software. When a name server
620 * fails to find a desired RR in the resource set associated with the
621 * domain name, it checks to see if the resource set consists of a CNAME
622 * record with a matching class. If so, the name server includes the CNAME
623 * record in the response and restarts the query at the domain name
624 * specified in the data field of the CNAME record. The one exception to
625 * this rule is that queries which match the CNAME type are not restarted.
627 * In other words, CNAMEs do not need to be expanded by the client.
630 while (numanswers--) {
632 /* First is the name; use dn_expand to get the compressed size */
633 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
638 /* Next is the query type */
642 /* Next is the query class; also skip over 4 byte TTL */
644 rrclass = NTOHSP(p,6);
646 /* Record data length */
652 * If this is an SRV record, process it. Record format is:
660 if (rrclass == C_IN && type == T_SRV) {
662 priority = NTOHSP(p,2);
664 weight = NTOHSP(p,2);
667 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
673 * We got everything! Insert it into our list, but make sure
674 * it's in the right order. Right now we don't do anything
675 * with the weight field
678 srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
682 srv->priority = priority;
683 srv->weight = weight;
685 srv->host = strdup(host);
687 if (head == NULL || head->priority > srv->priority) {
692 * This is confusing. Only insert an entry into this
694 * The next person has a higher priority (lower priorities
697 * There is no next entry (we're at the end)
699 for (entry = head; entry != NULL; entry = entry->next)
701 entry->next->priority > srv->priority) ||
702 entry->next == NULL) {
703 srv->next = entry->next;
708 INCR_CHECK(p, rdlen);
712 * Okay! Now we've got a linked list of entries sorted by
713 * priority. Start looking up A records and returning
721 fprintf (stderr, "walking answer list:\n");
723 for (entry = head; entry != NULL; entry = entry->next) {
725 fprintf (stderr, "\tport=%d host=%s\n", entry->port, entry->host);
727 code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
728 (strcmp("_tcp", protocol)
730 : SOCK_STREAM), family);
735 fprintf (stderr, "[end]\n");
738 for (entry = head; entry != NULL; ) {
755 static krb5_error_code
756 krb5_locate_srv_dns(const krb5_data *realm,
757 const char *service, const char *protocol,
760 return krb5_locate_srv_dns_1 (realm, service, protocol, al, 0);
763 #endif /* KRB5_DNS_LOOKUP */
766 * Wrapper function for the two backends
770 krb5int_locate_server (krb5_context context, const krb5_data *realm,
771 struct addrlist *addrlist,
773 const char *profname, const char *dnsname,
775 /* network order port numbers! */
776 int dflport1, int dflport2,
779 krb5_error_code code;
780 struct addrlist al = ADDRLIST_INIT;
785 * We always try the local file first
788 code = krb5_locate_srv_conf_1(context, realm, profname, &al, get_masters,
789 socktype, dflport1, dflport2, family);
791 #ifdef KRB5_DNS_LOOKUP
792 if (code && dnsname != 0) {
793 int use_dns = _krb5_use_dns_kdc(context);
796 if (socktype == SOCK_DGRAM || socktype == 0) {
797 code = krb5_locate_srv_dns_1(realm, dnsname, "_udp",
801 fprintf(stderr, "dns udp lookup returned error %d\n",
805 if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
806 code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp",
810 fprintf(stderr, "dns tcp lookup returned error %d\n",
816 #endif /* KRB5_DNS_LOOKUP */
819 fprintf (stderr, "krb5int_locate_server found %d addresses\n",
822 fprintf (stderr, "krb5int_locate_server returning error code %d\n",
830 if (al.naddrs == 0) { /* No good servers */
833 return KRB5_REALM_CANT_RESOLVE;
840 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
841 struct addrlist *addrlist,
842 int get_masters, int socktype, int family)
844 int udpport, sec_udpport;
846 udpport = get_port (KDC_PORTNAME, 0, KRB5_DEFAULT_PORT);
847 if (socktype == SOCK_STREAM)
850 sec_udpport = get_port (KDC_SECONDARY_PORTNAME, 0,
851 (udpport == htons (KRB5_DEFAULT_PORT)
852 ? KRB5_DEFAULT_SEC_PORT
853 : KRB5_DEFAULT_PORT));
854 if (sec_udpport == udpport)
858 return krb5int_locate_server(context, realm, addrlist, get_masters, "kdc",
862 socktype, udpport, sec_udpport, family);