2 * lib/krb5/os/locate_kdc.c
4 * Copyright 1990 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 /* for old Unixes and friends ... */
48 #ifndef MAXHOSTNAMELEN
49 #define MAXHOSTNAMELEN 64
52 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
53 #ifndef KPASSWD_PORTNAME
54 #define KPASSWD_PORTNAME "kpasswd"
57 #if KRB5_DNS_LOOKUP_KDC
58 #define DEFAULT_LOOKUP_KDC 1
60 #define DEFAULT_LOOKUP_KDC 0
62 #if KRB5_DNS_LOOKUP_REALM
63 #define DEFAULT_LOOKUP_REALM 1
65 #define DEFAULT_LOOKUP_REALM 0
69 maybe_use_dns (context, name, defalt)
78 code = profile_get_string(context->profile, "libdefaults",
80 if (value == 0 && code == 0)
81 code = profile_get_string(context->profile, "libdefaults",
82 "dns_fallback", 0, 0, &value);
89 use_dns = _krb5_conf_boolean(value);
90 profile_release_string(value);
95 _krb5_use_dns_kdc(context)
98 return maybe_use_dns (context, "dns_lookup_kdc", DEFAULT_LOOKUP_KDC);
102 _krb5_use_dns_realm(context)
103 krb5_context context;
105 return maybe_use_dns (context, "dns_lookup_realm", DEFAULT_LOOKUP_REALM);
108 #endif /* KRB5_DNS_LOOKUP */
111 * returns count of number of addresses found
112 * if master is non-NULL, it is filled in with the index of
117 krb5_locate_srv_conf(context, realm, name, addr_pp, naddrs, get_masters)
118 krb5_context context;
119 const krb5_data *realm;
121 struct sockaddr **addr_pp;
125 const char *realm_srv_names[4];
126 char **masterlist, **hostlist, *host, *port, *cp;
127 krb5_error_code code;
128 int i, j, out, count, ismaster;
129 struct sockaddr *addr_p;
130 struct sockaddr_in *sin_p;
133 #ifdef HAVE_NETINET_IN_H
138 if ((host = malloc(realm->length + 1)) == NULL)
141 strncpy(host, realm->data, realm->length);
142 host[realm->length] = '\0';
147 realm_srv_names[0] = "realms";
148 realm_srv_names[1] = host;
149 realm_srv_names[2] = name;
150 realm_srv_names[3] = 0;
152 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
155 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
156 code = KRB5_REALM_UNKNOWN;
161 #ifdef HAVE_NETINET_IN_H
162 if ( !strcmp(name,"kpasswd_server") ) {
163 if ((sp = getservbyname(KPASSWD_PORTNAME, "udp")))
164 udpport = sp->s_port;
166 udpport = htons(DEFAULT_KPASSWD_PORT);
169 if ((sp = getservbyname(KDC_PORTNAME, "udp")))
170 udpport = sp->s_port;
172 udpport = htons(KRB5_DEFAULT_PORT);
173 if ((sp = getservbyname(KDC_SECONDARY_PORTNAME, "udp")))
174 sec_udpport = sp->s_port;
176 sec_udpport = htons(KRB5_DEFAULT_SEC_PORT);
179 if (sec_udpport == udpport)
183 while (hostlist && hostlist[count])
187 profile_free_list(hostlist);
194 realm_srv_names[0] = "realms";
195 realm_srv_names[1] = host;
196 realm_srv_names[2] = "admin_server";
197 realm_srv_names[3] = 0;
199 code = profile_get_values(context->profile, realm_srv_names,
205 for (i=0; masterlist[i]; i++) {
206 host = masterlist[i];
209 * Strip off excess whitespace
211 cp = strchr(host, ' ');
214 cp = strchr(host, '\t');
217 cp = strchr(host, ':');
226 /* at this point, if master is non-NULL, then either the master kdc
227 is required, and there is one, or the master kdc is not required,
228 and there may or may not be one. */
230 #ifdef HAVE_NETINET_IN_H
235 addr_p = (struct sockaddr *)malloc (sizeof (struct sockaddr) * count);
236 if (addr_p == NULL) {
238 profile_free_list(hostlist);
240 profile_free_list(masterlist);
244 for (i=0, out=0; hostlist[i]; i++) {
247 * Strip off excess whitespace
249 cp = strchr(host, ' ');
252 cp = strchr(host, '\t');
255 port = strchr(host, ':');
261 if ((hp = gethostbyname(hostlist[i])) == 0) {
267 for (j=0; masterlist[j]; j++) {
268 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
274 if ( !get_masters || ismaster ) {
275 switch (hp->h_addrtype) {
277 #ifdef HAVE_NETINET_IN_H
279 for (j=0; hp->h_addr_list[j]; j++) {
280 sin_p = (struct sockaddr_in *) &addr_p[out++];
281 memset ((char *)sin_p, 0, sizeof(struct sockaddr));
282 sin_p->sin_family = hp->h_addrtype;
283 sin_p->sin_port = port ? htons(atoi(port)) : udpport;
284 memcpy((char *)&sin_p->sin_addr,
285 (char *)hp->h_addr_list[j],
286 sizeof(struct in_addr));
287 if (out+1 >= count) {
289 addr_p = (struct sockaddr *)
290 realloc ((char *)addr_p,
291 sizeof(struct sockaddr) * count);
292 if (addr_p == NULL) {
294 profile_free_list(hostlist);
296 profile_free_list(masterlist);
300 if (sec_udpport && !port) {
301 addr_p[out] = addr_p[out-1];
302 sin_p = (struct sockaddr_in *) &addr_p[out++];
303 sin_p->sin_port = sec_udpport;
315 profile_free_list(hostlist);
317 profile_free_list(masterlist);
319 if (out == 0) { /* Couldn't resolve any KDC names */
321 return KRB5_REALM_CANT_RESOLVE;
329 #ifdef KRB5_DNS_LOOKUP
332 * Lookup a KDC via DNS SRV records
336 krb5_locate_srv_dns(realm, service, protocol, addr_pp, naddrs)
337 const krb5_data *realm;
339 const char *protocol;
340 struct sockaddr **addr_pp;
345 unsigned char bytes[2048];
348 unsigned char *p=NULL;
349 char host[MAX_DNS_NAMELEN], *h;
350 struct sockaddr *addr = NULL;
351 struct sockaddr_in *sin = NULL;
352 struct hostent *hp = NULL;
354 int priority, weight, size, len, numanswers, numqueries, rdlen;
356 const int hdrsize = sizeof(HEADER);
357 struct srv_dns_entry {
358 struct srv_dns_entry *next;
365 struct srv_dns_entry *head = NULL;
366 struct srv_dns_entry *srv = NULL, *entry = NULL;
369 addr = (struct sockaddr *) malloc(sizeof(struct sockaddr));
376 * First off, build a query of the form:
378 * service.protocol.realm
380 * which will most likely be something like:
382 * _kerberos._udp.REALM
386 if ( strlen(service) + strlen(protocol) + realm->length + 6
389 sprintf(host, "%s.%s.%.*s", service, protocol, realm->length,
392 /* Realm names don't (normally) end with ".", but if the query
393 doesn't end with "." and doesn't get an answer as is, the
394 resolv code will try appending the local domain. Since the
395 realm names are absolutes, let's stop that.
397 But only if a name has been specified. If we are performing
398 a search on the prefix alone then the intention is to allow
399 the local domain or domain search lists to be expanded.
402 h = host + strlen (host);
403 if ((h > host) && (h[-1] != '.') && ((h - host + 1) < sizeof(host)))
406 size = res_search(host, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes));
412 * We got an answer! First off, parse the header and figure out how
413 * many answers we got back.
418 numqueries = ntohs(answer.hdr.qdcount);
419 numanswers = ntohs(answer.hdr.ancount);
424 * We need to skip over all of the questions, so we have to iterate
425 * over every query record. dn_expand() is able to tell us the size
426 * of compress DNS names, so we use it.
429 #define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto out
430 #define CHECK(x,y) if (x + y > size + answer.bytes) goto out
431 #define NTOHSP(x,y) x[0] << 8 | x[1]; x += y
433 while (numqueries--) {
434 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
437 INCR_CHECK(p, len + 4);
441 * We're now pointing at the answer records. Only process them if
442 * they're actually T_SRV records (they might be CNAME records,
445 * But in a DNS reply, if you get a CNAME you always get the associated
446 * "real" RR for that CNAME. RFC 1034, 3.6.2:
448 * CNAME RRs cause special action in DNS software. When a name server
449 * fails to find a desired RR in the resource set associated with the
450 * domain name, it checks to see if the resource set consists of a CNAME
451 * record with a matching class. If so, the name server includes the CNAME
452 * record in the response and restarts the query at the domain name
453 * specified in the data field of the CNAME record. The one exception to
454 * this rule is that queries which match the CNAME type are not restarted.
456 * In other words, CNAMEs do not need to be expanded by the client.
459 while (numanswers--) {
461 /* First is the name; use dn_expand to get the compressed size */
462 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
467 /* Next is the query type */
471 /* Next is the query class; also skip over 4 byte TTL */
475 /* Record data length */
481 * If this is an SRV record, process it. Record format is:
489 if (class == C_IN && type == T_SRV) {
491 priority = NTOHSP(p,2);
493 weight = NTOHSP(p,2);
496 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
502 * We got everything! Insert it into our list, but make sure
503 * it's in the right order. Right now we don't do anything
504 * with the weight field
507 srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
511 srv->priority = priority;
512 srv->weight = weight;
514 srv->host = strdup(host);
516 if (head == NULL || head->priority > srv->priority) {
521 * This is confusing. Only insert an entry into this
523 * The next person has a higher priority (lower priorities
526 * There is no next entry (we're at the end)
528 for (entry = head; entry != NULL; entry = entry->next)
530 entry->next->priority > srv->priority) ||
531 entry->next == NULL) {
532 srv->next = entry->next;
537 INCR_CHECK(p, rdlen);
541 * Okay! Now we've got a linked list of entries sorted by
542 * priority. Start looking up A records and returning
549 for (entry = head; entry != NULL; entry = entry->next) {
550 hp = gethostbyname(entry->host);
552 switch (hp->h_addrtype) {
553 #ifdef HAVE_NETINET_IN_H
555 for (j=0; hp->h_addr_list[j]; j++) {
556 sin = (struct sockaddr_in *) &addr[out++];
557 memset ((char *) sin, 0, sizeof (struct sockaddr));
558 sin->sin_family = hp->h_addrtype;
559 sin->sin_port = htons(entry->port);
560 memcpy((char *) &sin->sin_addr,
561 (char *) hp->h_addr_list[j],
562 sizeof(struct in_addr));
563 if (out + 1 >= count) {
565 addr = (struct sockaddr *)
566 realloc((char *) addr,
567 sizeof(struct sockaddr) * count);
573 #endif /* HAVE_NETINET_IN_H */
580 for (entry = head; entry != NULL; ) {
593 if (out == 0) { /* No good servers */
596 return KRB5_REALM_CANT_RESOLVE;
603 #endif /* KRB5_DNS_LOOKUP */
606 * Wrapper function for the two backends
610 krb5_locate_kdc(context, realm, addr_pp, naddrs, get_masters)
611 krb5_context context;
612 const krb5_data *realm;
613 struct sockaddr **addr_pp;
617 krb5_error_code code;
620 * We always try the local file first
623 code = krb5_locate_srv_conf(context, realm, "kdc", addr_pp, naddrs,
626 #ifdef KRB5_DNS_LOOKUP
628 int use_dns = _krb5_use_dns_kdc(context);
630 code = krb5_locate_srv_dns(realm,
631 get_masters ? "_kerberos-master" : "_kerberos",
632 "_udp", addr_pp, naddrs);
635 #endif /* KRB5_DNS_LOOKUP */
639 #if 0 /* Why is this useful? It's not used now, and it's certainly
640 not useful if you don't have the DNS code enabled. -KR */
643 * It turns out that it is really useful to be able to use these functions
644 * for other things (like admin servers), so create an abstract function
649 krb5_locate_server(realm, name, proto, addr_pp, naddrs)
650 const krb5_data *realm;
651 const char *name, *proto;
652 struct sockaddr **addr_pp;
655 krb5_error_code code = KRB5_REALM_UNKNOWN;
656 #ifdef KRB5_DNS_LOOKUP
657 code = krb5_locate_srv_dns(realm, name, proto,
658 (struct sockaddr **) addr_pp, naddrs);
659 #endif /* KRB5_DNS_LOOKUP */