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.
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 (context, name, defalt)
76 code = profile_get_string(context->profile, "libdefaults",
78 if (value == 0 && code == 0)
79 code = profile_get_string(context->profile, "libdefaults",
80 "dns_fallback", 0, 0, &value);
87 use_dns = _krb5_conf_boolean(value);
88 profile_release_string(value);
93 _krb5_use_dns_kdc(context)
96 return maybe_use_dns (context, "dns_lookup_kdc", DEFAULT_LOOKUP_KDC);
100 _krb5_use_dns_realm(context)
101 krb5_context context;
103 return maybe_use_dns (context, "dns_lookup_realm", DEFAULT_LOOKUP_REALM);
106 #endif /* KRB5_DNS_LOOKUP */
108 static int get_port (const char *service, int stream, int defalt)
110 struct addrinfo hints = { 0 };
114 hints.ai_family = PF_INET;
115 hints.ai_socktype = stream ? SOCK_STREAM : SOCK_DGRAM;
116 err = getaddrinfo (NULL, service, &hints, &ai);
117 if (err == 0 && ai != 0) {
118 if (ai->ai_addr->sa_family == AF_INET) {
119 int port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
125 /* Any error - don't complain, just use default. */
126 return htons (defalt);
130 grow_list (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;
153 /* Free up everything pointed to by the addrlist structure, but don't
154 free the structure itself. */
156 krb5int_free_addrlist (struct addrlist *lp)
159 for (i = 0; i < lp->naddrs; i++)
160 freeaddrinfo (lp->addrs[i]);
163 lp->naddrs = lp->space = 0;
165 #define free_list krb5int_free_addrlist
167 static int translate_ai_error (int err)
172 #ifdef EAI_ADDRFAMILY
179 /* All of these indicate bad inputs to getaddrinfo. */
182 /* Translate to standard errno code. */
185 /* Translate to standard errno code. */
187 #if EAI_NODATA != EAI_NONAME
191 /* Name not known or no address data, but no error. Do
196 /* System error, obviously. */
200 /* An error code we haven't handled? */
205 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a)
210 switch (a->ai_socktype) {
212 fprintf(stderr, "\tdgram\n");
215 fprintf(stderr, "\tstream\n");
218 fprintf(stderr, "\traw\n");
223 fprintf(stderr, "\tsocket type %d\n", a->ai_socktype);
228 if (lp->naddrs == lp->space) {
229 err = grow_list (lp, 1);
232 fprintf (stderr, "grow_list failed %d\n", err);
237 lp->addrs[lp->naddrs++] = a;
240 fprintf (stderr, "count is now %d\n", lp->naddrs);
246 add_host_to_list (struct addrlist *lp, const char *hostname,
247 int port, int secport, int socktype)
249 struct addrinfo *addrs, *a, *anext, hint;
251 char portbuf[10], secportbuf[10];
254 fprintf (stderr, "adding hostname %s, ports %d,%d\n", hostname,
255 ntohs (port), ntohs (secport));
258 memset(&hint, 0, sizeof(hint));
259 hint.ai_socktype = socktype;
260 sprintf(portbuf, "%d", ntohs(port));
261 sprintf(secportbuf, "%d", ntohs(secport));
262 err = getaddrinfo (hostname, portbuf, &hint, &addrs);
264 return translate_ai_error (err);
266 for (a = addrs; a != 0 && err == 0; a = anext) {
268 err = add_addrinfo_to_list (lp, a);
270 if (err || secport == 0)
272 err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
274 return translate_ai_error (err);
275 for (a = addrs; a != 0 && err == 0; a = anext) {
277 err = add_addrinfo_to_list (lp, a);
281 freeaddrinfo (anext);
286 * returns count of number of addresses found
287 * if master is non-NULL, it is filled in with the index of
291 static krb5_error_code
292 krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
293 const char * name, struct addrlist *addrlist,
294 int get_masters, int socktype,
295 int udpport, int sec_udpport)
297 const char *realm_srv_names[4];
298 char **masterlist, **hostlist, *host, *port, *cp;
299 krb5_error_code code;
300 int i, j, count, ismaster;
304 "looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
305 realm->data, name, ntohs (udpport), ntohs (sec_udpport));
308 if ((host = malloc(realm->length + 1)) == NULL)
311 strncpy(host, realm->data, realm->length);
312 host[realm->length] = '\0';
317 realm_srv_names[0] = "realms";
318 realm_srv_names[1] = host;
319 realm_srv_names[2] = name;
320 realm_srv_names[3] = 0;
322 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
325 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
326 code = KRB5_REALM_UNKNOWN;
332 while (hostlist && hostlist[count])
336 profile_free_list(hostlist);
338 addrlist->naddrs = 0;
343 realm_srv_names[0] = "realms";
344 realm_srv_names[1] = host;
345 realm_srv_names[2] = "admin_server";
346 realm_srv_names[3] = 0;
348 code = profile_get_values(context->profile, realm_srv_names,
354 for (i=0; masterlist[i]; i++) {
355 host = masterlist[i];
358 * Strip off excess whitespace
360 cp = strchr(host, ' ');
363 cp = strchr(host, '\t');
366 cp = strchr(host, ':');
375 /* at this point, if master is non-NULL, then either the master kdc
376 is required, and there is one, or the master kdc is not required,
377 and there may or may not be one. */
379 #ifdef HAVE_NETINET_IN_H
384 for (i=0; hostlist[i]; i++) {
389 * Strip off excess whitespace
391 cp = strchr(host, ' ');
394 cp = strchr(host, '\t');
397 port = strchr(host, ':');
405 for (j=0; masterlist[j]; j++) {
406 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
412 if (get_masters && !ismaster)
419 l = strtoul (port, &endptr, 10);
420 if (endptr == NULL || *endptr != 0)
425 /* L is unsigned, don't need to check <0. */
436 code = add_host_to_list (addrlist, hostlist[i], p1, p2, socktype);
438 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
441 code = add_host_to_list (addrlist, hostlist[i], p1, p2,
446 fprintf (stderr, "error %d returned from add_host_to_list\n", code);
449 profile_free_list (hostlist);
451 profile_free_list (masterlist);
457 profile_free_list(hostlist);
459 profile_free_list(masterlist);
465 static krb5_error_code
466 krb5_locate_srv_conf(context, realm, name, al, get_masters,
467 udpport, sec_udpport)
468 krb5_context context;
469 const krb5_data *realm;
473 int udpport, sec_udpport;
477 ret = krb5_locate_srv_conf_1 (context, realm, name, al,
478 get_masters, 0, udpport, sec_udpport);
481 if (al->naddrs == 0) /* Couldn't resolve any KDC names */
482 return KRB5_REALM_CANT_RESOLVE;
487 #ifdef KRB5_DNS_LOOKUP
490 * Lookup a KDC via DNS SRV records
493 static krb5_error_code
494 krb5_locate_srv_dns_1 (const krb5_data *realm,
496 const char *protocol,
497 struct addrlist *addrlist)
500 unsigned char bytes[2048];
503 unsigned char *p=NULL;
504 char host[MAX_DNS_NAMELEN], *h;
506 int priority, weight, size, len, numanswers, numqueries, rdlen;
508 const int hdrsize = sizeof(HEADER);
509 struct srv_dns_entry {
510 struct srv_dns_entry *next;
517 struct srv_dns_entry *head = NULL;
518 struct srv_dns_entry *srv = NULL, *entry = NULL;
519 krb5_error_code code = 0;
522 * First off, build a query of the form:
524 * service.protocol.realm
526 * which will most likely be something like:
528 * _kerberos._udp.REALM
532 if ( strlen(service) + strlen(protocol) + realm->length + 6
535 sprintf(host, "%s.%s.%.*s", service, protocol, (int) realm->length,
538 /* Realm names don't (normally) end with ".", but if the query
539 doesn't end with "." and doesn't get an answer as is, the
540 resolv code will try appending the local domain. Since the
541 realm names are absolutes, let's stop that.
543 But only if a name has been specified. If we are performing
544 a search on the prefix alone then the intention is to allow
545 the local domain or domain search lists to be expanded. */
547 h = host + strlen (host);
548 if ((h > host) && (h[-1] != '.') && ((h - host + 1) < sizeof(host)))
552 fprintf (stderr, "sending DNS SRV query for %s\n", host);
555 size = res_search(host, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes));
561 * We got an answer! First off, parse the header and figure out how
562 * many answers we got back.
567 numqueries = ntohs(answer.hdr.qdcount);
568 numanswers = ntohs(answer.hdr.ancount);
573 * We need to skip over all of the questions, so we have to iterate
574 * over every query record. dn_expand() is able to tell us the size
575 * of compress DNS names, so we use it.
578 #define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto out
579 #define CHECK(x,y) if (x + y > size + answer.bytes) goto out
580 #define NTOHSP(x,y) x[0] << 8 | x[1]; x += y
582 while (numqueries--) {
583 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
586 INCR_CHECK(p, len + 4);
590 * We're now pointing at the answer records. Only process them if
591 * they're actually T_SRV records (they might be CNAME records,
594 * But in a DNS reply, if you get a CNAME you always get the associated
595 * "real" RR for that CNAME. RFC 1034, 3.6.2:
597 * CNAME RRs cause special action in DNS software. When a name server
598 * fails to find a desired RR in the resource set associated with the
599 * domain name, it checks to see if the resource set consists of a CNAME
600 * record with a matching class. If so, the name server includes the CNAME
601 * record in the response and restarts the query at the domain name
602 * specified in the data field of the CNAME record. The one exception to
603 * this rule is that queries which match the CNAME type are not restarted.
605 * In other words, CNAMEs do not need to be expanded by the client.
608 while (numanswers--) {
610 /* First is the name; use dn_expand to get the compressed size */
611 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
616 /* Next is the query type */
620 /* Next is the query class; also skip over 4 byte TTL */
624 /* Record data length */
630 * If this is an SRV record, process it. Record format is:
638 if (class == C_IN && type == T_SRV) {
640 priority = NTOHSP(p,2);
642 weight = NTOHSP(p,2);
645 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
651 * We got everything! Insert it into our list, but make sure
652 * it's in the right order. Right now we don't do anything
653 * with the weight field
656 srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
660 srv->priority = priority;
661 srv->weight = weight;
663 srv->host = strdup(host);
665 if (head == NULL || head->priority > srv->priority) {
670 * This is confusing. Only insert an entry into this
672 * The next person has a higher priority (lower priorities
675 * There is no next entry (we're at the end)
677 for (entry = head; entry != NULL; entry = entry->next)
679 entry->next->priority > srv->priority) ||
680 entry->next == NULL) {
681 srv->next = entry->next;
686 INCR_CHECK(p, rdlen);
690 * Okay! Now we've got a linked list of entries sorted by
691 * priority. Start looking up A records and returning
699 fprintf (stderr, "walking answer list:\n");
701 for (entry = head; entry != NULL; entry = entry->next) {
703 fprintf (stderr, "\tport=%d host=%s\n", entry->port, entry->host);
705 code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
706 (strcmp("_tcp", protocol)
713 fprintf (stderr, "[end]\n");
716 for (entry = head; entry != NULL; ) {
733 static krb5_error_code
734 krb5_locate_srv_dns(const krb5_data *realm,
735 const char *service, const char *protocol,
738 return krb5_locate_srv_dns_1 (realm, service, protocol, al);
741 #endif /* KRB5_DNS_LOOKUP */
744 * Wrapper function for the two backends
748 krb5int_locate_server (krb5_context context, const krb5_data *realm,
749 struct addrlist *addrlist,
751 const char *profname, const char *dnsname,
753 /* network order port numbers! */
754 int dflport1, int dflport2)
756 krb5_error_code code;
757 struct addrlist al = ADDRLIST_INIT;
762 * We always try the local file first
765 code = krb5_locate_srv_conf_1(context, realm, profname, &al, get_masters,
766 socktype, dflport1, dflport2);
768 #ifdef KRB5_DNS_LOOKUP
769 if (code && dnsname != 0) {
770 int use_dns = _krb5_use_dns_kdc(context);
773 if (socktype == SOCK_DGRAM || socktype == 0) {
774 code = krb5_locate_srv_dns_1(realm, dnsname, "_udp", &al);
777 fprintf(stderr, "dns udp lookup returned error %d\n",
781 if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
782 code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp", &al);
785 fprintf(stderr, "dns tcp lookup returned error %d\n",
791 #endif /* KRB5_DNS_LOOKUP */
794 fprintf (stderr, "krb5int_locate_server found %d addresses\n",
797 fprintf (stderr, "krb5int_locate_server returning error code %d\n",
805 if (al.naddrs == 0) { /* No good servers */
808 return KRB5_REALM_CANT_RESOLVE;
815 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
816 struct addrlist *addrlist,
817 int get_masters, int socktype)
819 int udpport, sec_udpport;
821 udpport = get_port (KDC_PORTNAME, 0, KRB5_DEFAULT_PORT);
822 sec_udpport = get_port (KDC_SECONDARY_PORTNAME, 0,
823 (udpport == htons (KRB5_DEFAULT_PORT)
824 ? KRB5_DEFAULT_SEC_PORT
825 : KRB5_DEFAULT_PORT));
826 if (sec_udpport == udpport)
829 return krb5int_locate_server (context, realm, addrlist, get_masters, "kdc",
833 socktype, udpport, sec_udpport);