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