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)
51 _krb5_use_dns(context)
58 code = profile_get_string(context->profile, "libdefaults",
60 context->profile_in_memory?"1":"0",
66 use_dns = _krb5_conf_boolean(value);
67 profile_release_string(value);
73 #endif /* KRB5_DNS_LOOKUP */
76 * returns count of number of addresses found
77 * if master is non-NULL, it is filled in with the index of
81 static krb5_error_code
82 krb5_locate_srv_conf(context, realm, name, addr_pp, naddrs, master_index, nmasters)
84 const krb5_data *realm;
86 struct sockaddr **addr_pp;
91 const char *realm_srv_names[4];
92 char **masterlist, **hostlist, *host, *port, *cp;
94 int i, j, out, count, ismaster;
95 struct sockaddr *addr_p;
96 struct sockaddr_in *sin_p;
99 #ifdef HAVE_NETINET_IN_H
100 u_short udpport = htons(KRB5_DEFAULT_PORT);
101 u_short sec_udpport = htons(KRB5_DEFAULT_SEC_PORT);
104 if ((host = malloc(realm->length + 1)) == NULL)
107 strncpy(host, realm->data, realm->length);
108 host[realm->length] = '\0';
113 realm_srv_names[0] = "realms";
114 realm_srv_names[1] = host;
115 realm_srv_names[2] = name;
116 realm_srv_names[3] = 0;
118 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
121 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
122 code = KRB5_REALM_UNKNOWN;
127 #ifdef HAVE_NETINET_IN_H
128 if ((sp = getservbyname(KDC_PORTNAME, "udp")))
129 udpport = sp->s_port;
130 if ((sp = getservbyname(KDC_SECONDARY_PORTNAME, "udp")))
131 sec_udpport = sp->s_port;
133 if (sec_udpport == udpport)
137 while (hostlist && hostlist[count])
141 profile_free_list(hostlist);
151 realm_srv_names[0] = "realms";
152 realm_srv_names[1] = host;
153 realm_srv_names[2] = "admin_server";
154 realm_srv_names[3] = 0;
156 code = profile_get_values(context->profile, realm_srv_names,
162 for (i=0; masterlist[i]; i++) {
163 host = masterlist[i];
166 * Strip off excess whitespace
168 cp = strchr(host, ' ');
171 cp = strchr(host, '\t');
174 cp = strchr(host, ':');
183 /* at this point, if master is non-NULL, then either the master kdc
184 is required, and there is one, or the master kdc is not required,
185 and there may or may not be one. */
187 #ifdef HAVE_NETINET_IN_H
192 addr_p = (struct sockaddr *)malloc (sizeof (struct sockaddr) * count);
193 if (addr_p == NULL) {
194 profile_free_list(hostlist);
195 profile_free_list(masterlist);
199 for (i=0, out=0; hostlist[i]; i++) {
202 * Strip off excess whitespace
204 cp = strchr(host, ' ');
207 cp = strchr(host, '\t');
210 port = strchr(host, ':');
216 if ((hp = gethostbyname(hostlist[i])) == 0) {
222 for (j=0; masterlist[j]; j++) {
223 if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
230 switch (hp->h_addrtype) {
232 #ifdef HAVE_NETINET_IN_H
234 for (j=0; hp->h_addr_list[j]; j++) {
235 sin_p = (struct sockaddr_in *) &addr_p[out++];
236 memset ((char *)sin_p, 0, sizeof(struct sockaddr));
237 sin_p->sin_family = hp->h_addrtype;
238 sin_p->sin_port = port ? htons(atoi(port)) : udpport;
239 memcpy((char *)&sin_p->sin_addr,
240 (char *)hp->h_addr_list[j],
241 sizeof(struct in_addr));
242 if (out+1 >= count) {
244 addr_p = (struct sockaddr *)
245 realloc ((char *)addr_p,
246 sizeof(struct sockaddr) * count);
247 if (addr_p == NULL) {
248 profile_free_list(hostlist);
249 profile_free_list(masterlist);
253 if (sec_udpport && !port) {
254 addr_p[out] = addr_p[out-1];
255 sin_p = (struct sockaddr_in *) &addr_p[out++];
256 sin_p->sin_port = sec_udpport;
265 *nmasters = out - *master_index;
268 profile_free_list(hostlist);
269 profile_free_list(masterlist);
271 if (out == 0) { /* Couldn't resolve any KDC names */
273 return KRB5_REALM_CANT_RESOLVE;
281 #ifdef KRB5_DNS_LOOKUP
284 * Lookup a KDC via DNS SRV records
287 static krb5_error_code
288 krb5_locate_srv_dns(realm, service, protocol, addr_pp, naddrs)
289 const krb5_data *realm;
291 const char *protocol;
292 struct sockaddr **addr_pp;
295 krb5_error_code code;
298 unsigned char bytes[2048];
301 unsigned char *p=NULL;
302 char host[MAX_DNS_NAMELEN];
303 struct sockaddr *addr = NULL;
304 struct sockaddr_in *sin = NULL;
305 struct hostent *hp = NULL;
307 int status, priority, weight, size, len, numanswers, numqueries, rdlen;
309 const int hdrsize = sizeof(HEADER);
310 struct srv_dns_entry {
311 struct srv_dns_entry *next;
318 struct srv_dns_entry *head = NULL;
319 struct srv_dns_entry *srv = NULL, *entry = NULL;
322 addr = (struct sockaddr *) malloc(sizeof(struct sockaddr));
329 * First off, build a query of the form:
331 * service.protocol.realm
333 * which will most likely be something like:
335 * _kerberos._udp.REALM
339 if ( strlen(service) + strlen(protocol) + realm->length + 5
342 sprintf(host, "%s.%s.%.*s", service, protocol, realm->length,
345 size = res_search(host, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes));
351 * We got an answer! First off, parse the header and figure out how
352 * many answers we got back.
357 numqueries = ntohs(answer.hdr.qdcount);
358 numanswers = ntohs(answer.hdr.ancount);
363 * We need to skip over all of the questions, so we have to iterate
364 * over every query record. dn_expand() is able to tell us the size
365 * of compress DNS names, so we use it.
368 #define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto out
369 #define CHECK(x,y) if (x + y > size + answer.bytes) goto out
370 #define NTOHSP(x,y) x[0] << 8 | x[1]; x += y
372 while (numqueries--) {
373 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
376 INCR_CHECK(p, len + 4);
380 * We're now pointing at the answer records. Only process them if
381 * they're actually T_SRV records (they might be CNAME records,
384 * But in a DNS reply, if you get a CNAME you always get the associated
385 * "real" RR for that CNAME. RFC 1034, 3.6.2:
387 * CNAME RRs cause special action in DNS software. When a name server
388 * fails to find a desired RR in the resource set associated with the
389 * domain name, it checks to see if the resource set consists of a CNAME
390 * record with a matching class. If so, the name server includes the CNAME
391 * record in the response and restarts the query at the domain name
392 * specified in the data field of the CNAME record. The one exception to
393 * this rule is that queries which match the CNAME type are not restarted.
395 * In other words, CNAMEs do not need to be expanded by the client.
398 while (numanswers--) {
400 /* First is the name; use dn_expand to get the compressed size */
401 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
406 /* Next is the query type */
410 /* Next is the query class; also skip over 4 byte TTL */
414 /* Record data length */
420 * If this is an SRV record, process it. Record format is:
428 if (class == C_IN && type == T_SRV) {
430 priority = NTOHSP(p,2);
432 weight = NTOHSP(p,2);
435 len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
441 * We got everything! Insert it into our list, but make sure
442 * it's in the right order. Right now we don't do anything
443 * with the weight field
446 srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
450 srv->priority = priority;
451 srv->weight = weight;
453 srv->host = strdup(host);
455 if (head == NULL || head->priority > srv->priority) {
460 * This is confusing. Only insert an entry into this
462 * The next person has a higher priority (lower priorities
465 * There is no next entry (we're at the end)
467 for (entry = head; entry != NULL; entry = entry->next)
469 entry->next->priority > srv->priority) ||
470 entry->next == NULL) {
471 srv->next = entry->next;
476 INCR_CHECK(p, rdlen);
480 * Okay! Now we've got a linked list of entries sorted by
481 * priority. Start looking up A records and returning
488 for (entry = head; entry != NULL; entry = entry->next) {
489 hp = gethostbyname(entry->host);
491 switch (hp->h_addrtype) {
492 #ifdef HAVE_NETINET_IN_H
494 for (j=0; hp->h_addr_list[j]; j++) {
495 sin = (struct sockaddr_in *) &addr[out++];
496 memset ((char *) sin, 0, sizeof (struct sockaddr));
497 sin->sin_family = hp->h_addrtype;
498 sin->sin_port = htons(entry->port);
499 memcpy((char *) &sin->sin_addr,
500 (char *) hp->h_addr_list[j],
501 sizeof(struct in_addr));
502 if (out + 1 >= count) {
504 addr = (struct sockaddr *)
505 realloc((char *) addr,
506 sizeof(struct sockaddr) * count);
512 #endif /* HAVE_NETINET_IN_H */
519 for (entry = head; entry != NULL; ) {
532 if (out == 0) { /* No good servers */
535 return KRB5_REALM_CANT_RESOLVE;
542 #endif /* KRB5_DNS_LOOKUP */
545 * Wrapper function for the two backends
549 krb5_locate_kdc(context, realm, addr_pp, naddrs, master_index, nmasters)
550 krb5_context context;
551 const krb5_data *realm;
552 struct sockaddr **addr_pp;
557 krb5_error_code code;
558 #ifdef KRB5_DNS_LOOKUP
559 struct sockaddr *admin_addr_p, *kdc_addr_p;
560 int nadmin_addrs, nkdc_addrs;
562 #endif /* KRB5_DNS_LOOKUP */
565 * We always try the local file first
568 code = krb5_locate_srv_conf(context, realm, "kdc", addr_pp, naddrs,
569 master_index, nmasters);
571 #ifdef KRB5_DNS_LOOKUP
573 int use_dns = _krb5_use_dns(context);
575 code = krb5_locate_srv_dns(realm, "_kerberos", "_udp",
577 if ( master_index && nmasters ) {
579 code = krb5_locate_srv_dns(realm, "_kerberos-adm", "_tcp",
580 &admin_addr_p, &nadmin_addrs);
588 kdc_addr_p = *addr_pp;
589 nkdc_addrs = *naddrs;
592 *addr_pp = (struct sockaddr *) malloc(sizeof(*kdc_addr_p));
593 if ( *addr_pp == NULL ) {
599 for ( i=0; i<nkdc_addrs; i++ ) {
600 for ( j=0 ; j<nadmin_addrs; j++) {
601 if ( !memcmp(&kdc_addr_p[i].sa_data[2],&admin_addr_p[j].sa_data[2],4) ) {
602 memcpy(&(*addr_pp)[(*naddrs)],&kdc_addr_p[i],
603 sizeof(struct sockaddr));
613 if ( *naddrs == 0 ) {
616 return KRB5_REALM_CANT_RESOLVE;
623 #endif /* KRB5_DNS_LOOKUP */
627 #if 0 /* Why is this useful? It's not used now, and it's certainly
628 not useful if you don't have the DNS code enabled. -KR */
631 * It turns out that it is really useful to be able to use these functions
632 * for other things (like admin servers), so create an abstract function
637 krb5_locate_server(realm, name, proto, addr_pp, naddrs)
638 const krb5_data *realm;
639 const char *name, *proto;
640 struct sockaddr **addr_pp;
643 krb5_error_code code = KRB5_REALM_UNKNOWN;
644 #ifdef KRB5_DNS_LOOKUP
645 code = krb5_locate_srv_dns(realm, name, proto,
646 (struct sockaddr **) addr_pp, naddrs);
647 #endif /* KRB5_DNS_LOOKUP */