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. M.I.T. makes no representations about the suitability of
20 * this software for any purpose. It is provided "as is" without express
21 * or implied warranty.
24 * get socket addresses for KDC.
30 #ifdef KRB5_DNS_LOOKUP
34 #include <arpa/inet.h>
35 #include <arpa/nameser.h>
43 /* for old Unixes and friends ... */
44 #ifndef MAXHOSTNAMELEN
45 #define MAXHOSTNAMELEN 64
48 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
49 #endif /* KRB5_DNS_LOOKUP */
52 * returns count of number of addresses found
53 * if master is non-NULL, it is filled in with the index of
57 static krb5_error_code
58 krb5_locate_srv_conf(context, realm, name, addr_pp, naddrs, master_index, nmasters)
60 const krb5_data *realm;
62 struct sockaddr **addr_pp;
67 const char *realm_srv_names[4];
68 char **masterlist, **hostlist, *host, *port, *cp;
70 int i, j, out, count, ismaster;
71 struct sockaddr *addr_p;
72 struct sockaddr_in *sin_p;
75 #ifdef HAVE_NETINET_IN_H
76 u_short udpport = htons(KRB5_DEFAULT_PORT);
77 u_short sec_udpport = htons(KRB5_DEFAULT_SEC_PORT);
80 if ((host = malloc(realm->length + 1)) == NULL)
83 strncpy(host, realm->data, realm->length);
84 host[realm->length] = '\0';
89 realm_srv_names[0] = "realms";
90 realm_srv_names[1] = host;
91 realm_srv_names[2] = name;
92 realm_srv_names[3] = 0;
94 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
97 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
98 code = KRB5_REALM_UNKNOWN;
103 #ifdef HAVE_NETINET_IN_H
104 if ((sp = getservbyname(KDC_PORTNAME, "udp")))
105 udpport = sp->s_port;
106 if ((sp = getservbyname(KDC_SECONDARY_PORTNAME, "udp")))
107 sec_udpport = sp->s_port;
109 if (sec_udpport == udpport)
113 while (hostlist && hostlist[count])
126 realm_srv_names[0] = "realms";
127 realm_srv_names[1] = host;
128 realm_srv_names[2] = "admin_server";
129 realm_srv_names[3] = 0;
131 code = profile_get_values(context->profile, realm_srv_names,
137 for (i=0; masterlist[i]; i++) {
138 host = masterlist[i];
141 * Strip off excess whitespace
143 cp = strchr(host, ' ');
146 cp = strchr(host, '\t');
149 cp = strchr(host, ':');
158 /* at this point, if master is non-NULL, then either the master kdc
159 is required, and there is one, or the master kdc is not required,
160 and there may or may not be one. */
162 #ifdef HAVE_NETINET_IN_H
167 addr_p = (struct sockaddr *)malloc (sizeof (struct sockaddr) * count);
171 for (i=0, out=0; hostlist[i]; i++) {
174 * Strip off excess whitespace
176 cp = strchr(host, ' ');
179 cp = strchr(host, '\t');
182 port = strchr(host, ':');
188 if ((hp = gethostbyname(hostlist[i])) == 0) {
196 for (j=0; masterlist[j]; j++) {
197 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
204 switch (hp->h_addrtype) {
206 #ifdef HAVE_NETINET_IN_H
208 for (j=0; hp->h_addr_list[j]; j++) {
209 sin_p = (struct sockaddr_in *) &addr_p[out++];
210 memset ((char *)sin_p, 0, sizeof(struct sockaddr));
211 sin_p->sin_family = hp->h_addrtype;
212 sin_p->sin_port = port ? htons(atoi(port)) : udpport;
213 memcpy((char *)&sin_p->sin_addr,
214 (char *)hp->h_addr_list[j],
215 sizeof(struct in_addr));
216 if (out+1 >= count) {
218 addr_p = (struct sockaddr *)
219 realloc ((char *)addr_p,
220 sizeof(struct sockaddr) * count);
224 if (sec_udpport && !port) {
225 addr_p[out] = addr_p[out-1];
226 sin_p = (struct sockaddr_in *) &addr_p[out++];
227 sin_p->sin_port = sec_udpport;
236 *nmasters = out - *master_index;
238 /* Free the hostlist entry we are looping over. */
244 for (i=0; masterlist[i]; i++)
249 free ((char *)hostlist);
251 if (out == 0) { /* Couldn't resolve any KDC names */
253 return KRB5_REALM_CANT_RESOLVE;
261 #ifdef KRB5_DNS_LOOKUP
264 * Lookup a KDC via DNS SRV records
267 static krb5_error_code
268 krb5_locate_srv_dns(realm, service, protocol, addr_pp, naddrs)
269 const krb5_data *realm;
271 const char *protocol;
272 struct sockaddr **addr_pp;
275 krb5_error_code code;
278 unsigned char bytes[2048];
281 unsigned char *p=NULL;
282 char host[MAX_DNS_NAMELEN];
283 struct sockaddr *addr = NULL;
284 struct sockaddr_in *sin = NULL;
285 struct hostent *hp = NULL;
287 int status, priority, weight, size, len, numanswers, numqueries, rdlen;
289 const int hdrsize = sizeof(HEADER);
290 struct srv_dns_entry {
291 struct srv_dns_entry *next;
298 struct srv_dns_entry *head = NULL;
299 struct srv_dns_entry *srv = NULL, *entry = NULL;
302 addr = (struct sockaddr *) malloc(sizeof(struct sockaddr));
309 * First off, build a query of the form:
311 * service.protocol.realm
313 * which will most likely be something like:
315 * _kerberos._udp.REALM
319 if ( strlen(service) + strlen(protocol) + realm->length + 5
322 sprintf(host, "%s.%s.%.*s", service, protocol, realm->length,
325 size = res_search(host, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes));
331 * We got an answer! First off, parse the header and figure out how
332 * many answers we got back.
337 numqueries = ntohs(answer.hdr.qdcount);
338 numanswers = ntohs(answer.hdr.ancount);
343 * We need to skip over all of the questions, so we have to iterate
344 * over every query record. dn_expand() is able to tell us the size
345 * of compress DNS names, so we use it.
348 #define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto out
349 #define CHECK(x,y) if (x + y > size + answer.bytes) goto out
350 #define NTOHSP(x,y) x[0] << 8 | x[1]; x += y
352 while (numqueries--) {
353 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
356 INCR_CHECK(p, len + 4);
360 * We're now pointing at the answer records. Only process them if
361 * they're actually T_SRV records (they might be CNAME records,
364 * But in a DNS reply, if you get a CNAME you always get the associated
365 * "real" RR for that CNAME. RFC 1034, 3.6.2:
367 * CNAME RRs cause special action in DNS software. When a name server
368 * fails to find a desired RR in the resource set associated with the
369 * domain name, it checks to see if the resource set consists of a CNAME
370 * record with a matching class. If so, the name server includes the CNAME
371 * record in the response and restarts the query at the domain name
372 * specified in the data field of the CNAME record. The one exception to
373 * this rule is that queries which match the CNAME type are not restarted.
375 * In other words, CNAMEs do not need to be expanded by the client.
378 while (numanswers--) {
380 /* First is the name; use dn_expand to get the compressed size */
381 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
386 /* Next is the query type */
390 /* Next is the query class; also skip over 4 byte TTL */
394 /* Record data length */
400 * If this is an SRV record, process it. Record format is:
408 if (class == C_IN && type == T_SRV) {
410 priority = NTOHSP(p,2);
412 weight = NTOHSP(p,2);
415 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
421 * We got everything! Insert it into our list, but make sure
422 * it's in the right order. Right now we don't do anything
423 * with the weight field
426 srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
430 srv->priority = priority;
431 srv->weight = weight;
433 srv->host = strdup(host);
435 if (head == NULL || head->priority > srv->priority) {
440 * This is confusing. Only insert an entry into this
442 * The next person has a higher priority (lower priorities
445 * There is no next entry (we're at the end)
447 for (entry = head; entry != NULL; entry = entry->next)
449 entry->next->priority > srv->priority) ||
450 entry->next == NULL) {
451 srv->next = entry->next;
456 INCR_CHECK(p, rdlen);
460 * Okay! Now we've got a linked list of entries sorted by
461 * priority. Start looking up A records and returning
468 for (entry = head; entry != NULL; entry = entry->next) {
469 hp = gethostbyname(entry->host);
471 switch (hp->h_addrtype) {
472 #ifdef HAVE_NETINET_IN_H
474 for (j=0; hp->h_addr_list[j]; j++) {
475 sin = (struct sockaddr_in *) &addr[out++];
476 memset ((char *) sin, 0, sizeof (struct sockaddr));
477 sin->sin_family = hp->h_addrtype;
478 sin->sin_port = htons(entry->port);
479 memcpy((char *) &sin->sin_addr,
480 (char *) hp->h_addr_list[j],
481 sizeof(struct in_addr));
482 if (out + 1 >= count) {
484 addr = (struct sockaddr *)
485 realloc((char *) addr,
486 sizeof(struct sockaddr) * count);
492 #endif /* HAVE_NETINET_IN_H */
499 for (entry = head; entry != NULL; ) {
512 if (out == 0) { /* No good servers */
515 return KRB5_REALM_CANT_RESOLVE;
522 #endif /* KRB5_DNS_LOOKUP */
525 * Wrapper function for the two backends
529 krb5_locate_kdc(context, realm, addr_pp, naddrs, master_index, nmasters)
530 krb5_context context;
531 const krb5_data *realm;
532 struct sockaddr **addr_pp;
537 krb5_error_code code;
538 #ifdef KRB5_DNS_LOOKUP
539 struct sockaddr *admin_addr_p, *kdc_addr_p;
540 int nadmin_addrs, nkdc_addrs;
542 #endif /* KRB5_DNS_LOOKUP */
545 * We always try the local file first
548 code = krb5_locate_srv_conf(context, realm, "kdc", addr_pp, naddrs,
549 master_index, nmasters);
551 #ifdef KRB5_DNS_LOOKUP
555 krb5_error_code code2; /* preserve error code from krb5_locate_srv_conf */
557 code2 = profile_get_string(context->profile, "libdefaults",
559 context->profile_in_memory?"1":"0",
565 use_dns = krb5_conf_boolean(string);
569 code = krb5_locate_srv_dns(realm, "_kerberos", "_udp",
571 if ( master_index && nmasters ) {
573 code = krb5_locate_srv_dns(realm, "_kerberos-adm", "_tcp",
574 &admin_addr_p, &nadmin_addrs);
582 kdc_addr_p = *addr_pp;
583 nkdc_addrs = *naddrs;
586 *addr_pp = (struct sockaddr *) malloc(sizeof(*kdc_addr_p));
587 if ( *addr_pp == NULL ) {
593 for ( i=0; i<nkdc_addrs; i++ ) {
594 for ( j=0 ; j<nadmin_addrs; j++) {
595 if ( !memcmp(&kdc_addr_p[i].sa_data[2],&admin_addr_p[j].sa_data[2],4) ) {
596 memcpy(&(*addr_pp)[(*naddrs)],&kdc_addr_p[i],
597 sizeof(struct sockaddr));
607 if ( *naddrs == 0 ) {
610 return KRB5_REALM_CANT_RESOLVE;
617 #endif /* KRB5_DNS_LOOKUP */
621 #if 0 /* Why is this useful? It's not used now, and it's certainly
622 not useful if you don't have the DNS code enabled. -KR */
625 * It turns out that it is really useful to be able to use these functions
626 * for other things (like admin servers), so create an abstract function
631 krb5_locate_server(realm, name, proto, addr_pp, naddrs)
632 const krb5_data *realm;
633 const char *name, *proto;
634 struct sockaddr **addr_pp;
637 krb5_error_code code = KRB5_REALM_UNKNOWN;
638 #ifdef KRB5_DNS_LOOKUP
639 code = krb5_locate_srv_dns(realm, name, proto,
640 (struct sockaddr **) addr_pp, naddrs);
641 #endif /* KRB5_DNS_LOOKUP */