xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * lib/krb5/os/locate_kdc.c
8  *
9  * Copyright 1990,2000,2001,2002,2003,2004,2006 Massachusetts Institute of Technology.
10  * All Rights Reserved.
11  *
12  * Export of this software from the United States of America may
13  *   require a specific license from the United States Government.
14  *   It is the responsibility of any person or organization contemplating
15  *   export to obtain such a license before exporting.
16  *
17  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18  * distribute this software and its documentation for any purpose and
19  * without fee is hereby granted, provided that the above copyright
20  * notice appear in all copies and that both that copyright notice and
21  * this permission notice appear in supporting documentation, and that
22  * the name of M.I.T. not be used in advertising or publicity pertaining
23  * to distribution of the software without specific, written prior
24  * permission.  Furthermore if you modify this software you must label
25  * your software as modified software and not distribute it in such a
26  * fashion that it might be confused with the original M.I.T. software.
27  * M.I.T. makes no representations about the suitability of
28  * this software for any purpose.  It is provided "as is" without express
29  * or implied warranty.
30  *
31  *
32  * get socket addresses for KDC.
33  */
34 
35 #include "fake-addrinfo.h"
36 #include "k5-int.h"
37 #include "os-proto.h"
38 #include <stdio.h>
39 #ifdef KRB5_DNS_LOOKUP
40 #ifdef WSHELPER
41 #include <wshelper.h>
42 #else /* WSHELPER */
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <arpa/nameser.h>
46 #include <resolv.h>
47 #include <netdb.h>
48 #endif /* WSHELPER */
49 #ifndef T_SRV
50 #define T_SRV 33
51 #endif /* T_SRV */
52 
53 /* for old Unixes and friends ... */
54 #ifndef MAXHOSTNAMELEN
55 #define MAXHOSTNAMELEN 64
56 #endif
57 
58 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
59 
60 /* Solaris Kerberos: default to dns lookup for the KDC but not the realm */
61 #define DEFAULT_LOOKUP_KDC 1
62 #define DEFAULT_LOOKUP_REALM 0
63 
64 static int
65 maybe_use_dns (krb5_context context, const char *name, int defalt)
66 {
67     krb5_error_code code;
68     char * value = NULL;
69     int use_dns = 0;
70 
71     code = profile_get_string(context->profile, "libdefaults",
72                               name, 0, 0, &value);
73     if (value == 0 && code == 0)
74 	code = profile_get_string(context->profile, "libdefaults",
75 				  "dns_fallback", 0, 0, &value);
76     if (code)
77         return defalt;
78 
79     if (value == 0)
80 	return defalt;
81 
82     use_dns = _krb5_conf_boolean(value);
83     profile_release_string(value);
84     return use_dns;
85 }
86 
87 int
88 _krb5_use_dns_kdc(krb5_context context)
89 {
90     return maybe_use_dns (context, "dns_lookup_kdc", DEFAULT_LOOKUP_KDC);
91 }
92 
93 int
94 _krb5_use_dns_realm(krb5_context context)
95 {
96     return maybe_use_dns (context, "dns_lookup_realm", DEFAULT_LOOKUP_REALM);
97 }
98 
99 #endif /* KRB5_DNS_LOOKUP */
100 
101 int
102 krb5int_grow_addrlist (struct addrlist *lp, int nmore)
103 {
104     int i;
105     int newspace = lp->space + nmore;
106     size_t newsize = newspace * sizeof (*lp->addrs);
107     void *newaddrs;
108 
109     newaddrs = realloc (lp->addrs, newsize);
110     if (newaddrs == NULL)
111 	return errno;
112     lp->addrs = newaddrs;
113     for (i = lp->space; i < newspace; i++) {
114 	lp->addrs[i].ai = NULL;
115 	lp->addrs[i].freefn = NULL;
116 	lp->addrs[i].data = NULL;
117     }
118     lp->space = newspace;
119     return 0;
120 }
121 #define grow_list krb5int_grow_addrlist
122 
123 /* Free up everything pointed to by the addrlist structure, but don't
124    free the structure itself.  */
125 void
126 krb5int_free_addrlist (struct addrlist *lp)
127 {
128     int i;
129     for (i = 0; i < lp->naddrs; i++)
130 	if (lp->addrs[i].freefn)
131 	    (lp->addrs[i].freefn)(lp->addrs[i].data);
132     free (lp->addrs);
133     lp->addrs = NULL;
134     lp->naddrs = lp->space = 0;
135 }
136 #define free_list krb5int_free_addrlist
137 
138 static int translate_ai_error (int err)
139 {
140     switch (err) {
141     case 0:
142 	return 0;
143     case EAI_BADFLAGS:
144     case EAI_FAMILY:
145     case EAI_SOCKTYPE:
146     case EAI_SERVICE:
147 	/* All of these indicate bad inputs to getaddrinfo.  */
148 	return EINVAL;
149     case EAI_AGAIN:
150 	/* Translate to standard errno code.  */
151 	return EAGAIN;
152     case EAI_MEMORY:
153 	/* Translate to standard errno code.  */
154 	return ENOMEM;
155 #ifdef EAI_ADDRFAMILY
156     case EAI_ADDRFAMILY:
157 #endif
158 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
159     case EAI_NODATA:
160 #endif
161     case EAI_NONAME:
162 	/* Name not known or no address data, but no error.  Do
163 	   nothing more.  */
164 	return 0;
165 #ifdef EAI_OVERFLOW
166     case EAI_OVERFLOW:
167 	/* An argument buffer overflowed.  */
168 	return EINVAL;		/* XXX */
169 #endif
170 #ifdef EAI_SYSTEM
171     case EAI_SYSTEM:
172 	/* System error, obviously.  */
173 	return errno;
174 #endif
175     default:
176 	/* An error code we haven't handled?  */
177 	return EINVAL;
178     }
179 }
180 
181 /* Solaris Kerberos: want dbg messages to syslog */
182 #include <stdarg.h>
183 static inline void Tprintf(const char *fmt, ...)
184 {
185 #ifdef TEST
186     va_list ap;
187     char err_str[2048];
188 
189     va_start(ap, fmt);
190     vsnprintf(err_str, sizeof (err_str), fmt, args);
191     syslog(LOG_DEBUG, err_str);
192     va_end(ap);
193 #endif
194 }
195 
196 #if 0
197 extern void krb5int_debug_fprint(const char *, ...);
198 #define dprint krb5int_debug_fprint
199 #define print_addrlist krb5int_print_addrlist
200 extern void print_addrlist (const struct addrlist *a);
201 #else
202 static inline void dprint(const char *fmt, ...) { }
203 static inline void print_addrlist(const struct addrlist *a) { }
204 #endif
205 
206 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a,
207 				 void (*freefn)(void *), void *data)
208 {
209     int err;
210 
211     dprint("\tadding %p=%A to %p (naddrs=%d space=%d)\n", a, a, lp,
212 	   lp->naddrs, lp->space);
213 
214     if (lp->naddrs == lp->space) {
215 	err = grow_list (lp, 1);
216 	if (err) {
217 	    Tprintf ("grow_list failed %d\n", err);
218 	    return err;
219 	}
220     }
221     Tprintf("setting element %d\n", lp->naddrs);
222     lp->addrs[lp->naddrs].ai = a;
223     lp->addrs[lp->naddrs].freefn = freefn;
224     lp->addrs[lp->naddrs].data = data;
225     lp->naddrs++;
226     Tprintf ("\tcount is now %d: ", lp->naddrs);
227     print_addrlist(lp);
228     Tprintf("\n");
229     return 0;
230 }
231 
232 #define add_host_to_list krb5int_add_host_to_list
233 
234 static void call_freeaddrinfo(void *data)
235 {
236     /* Strict interpretation of the C standard says we can't assume
237        that the ABI for f(void*) and f(struct foo *) will be
238        compatible.  Use this stub just to be paranoid.  */
239     freeaddrinfo(data);
240 }
241 
242 int
243 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname,
244 			  int port, int secport,
245 			  int socktype, int family)
246 {
247     struct addrinfo *addrs, *a, *anext, hint;
248     int err;
249     char portbuf[10], secportbuf[10];
250     void (*freefn)(void *);
251 
252     Tprintf ("adding hostname %s, ports %d,%d, family %d, socktype %d\n",
253 	     hostname, ntohs (port), ntohs (secport),
254 	     family, socktype);
255 
256     memset(&hint, 0, sizeof(hint));
257     hint.ai_family = family;
258     hint.ai_socktype = socktype;
259 #ifdef AI_NUMERICSERV
260     hint.ai_flags = AI_NUMERICSERV;
261 #endif
262     sprintf(portbuf, "%d", ntohs(port));
263     sprintf(secportbuf, "%d", ntohs(secport));
264     err = getaddrinfo (hostname, portbuf, &hint, &addrs);
265     if (err) {
266 	Tprintf ("\tgetaddrinfo(\"%s\", \"%s\", ...)\n\treturns %d: %s\n",
267 		 hostname, portbuf, err, gai_strerror (err));
268 	return translate_ai_error (err);
269     }
270     freefn = call_freeaddrinfo;
271     anext = 0;
272     for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
273 	anext = a->ai_next;
274 	err = add_addrinfo_to_list (lp, a, freefn, a);
275     }
276     if (err || secport == 0)
277 	goto egress;
278     if (socktype == 0)
279 	socktype = SOCK_DGRAM;
280     else if (socktype != SOCK_DGRAM)
281 	goto egress;
282     hint.ai_family = AF_INET;
283     err = getaddrinfo (hostname, secportbuf, &hint, &addrs);
284     if (err) {
285 	err = translate_ai_error (err);
286 	goto egress;
287     }
288     freefn = call_freeaddrinfo;
289     for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) {
290 	anext = a->ai_next;
291 	err = add_addrinfo_to_list (lp, a, freefn, a);
292     }
293 egress:
294     /* Solaris Kerberos */
295     if (anext)
296 	freeaddrinfo (anext);
297     return err;
298 }
299 
300 /*
301  * returns count of number of addresses found
302  * if master is non-NULL, it is filled in with the index of
303  * the master kdc
304  */
305 
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)
311 {
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;
316 
317     Tprintf ("looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
318 	     realm->data, name, ntohs (udpport), ntohs (sec_udpport));
319 
320     if ((host = malloc(realm->length + 1)) == NULL)
321 	return ENOMEM;
322 
323     strncpy(host, realm->data, realm->length);
324     host[realm->length] = '\0';
325     hostlist = 0;
326 
327     masterlist = NULL;
328 
329     realm_srv_names[0] = "realms";
330     realm_srv_names[1] = host;
331     realm_srv_names[2] = name;
332     realm_srv_names[3] = 0;
333 
334     code = profile_get_values(context->profile, realm_srv_names, &hostlist);
335 
336     if (code) {
337 	Tprintf ("config file lookup failed: %s\n",
338 		 error_message(code));
339         if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
340 	    code = KRB5_REALM_UNKNOWN;
341  	krb5_xfree(host);
342   	return code;
343      }
344 
345     count = 0;
346     while (hostlist && hostlist[count])
347 	    count++;
348     Tprintf ("found %d entries under 'kdc'\n", count);
349 
350     if (count == 0) {
351         profile_free_list(hostlist);
352 	krb5_xfree(host);
353 	addrlist->naddrs = 0;
354 	return 0;
355     }
356 
357     if (get_masters) {
358 	realm_srv_names[0] = "realms";
359 	realm_srv_names[1] = host;
360 	realm_srv_names[2] = "admin_server";
361 	realm_srv_names[3] = 0;
362 
363 	code = profile_get_values(context->profile, realm_srv_names,
364 				  &masterlist);
365 
366 	krb5_xfree(host);
367 
368 	if (code == 0) {
369 	    for (i=0; masterlist[i]; i++) {
370 		host = masterlist[i];
371 
372 		/*
373 		 * Strip off excess whitespace
374 		 */
375 		cp = strchr(host, ' ');
376 		if (cp)
377 		    *cp = 0;
378 		cp = strchr(host, '\t');
379 		if (cp)
380 		    *cp = 0;
381 		cp = strchr(host, ':');
382 		if (cp)
383 		    *cp = 0;
384 	    }
385 	}
386     } else {
387 	krb5_xfree(host);
388     }
389 
390     /* at this point, if master is non-NULL, then either the master kdc
391        is required, and there is one, or the master kdc is not required,
392        and there may or may not be one. */
393 
394 #ifdef HAVE_NETINET_IN_H
395     if (sec_udpport)
396 	    count = count * 2;
397 #endif
398 
399     for (i=0; hostlist[i]; i++) {
400 	int p1, p2;
401 
402 	host = hostlist[i];
403 	Tprintf ("entry %d is '%s'\n", i, host);
404 	/*
405 	 * Strip off excess whitespace
406 	 */
407 	cp = strchr(host, ' ');
408 	if (cp)
409 	    *cp = 0;
410 	cp = strchr(host, '\t');
411 	if (cp)
412 	    *cp = 0;
413 	port = strchr(host, ':');
414 	if (port) {
415 	    *port = 0;
416 	    port++;
417 	}
418 
419 	ismaster = 0;
420 	if (masterlist) {
421 	    for (j=0; masterlist[j]; j++) {
422 		if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
423 		    ismaster = 1;
424 		}
425 	    }
426 	}
427 
428 	if (get_masters && !ismaster)
429 	    continue;
430 
431 	if (port) {
432 	    unsigned long l;
433 #ifdef HAVE_STROUL
434 	    char *endptr;
435 	    l = strtoul (port, &endptr, 10);
436 	    if (endptr == NULL || *endptr != 0)
437 		return EINVAL;
438 #else
439 	    l = atoi (port);
440 #endif
441 	    /* L is unsigned, don't need to check <0.  */
442 	    if (l > 65535)
443 		return EINVAL;
444 	    p1 = htons (l);
445 	    p2 = 0;
446 	} else {
447 	    p1 = udpport;
448 	    p2 = sec_udpport;
449 	}
450 
451 	if (socktype != 0)
452 	    code = add_host_to_list (addrlist, hostlist[i], p1, p2,
453 				     socktype, family);
454 	else {
455 	    code = add_host_to_list (addrlist, hostlist[i], p1, p2,
456 				     SOCK_DGRAM, family);
457 	    if (code == 0)
458 		code = add_host_to_list (addrlist, hostlist[i], p1, p2,
459 					 SOCK_STREAM, family);
460 	}
461 	if (code) {
462 	    Tprintf ("error %d (%s) returned from add_host_to_list\n", code,
463 		     error_message (code));
464 	    if (hostlist)
465 		profile_free_list (hostlist);
466 	    if (masterlist)
467 		profile_free_list (masterlist);
468 	    return code;
469 	}
470     }
471 
472     if (hostlist)
473         profile_free_list(hostlist);
474     if (masterlist)
475         profile_free_list(masterlist);
476 
477     return 0;
478 }
479 
480 #ifdef TEST
481 static krb5_error_code
482 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
483 		     const char *name, struct addrlist *al, int get_masters,
484 		     int udpport, int sec_udpport)
485 {
486     krb5_error_code ret;
487 
488     ret = krb5_locate_srv_conf_1 (context, realm, name, al,
489 				  get_masters, 0, udpport, sec_udpport, 0);
490     if (ret)
491 	return ret;
492     if (al->naddrs == 0)	/* Couldn't resolve any KDC names */
493 	return KRB5_REALM_CANT_RESOLVE;
494     return 0;
495 }
496 #endif
497 
498 #ifdef KRB5_DNS_LOOKUP
499 static krb5_error_code
500 krb5_locate_srv_dns_1 (const krb5_data *realm,
501 		       const char *service,
502 		       const char *protocol,
503 		       struct addrlist *addrlist,
504 		       int family)
505 {
506     struct srv_dns_entry *head = NULL;
507     struct srv_dns_entry *entry = NULL, *next;
508     krb5_error_code code = 0;
509 
510     code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
511     if (code)
512 	return 0;
513 
514     /*
515      * Okay!  Now we've got a linked list of entries sorted by
516      * priority.  Start looking up A records and returning
517      * addresses.
518      */
519 
520     if (head == NULL)
521 	return 0;
522 
523     /* Check for the "." case indicating no support.  */
524     if (head->next == 0 && head->host[0] == 0) {
525 	free(head->host);
526 	free(head);
527 	return KRB5_ERR_NO_SERVICE;
528     }
529 
530     Tprintf ("walking answer list:\n");
531     for (entry = head; entry != NULL; entry = next) {
532 	Tprintf ("\tport=%d host=%s\n", entry->port, entry->host);
533 	next = entry->next;
534 	code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
535 				 (strcmp("_tcp", protocol)
536 				  ? SOCK_DGRAM
537 				  : SOCK_STREAM), family);
538 	if (code) {
539 	    break;
540 	}
541 	if (entry == head) {
542 	    free(entry->host);
543 	    free(entry);
544 	    head = next;
545 	    entry = 0;
546 	}
547     }
548     Tprintf ("[end]\n");
549 
550     krb5int_free_srv_dns_data(head);
551     return code;
552 }
553 #endif
554 
555 #include <locate_plugin.h>
556 
557 #if TARGET_OS_MAC
558 static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/libkrb5", NULL }; /* should be a list */
559 #else
560 static const char *objdirs[] = { LIBDIR "/krb5/plugins/libkrb5", NULL };
561 #endif
562 
563 struct module_callback_data {
564     int out_of_mem;
565     struct addrlist *lp;
566 };
567 
568 static int
569 module_callback (void *cbdata, int socktype, struct sockaddr *sa)
570 {
571     struct module_callback_data *d = cbdata;
572     struct {
573 	struct addrinfo ai;
574 	union {
575 	    struct sockaddr_in sin;
576 	    struct sockaddr_in6 sin6;
577 	} u;
578     } *x;
579 
580     if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
581 	return 0;
582     if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
583 	return 0;
584     x = malloc (sizeof (*x));
585     if (x == 0) {
586 	d->out_of_mem = 1;
587 	return 1;
588     }
589     memset(x, 0, sizeof (*x));
590     x->ai.ai_addr = (struct sockaddr *) &x->u;
591     x->ai.ai_socktype = socktype;
592     x->ai.ai_family = sa->sa_family;
593     if (sa->sa_family == AF_INET) {
594 	x->u.sin = *(struct sockaddr_in *)sa;
595 	x->ai.ai_addrlen = sizeof(struct sockaddr_in);
596     }
597     if (sa->sa_family == AF_INET6) {
598 	x->u.sin6 = *(struct sockaddr_in6 *)sa;
599 	x->ai.ai_addrlen = sizeof(struct sockaddr_in6);
600     }
601     if (add_addrinfo_to_list (d->lp, &x->ai, free, x) != 0) {
602 	/* Assumes only error is ENOMEM.  */
603 	d->out_of_mem = 1;
604 	return 1;
605     }
606     return 0;
607 }
608 
609 static krb5_error_code
610 module_locate_server (krb5_context ctx, const krb5_data *realm,
611 		      struct addrlist *addrlist,
612 		      enum locate_service_type svc, int socktype, int family)
613 {
614     struct krb5plugin_service_locate_result *res = NULL;
615     krb5_error_code code;
616     struct krb5plugin_service_locate_ftable *vtbl = NULL;
617     void **ptrs;
618     int i;
619     struct module_callback_data cbdata = { 0, };
620 
621     Tprintf("in module_locate_server\n");
622     cbdata.lp = addrlist;
623     if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) {
624 
625 	code = krb5int_open_plugin_dirs (objdirs, NULL, &ctx->libkrb5_plugins,
626 					 &ctx->err);
627 	if (code)
628 	    return KRB5_PLUGIN_NO_HANDLE;
629     }
630 
631     code = krb5int_get_plugin_dir_data (&ctx->libkrb5_plugins,
632 					"service_locator", &ptrs, &ctx->err);
633     if (code) {
634 	Tprintf("error looking up plugin symbols: %s\n",
635 		krb5_get_error_message(ctx, code));
636 	return KRB5_PLUGIN_NO_HANDLE;
637     }
638 
639     for (i = 0; ptrs[i]; i++) {
640 	void *blob;
641 
642 	vtbl = ptrs[i];
643 	Tprintf("element %d is %p\n", i, ptrs[i]);
644 
645 	/* For now, don't keep the plugin data alive.  For long-lived
646 	   contexts, it may be desirable to change that later.  */
647 	code = vtbl->init(ctx, &blob);
648 	if (code)
649 	    continue;
650 
651 	code = vtbl->lookup(blob, svc, realm->data, socktype, family,
652 			    module_callback, &cbdata);
653 	vtbl->fini(blob);
654 	if (code == KRB5_PLUGIN_NO_HANDLE) {
655 	    /* Module passes, keep going.  */
656 	    /* XXX */
657 	    Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)\n");
658 	    continue;
659 	}
660 	if (code != 0) {
661 	    /* Module encountered an actual error.  */
662 	    Tprintf("plugin lookup routine returned error %d: %s\n",
663 		    code, error_message(code));
664 	    krb5int_free_plugin_dir_data (ptrs);
665 	    return code;
666 	}
667 	break;
668     }
669     if (ptrs[i] == NULL) {
670 	Tprintf("ran off end of plugin list\n");
671 	krb5int_free_plugin_dir_data (ptrs);
672 	return KRB5_PLUGIN_NO_HANDLE;
673     }
674     Tprintf("stopped with plugin #%d, res=%p\n", i, res);
675 
676     /* Got something back, yippee.  */
677     Tprintf("now have %d addrs in list %p\n", addrlist->naddrs, addrlist);
678     print_addrlist(addrlist);
679     krb5int_free_plugin_dir_data (ptrs);
680     return 0;
681 }
682 
683 static krb5_error_code
684 prof_locate_server (krb5_context context, const krb5_data *realm,
685 		    struct addrlist *addrlist,
686 		    enum locate_service_type svc, int socktype, int family)
687 {
688     const char *profname;
689     int dflport1, dflport2 = 0;
690     struct servent *serv;
691 
692     switch (svc) {
693     case locate_service_kdc:
694 	profname = "kdc";
695 	/* We used to use /etc/services for these, but enough systems
696 	   have old, crufty, wrong settings that this is probably
697 	   better.  */
698     kdc_ports:
699 	dflport1 = htons(KRB5_DEFAULT_PORT);
700 	dflport2 = htons(KRB5_DEFAULT_SEC_PORT);
701 	break;
702     case locate_service_master_kdc:
703 	profname = "master_kdc";
704 	goto kdc_ports;
705     case locate_service_kadmin:
706 	profname = "admin_server";
707 	dflport1 = htons(DEFAULT_KADM5_PORT);
708 	break;
709     case locate_service_krb524:
710 	profname = "krb524_server";
711 	serv = getservbyname(KRB524_SERVICE, "udp");
712 	dflport1 = serv ? serv->s_port : htons (KRB524_PORT);
713 	break;
714     case locate_service_kpasswd:
715 	profname = "kpasswd_server";
716 	dflport1 = htons(DEFAULT_KPASSWD_PORT);
717 	break;
718     default:
719 	return EBUSY;		/* XXX */
720     }
721 
722     return krb5_locate_srv_conf_1 (context, realm, profname, addrlist,
723 				   0, socktype,
724 				   dflport1, dflport2, family);
725 }
726 
727 static krb5_error_code
728 dns_locate_server (krb5_context context, const krb5_data *realm,
729 		   struct addrlist *addrlist,
730 		   enum locate_service_type svc, int socktype, int family)
731 {
732     const char *dnsname;
733     int use_dns = _krb5_use_dns_kdc(context);
734     krb5_error_code code;
735 
736     if (!use_dns)
737 	return KRB5_PLUGIN_NO_HANDLE;
738 
739     switch (svc) {
740     case locate_service_kdc:
741 	dnsname = "_kerberos";
742 	break;
743     case locate_service_master_kdc:
744 	dnsname = "_kerberos-master";
745 	break;
746     case locate_service_kadmin:
747 	dnsname = "_kerberos-adm";
748 	break;
749     case locate_service_krb524:
750 	dnsname = "_krb524";
751 	break;
752     case locate_service_kpasswd:
753 	dnsname = "_kpasswd";
754 	break;
755     default:
756 	return KRB5_PLUGIN_NO_HANDLE;
757     }
758 
759     code = 0;
760     if (socktype == SOCK_DGRAM || socktype == 0) {
761 	code = krb5_locate_srv_dns_1(realm, dnsname, "_udp", addrlist, family);
762 	if (code)
763 	    Tprintf("dns udp lookup returned error %d\n", code);
764     }
765     if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
766 	code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp", addrlist, family);
767 	if (code)
768 	    Tprintf("dns tcp lookup returned error %d\n", code);
769     }
770     return code;
771 }
772 
773 /*
774  * Wrapper function for the various backends
775  */
776 
777 krb5_error_code
778 krb5int_locate_server (krb5_context context, const krb5_data *realm,
779 		       struct addrlist *addrlist,
780 		       enum locate_service_type svc,
781 		       int socktype, int family)
782 {
783     krb5_error_code code;
784     struct addrlist al = ADDRLIST_INIT;
785 
786     *addrlist = al;
787 
788     code = module_locate_server(context, realm, &al, svc, socktype, family);
789     Tprintf("module_locate_server returns %d\n", code);
790     if (code == KRB5_PLUGIN_NO_HANDLE) {
791 	/*
792 	 * We always try the local file before DNS.  Note that there
793 	 * is no way to indicate "service not available" via the
794 	 * config file.
795 	 */
796 
797 	code = prof_locate_server(context, realm, &al, svc, socktype, family);
798 
799 #ifdef KRB5_DNS_LOOKUP
800 	/*
801 	 * Solaris Kerberos:
802 	 * There is no point in trying to locate the KDC in DNS if "realm"
803 	 * is empty.
804 	 */
805 	/* Try DNS for all profile errors?  */
806 	if (code && !krb5_is_referral_realm(realm)) {
807 	    krb5_error_code code2;
808 	    code2 = dns_locate_server(context, realm, &al, svc, socktype,
809 				      family);
810 	    if (code2 != KRB5_PLUGIN_NO_HANDLE)
811 		code = code2;
812 	}
813 #endif /* KRB5_DNS_LOOKUP */
814 
815 	/* We could put more heuristics here, like looking up a hostname
816 	   of "kerberos."+REALM, etc.  */
817     }
818     if (code == 0)
819 	Tprintf ("krb5int_locate_server found %d addresses\n",
820 		 al.naddrs);
821     else
822 	Tprintf ("krb5int_locate_server returning error code %d/%s\n",
823 		 code, error_message(code));
824     if (code != 0) {
825 	if (al.space)
826 	    free_list (&al);
827 	return code;
828     }
829     if (al.naddrs == 0) {	/* No good servers */
830 	if (al.space)
831 	    free_list (&al);
832 	krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
833 			       "Cannot resolve network address for KDC in realm %.*s",
834 			       realm->length, realm->data);
835 
836 	return KRB5_REALM_CANT_RESOLVE;
837     }
838     *addrlist = al;
839     return 0;
840 }
841 
842 krb5_error_code
843 krb5_locate_kdc(krb5_context context, const krb5_data *realm,
844 		struct addrlist *addrlist,
845 		int get_masters, int socktype, int family)
846 {
847     return krb5int_locate_server(context, realm, addrlist,
848 				 (get_masters
849 				  ? locate_service_master_kdc
850 				  : locate_service_kdc),
851 				 socktype, family);
852 }
853 
854 /*
855  * Solaris Kerberos: for backward compat.  Avoid using this
856  * function!
857  */
858 krb5_error_code
859 krb5_get_servername(krb5_context context,
860     const krb5_data *realm,
861     const char *name, const char *proto,
862     char *srvhost,
863     unsigned short *port)
864 {
865     krb5_error_code code = KRB5_REALM_UNKNOWN;
866 
867 #ifdef KRB5_DNS_LOOKUP
868     {
869 	int use_dns = _krb5_use_dns_kdc(context);
870 
871 	if (use_dns) {
872 	    struct srv_dns_entry *head = NULL;
873 
874 	    code = krb5int_make_srv_query_realm(realm, name, proto, &head);
875 	    if (code)
876 		return (code);
877 
878 	    if (head == NULL)
879 		return KRB5_REALM_CANT_RESOLVE;
880 
881 	    *port = head->port;
882 	    (void) strlcpy(srvhost, head->host, MAX_DNS_NAMELEN);
883 
884 #ifdef DEBUG
885 	    fprintf (stderr, "krb5_get_servername svrhost %s, port %d\n",
886 		srvhost, *port);
887 #endif
888 	    krb5int_free_srv_dns_data(head);
889 	}
890     }
891 #endif /* KRB5_DNS_LOOKUP */
892 
893     return (code);
894 }
895