1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/locate_kdc.c - Get addresses for realm KDCs and other servers */
3 /*
4 * Copyright 1990,2000,2001,2002,2003,2004,2006,2008 Massachusetts Institute of
5 * Technology. All Rights Reserved.
6 *
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.
11 *
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.
25 */
26
27 #include "k5-int.h"
28 #include "fake-addrinfo.h"
29 #include "os-proto.h"
30
31 #ifdef KRB5_DNS_LOOKUP
32
33 #define DEFAULT_LOOKUP_KDC 1
34 #if KRB5_DNS_LOOKUP_REALM
35 #define DEFAULT_LOOKUP_REALM 1
36 #else
37 #define DEFAULT_LOOKUP_REALM 0
38 #endif
39 #define DEFAULT_URI_LOOKUP TRUE
40
41 static int
maybe_use_dns(krb5_context context,const char * name,int defalt)42 maybe_use_dns (krb5_context context, const char *name, int defalt)
43 {
44 krb5_error_code code;
45 char * value = NULL;
46 int use_dns = 0;
47
48 code = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
49 name, 0, 0, &value);
50 if (value == 0 && code == 0) {
51 code = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
52 KRB5_CONF_DNS_FALLBACK, 0, 0, &value);
53 }
54 if (code)
55 return defalt;
56
57 if (value == 0)
58 return defalt;
59
60 use_dns = _krb5_conf_boolean(value);
61 profile_release_string(value);
62 return use_dns;
63 }
64
65 static krb5_boolean
use_dns_uri(krb5_context ctx)66 use_dns_uri(krb5_context ctx)
67 {
68 krb5_error_code ret;
69 int use;
70
71 ret = profile_get_boolean(ctx->profile, KRB5_CONF_LIBDEFAULTS,
72 KRB5_CONF_DNS_URI_LOOKUP, NULL,
73 DEFAULT_URI_LOOKUP, &use);
74 return ret ? DEFAULT_URI_LOOKUP : use;
75 }
76
77 int
_krb5_use_dns_kdc(krb5_context context)78 _krb5_use_dns_kdc(krb5_context context)
79 {
80 return maybe_use_dns(context, KRB5_CONF_DNS_LOOKUP_KDC,
81 DEFAULT_LOOKUP_KDC);
82 }
83
84 int
_krb5_use_dns_realm(krb5_context context)85 _krb5_use_dns_realm(krb5_context context)
86 {
87 return maybe_use_dns(context, KRB5_CONF_DNS_LOOKUP_REALM,
88 DEFAULT_LOOKUP_REALM);
89 }
90
91 #endif /* KRB5_DNS_LOOKUP */
92
93 /* Free up everything pointed to by the serverlist structure, but don't
94 * free the structure itself. */
95 void
k5_free_serverlist(struct serverlist * list)96 k5_free_serverlist (struct serverlist *list)
97 {
98 size_t i;
99
100 for (i = 0; i < list->nservers; i++) {
101 free(list->servers[i].hostname);
102 free(list->servers[i].uri_path);
103 }
104 free(list->servers);
105 list->servers = NULL;
106 list->nservers = 0;
107 }
108
109 #include <stdarg.h>
110 static inline void
Tprintf(const char * fmt,...)111 Tprintf(const char *fmt, ...)
112 {
113 #ifdef TEST
114 va_list ap;
115 va_start(ap, fmt);
116 vfprintf(stderr, fmt, ap);
117 va_end(ap);
118 #endif
119 }
120
121 /* Make room for a new server entry in list and return a pointer to the new
122 * entry. (Do not increment list->nservers.) */
123 static struct server_entry *
new_server_entry(struct serverlist * list)124 new_server_entry(struct serverlist *list)
125 {
126 struct server_entry *newservers, *entry;
127 size_t newspace = (list->nservers + 1) * sizeof(struct server_entry);
128
129 newservers = realloc(list->servers, newspace);
130 if (newservers == NULL)
131 return NULL;
132 list->servers = newservers;
133 entry = &newservers[list->nservers];
134 memset(entry, 0, sizeof(*entry));
135 entry->primary = -1;
136 return entry;
137 }
138
139 /* Add an address entry to list. */
140 static int
add_addr_to_list(struct serverlist * list,k5_transport transport,int family,size_t addrlen,struct sockaddr * addr)141 add_addr_to_list(struct serverlist *list, k5_transport transport, int family,
142 size_t addrlen, struct sockaddr *addr)
143 {
144 struct server_entry *entry;
145
146 entry = new_server_entry(list);
147 if (entry == NULL)
148 return ENOMEM;
149 entry->transport = transport;
150 entry->family = family;
151 entry->hostname = NULL;
152 entry->uri_path = NULL;
153 entry->addrlen = addrlen;
154 memcpy(&entry->addr, addr, addrlen);
155 list->nservers++;
156 return 0;
157 }
158
159 /* Add a hostname entry to list. */
160 static int
add_host_to_list(struct serverlist * list,const char * hostname,int port,k5_transport transport,int family,const char * uri_path,int primary)161 add_host_to_list(struct serverlist *list, const char *hostname, int port,
162 k5_transport transport, int family, const char *uri_path,
163 int primary)
164 {
165 struct server_entry *entry;
166
167 entry = new_server_entry(list);
168 if (entry == NULL)
169 return ENOMEM;
170 entry->transport = transport;
171 entry->family = family;
172 entry->hostname = strdup(hostname);
173 if (entry->hostname == NULL)
174 goto oom;
175 if (uri_path != NULL) {
176 entry->uri_path = strdup(uri_path);
177 if (entry->uri_path == NULL)
178 goto oom;
179 }
180 entry->port = port;
181 entry->primary = primary;
182 list->nservers++;
183 return 0;
184 oom:
185 free(entry->hostname);
186 entry->hostname = NULL;
187 return ENOMEM;
188 }
189
190 static void
parse_uri_if_https(const char * host_or_uri,k5_transport * transport,const char ** host,const char ** uri_path)191 parse_uri_if_https(const char *host_or_uri, k5_transport *transport,
192 const char **host, const char **uri_path)
193 {
194 char *cp;
195
196 if (strncmp(host_or_uri, "https://", 8) == 0) {
197 *transport = HTTPS;
198 *host = host_or_uri + 8;
199
200 cp = strchr(*host, '/');
201 if (cp != NULL) {
202 *cp = '\0';
203 *uri_path = cp + 1;
204 }
205 }
206 }
207
208 /* Return true if server is identical to an entry in list. */
209 static krb5_boolean
server_list_contains(struct serverlist * list,struct server_entry * server)210 server_list_contains(struct serverlist *list, struct server_entry *server)
211 {
212 struct server_entry *ent;
213
214 for (ent = list->servers; ent < list->servers + list->nservers; ent++) {
215 if (server->hostname != NULL && ent->hostname != NULL &&
216 strcmp(server->hostname, ent->hostname) == 0)
217 return TRUE;
218 if (server->hostname == NULL && ent->hostname == NULL &&
219 server->addrlen == ent->addrlen &&
220 memcmp(&server->addr, &ent->addr, server->addrlen) == 0)
221 return TRUE;
222 }
223 return FALSE;
224 }
225
226 static krb5_error_code
locate_srv_conf_1(krb5_context context,const krb5_data * realm,const char * name,struct serverlist * serverlist,k5_transport transport,int udpport)227 locate_srv_conf_1(krb5_context context, const krb5_data *realm,
228 const char * name, struct serverlist *serverlist,
229 k5_transport transport, int udpport)
230 {
231 const char *realm_srv_names[4];
232 char **hostlist = NULL, *realmstr = NULL, *host = NULL;
233 const char *hostspec;
234 krb5_error_code code;
235 int i, default_port;
236
237 Tprintf("looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
238 realm->data, name, udpport);
239
240 realmstr = k5memdup0(realm->data, realm->length, &code);
241 if (realmstr == NULL)
242 goto cleanup;
243
244 realm_srv_names[0] = KRB5_CONF_REALMS;
245 realm_srv_names[1] = realmstr;
246 realm_srv_names[2] = name;
247 realm_srv_names[3] = 0;
248 code = profile_get_values(context->profile, realm_srv_names, &hostlist);
249 if (code == PROF_NO_RELATION && strcmp(name, KRB5_CONF_PRIMARY_KDC) == 0) {
250 realm_srv_names[2] = KRB5_CONF_MASTER_KDC;
251 code = profile_get_values(context->profile, realm_srv_names,
252 &hostlist);
253 }
254 if (code) {
255 Tprintf("config file lookup failed: %s\n", error_message(code));
256 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
257 code = 0;
258 goto cleanup;
259 }
260
261 for (i = 0; hostlist[i]; i++) {
262 int port_num;
263 k5_transport this_transport = transport;
264 const char *uri_path = NULL;
265
266 hostspec = hostlist[i];
267 Tprintf("entry %d is '%s'\n", i, hostspec);
268
269 parse_uri_if_https(hostspec, &this_transport, &hostspec, &uri_path);
270
271 default_port = (this_transport == HTTPS) ? 443 : udpport;
272 code = k5_parse_host_string(hostspec, default_port, &host, &port_num);
273 if (code == 0 && host == NULL)
274 code = EINVAL;
275 if (code)
276 goto cleanup;
277
278 code = add_host_to_list(serverlist, host, port_num, this_transport,
279 AF_UNSPEC, uri_path, -1);
280 if (code)
281 goto cleanup;
282
283 free(host);
284 host = NULL;
285 }
286
287 cleanup:
288 free(realmstr);
289 free(host);
290 profile_free_list(hostlist);
291 return code;
292 }
293
294 #ifdef TEST
295 static krb5_error_code
krb5_locate_srv_conf(krb5_context context,const krb5_data * realm,const char * name,struct serverlist * al,int udpport)296 krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
297 const char *name, struct serverlist *al, int udpport)
298 {
299 krb5_error_code ret;
300
301 ret = locate_srv_conf_1(context, realm, name, al, TCP_OR_UDP, udpport);
302 if (ret)
303 return ret;
304 if (al->nservers == 0) /* Couldn't resolve any KDC names */
305 return KRB5_REALM_CANT_RESOLVE;
306 return 0;
307 }
308 #endif
309
310 #ifdef KRB5_DNS_LOOKUP
311 static krb5_error_code
locate_srv_dns_1(krb5_context context,const krb5_data * realm,const char * service,const char * protocol,struct serverlist * serverlist)312 locate_srv_dns_1(krb5_context context, const krb5_data *realm,
313 const char *service, const char *protocol,
314 struct serverlist *serverlist)
315 {
316 struct srv_dns_entry *head = NULL, *entry = NULL;
317 krb5_error_code code = 0;
318 k5_transport transport;
319
320 code = krb5int_make_srv_query_realm(context, realm, service, protocol,
321 &head);
322 if (code)
323 return 0;
324
325 if (head == NULL)
326 return 0;
327
328 /* Check for the "." case indicating no support. */
329 if (head->next == NULL && head->host[0] == '\0') {
330 code = KRB5_ERR_NO_SERVICE;
331 goto cleanup;
332 }
333
334 for (entry = head; entry != NULL; entry = entry->next) {
335 transport = (strcmp(protocol, "_tcp") == 0) ? TCP : UDP;
336 code = add_host_to_list(serverlist, entry->host, entry->port,
337 transport, AF_UNSPEC, NULL, -1);
338 if (code)
339 goto cleanup;
340 }
341
342 cleanup:
343 krb5int_free_srv_dns_data(head);
344 return code;
345 }
346 #endif
347
348 #include <krb5/locate_plugin.h>
349
350 #if TARGET_OS_MAC
351 static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR,
352 LIBDIR "/krb5/plugins/libkrb5",
353 NULL }; /* should be a list */
354 #else
355 static const char *objdirs[] = { LIBDIR "/krb5/plugins/libkrb5", NULL };
356 #endif
357
358 struct module_callback_data {
359 int out_of_mem;
360 struct serverlist *list;
361 };
362
363 static int
module_callback(void * cbdata,int socktype,struct sockaddr * sa)364 module_callback(void *cbdata, int socktype, struct sockaddr *sa)
365 {
366 struct module_callback_data *d = cbdata;
367 size_t addrlen;
368 k5_transport transport;
369
370 if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)
371 return 0;
372 if (sa->sa_family == AF_INET)
373 addrlen = sizeof(struct sockaddr_in);
374 else if (sa->sa_family == AF_INET6)
375 addrlen = sizeof(struct sockaddr_in6);
376 else
377 return 0;
378 transport = (socktype == SOCK_STREAM) ? TCP : UDP;
379 if (add_addr_to_list(d->list, transport, sa->sa_family, addrlen,
380 sa) != 0) {
381 /* Assumes only error is ENOMEM. */
382 d->out_of_mem = 1;
383 return 1;
384 }
385 return 0;
386 }
387
388 static krb5_error_code
module_locate_server(krb5_context ctx,const krb5_data * realm,struct serverlist * serverlist,enum locate_service_type svc,k5_transport transport)389 module_locate_server(krb5_context ctx, const krb5_data *realm,
390 struct serverlist *serverlist,
391 enum locate_service_type svc, k5_transport transport)
392 {
393 struct krb5plugin_service_locate_result *res = NULL;
394 krb5_error_code code;
395 struct krb5plugin_service_locate_ftable *vtbl = NULL;
396 void **ptrs;
397 char *realmz; /* NUL-terminated realm */
398 int socktype, i;
399 struct module_callback_data cbdata = { 0, };
400 const char *msg;
401
402 Tprintf("in module_locate_server\n");
403 cbdata.list = serverlist;
404 if (!PLUGIN_DIR_OPEN(&ctx->libkrb5_plugins)) {
405
406 code = krb5int_open_plugin_dirs(objdirs, NULL, &ctx->libkrb5_plugins,
407 &ctx->err);
408 if (code)
409 return KRB5_PLUGIN_NO_HANDLE;
410 }
411
412 code = krb5int_get_plugin_dir_data(&ctx->libkrb5_plugins,
413 "service_locator", &ptrs, &ctx->err);
414 if (code) {
415 Tprintf("error looking up plugin symbols: %s\n",
416 (msg = krb5_get_error_message(ctx, code)));
417 krb5_free_error_message(ctx, msg);
418 return KRB5_PLUGIN_NO_HANDLE;
419 }
420
421 if (realm->length >= UINT_MAX) {
422 krb5int_free_plugin_dir_data(ptrs);
423 return ENOMEM;
424 }
425 realmz = k5memdup0(realm->data, realm->length, &code);
426 if (realmz == NULL) {
427 krb5int_free_plugin_dir_data(ptrs);
428 return code;
429 }
430 for (i = 0; ptrs[i]; i++) {
431 void *blob;
432
433 vtbl = ptrs[i];
434 Tprintf("element %d is %p\n", i, ptrs[i]);
435
436 /* For now, don't keep the plugin data alive. For long-lived
437 * contexts, it may be desirable to change that later. */
438 code = vtbl->init(ctx, &blob);
439 if (code)
440 continue;
441
442 socktype = (transport == TCP) ? SOCK_STREAM : SOCK_DGRAM;
443 code = vtbl->lookup(blob, svc, realmz, socktype, AF_UNSPEC,
444 module_callback, &cbdata);
445 /* Also ask for TCP addresses if we got UDP addresses and want both. */
446 if (code == 0 && transport == TCP_OR_UDP) {
447 code = vtbl->lookup(blob, svc, realmz, SOCK_STREAM, AF_UNSPEC,
448 module_callback, &cbdata);
449 if (code == KRB5_PLUGIN_NO_HANDLE)
450 code = 0;
451 }
452 vtbl->fini(blob);
453 if (code == KRB5_PLUGIN_NO_HANDLE) {
454 /* Module passes, keep going. */
455 /* XXX */
456 Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)"
457 "\n");
458 continue;
459 }
460 if (code != 0) {
461 /* Module encountered an actual error. */
462 Tprintf("plugin lookup routine returned error %d: %s\n",
463 code, error_message(code));
464 free(realmz);
465 krb5int_free_plugin_dir_data(ptrs);
466 return code;
467 }
468 break;
469 }
470 if (ptrs[i] == NULL) {
471 Tprintf("ran off end of plugin list\n");
472 free(realmz);
473 krb5int_free_plugin_dir_data(ptrs);
474 return KRB5_PLUGIN_NO_HANDLE;
475 }
476 Tprintf("stopped with plugin #%d, res=%p\n", i, res);
477
478 /* Got something back, yippee. */
479 Tprintf("now have %lu addrs in list %p\n",
480 (unsigned long)serverlist->nservers, serverlist);
481 free(realmz);
482 krb5int_free_plugin_dir_data(ptrs);
483 return 0;
484 }
485
486 static krb5_error_code
prof_locate_server(krb5_context context,const krb5_data * realm,struct serverlist * serverlist,enum locate_service_type svc,k5_transport transport)487 prof_locate_server(krb5_context context, const krb5_data *realm,
488 struct serverlist *serverlist, enum locate_service_type svc,
489 k5_transport transport)
490 {
491 const char *profname;
492 int dflport = 0;
493 struct servent *serv;
494
495 switch (svc) {
496 case locate_service_kdc:
497 profname = KRB5_CONF_KDC;
498 /* We used to use /etc/services for these, but enough systems have old,
499 * crufty, wrong settings that this is probably better. */
500 kdc_ports:
501 dflport = KRB5_DEFAULT_PORT;
502 break;
503 case locate_service_primary_kdc:
504 profname = KRB5_CONF_PRIMARY_KDC;
505 goto kdc_ports;
506 case locate_service_kadmin:
507 profname = KRB5_CONF_ADMIN_SERVER;
508 dflport = DEFAULT_KADM5_PORT;
509 break;
510 case locate_service_krb524:
511 profname = KRB5_CONF_KRB524_SERVER;
512 serv = getservbyname("krb524", "udp");
513 dflport = serv ? serv->s_port : 4444;
514 break;
515 case locate_service_kpasswd:
516 profname = KRB5_CONF_KPASSWD_SERVER;
517 dflport = DEFAULT_KPASSWD_PORT;
518 break;
519 default:
520 return EBUSY; /* XXX */
521 }
522
523 return locate_srv_conf_1(context, realm, profname, serverlist, transport,
524 dflport);
525 }
526
527 #ifdef KRB5_DNS_LOOKUP
528
529 /*
530 * Parse the initial part of the URI, first confirming the scheme name. Get
531 * the transport, flags (indicating primary status), and host. The host is
532 * either an address or hostname with an optional port, or an HTTPS URL.
533 * The format is krb5srv:flags:udp|tcp|kkdcp:host
534 *
535 * Return a NULL *host_out if there are any problems parsing the URI.
536 */
537 static void
parse_uri_fields(const char * uri,k5_transport * transport_out,const char ** host_out,int * primary_out)538 parse_uri_fields(const char *uri, k5_transport *transport_out,
539 const char **host_out, int *primary_out)
540
541 {
542 k5_transport transport;
543 int primary = FALSE;
544
545 *transport_out = 0;
546 *host_out = NULL;
547 *primary_out = -1;
548
549 /* Confirm the scheme name. */
550 if (strncasecmp(uri, "krb5srv", 7) != 0)
551 return;
552
553 uri += 7;
554 if (*uri != ':')
555 return;
556
557 uri++;
558 if (*uri == '\0')
559 return;
560
561 /* Check the flags field for supported flags. */
562 for (; *uri != ':' && *uri != '\0'; uri++) {
563 if (*uri == 'm' || *uri == 'M')
564 primary = TRUE;
565 }
566 if (*uri != ':')
567 return;
568
569 /* Look for the transport type. */
570 uri++;
571 if (strncasecmp(uri, "udp", 3) == 0) {
572 transport = UDP;
573 uri += 3;
574 } else if (strncasecmp(uri, "tcp", 3) == 0) {
575 transport = TCP;
576 uri += 3;
577 } else if (strncasecmp(uri, "kkdcp", 5) == 0) {
578 /* Currently the only MS-KKDCP transport type is HTTPS. */
579 transport = HTTPS;
580 uri += 5;
581 } else {
582 return;
583 }
584
585 if (*uri != ':')
586 return;
587
588 /* The rest of the URI is the host (with optional port) or URI. */
589 *host_out = uri + 1;
590 *transport_out = transport;
591 *primary_out = primary;
592 }
593
594 /*
595 * Collect a list of servers from DNS URI records, for the requested service
596 * and transport type. Problematic entries are skipped.
597 */
598 static krb5_error_code
locate_uri(krb5_context context,const krb5_data * realm,const char * req_service,struct serverlist * serverlist,k5_transport req_transport,int default_port,krb5_boolean primary_only)599 locate_uri(krb5_context context, const krb5_data *realm,
600 const char *req_service, struct serverlist *serverlist,
601 k5_transport req_transport, int default_port,
602 krb5_boolean primary_only)
603 {
604 krb5_error_code ret;
605 k5_transport transport, host_trans;
606 struct srv_dns_entry *answers, *entry;
607 char *host;
608 const char *host_field, *path;
609 int port, def_port, primary;
610
611 ret = k5_make_uri_query(context, realm, req_service, &answers);
612 if (ret || answers == NULL)
613 return ret;
614
615 for (entry = answers; entry != NULL; entry = entry->next) {
616 def_port = default_port;
617 path = NULL;
618
619 parse_uri_fields(entry->host, &transport, &host_field, &primary);
620 if (host_field == NULL)
621 continue;
622
623 /* TCP_OR_UDP allows entries of any transport type; otherwise
624 * we're asking for a match. */
625 if (req_transport != TCP_OR_UDP && req_transport != transport)
626 continue;
627
628 /* Process a MS-KKDCP target. */
629 if (transport == HTTPS) {
630 host_trans = 0;
631 def_port = 443;
632 parse_uri_if_https(host_field, &host_trans, &host_field, &path);
633 if (host_trans != HTTPS)
634 continue;
635 }
636
637 ret = k5_parse_host_string(host_field, def_port, &host, &port);
638 if (ret == ENOMEM)
639 break;
640
641 if (ret || host == NULL) {
642 ret = 0;
643 continue;
644 }
645
646 ret = add_host_to_list(serverlist, host, port, transport, AF_UNSPEC,
647 path, primary);
648 free(host);
649 if (ret)
650 break;
651 }
652
653 krb5int_free_srv_dns_data(answers);
654 return ret;
655 }
656
657 static krb5_error_code
dns_locate_server_uri(krb5_context context,const krb5_data * realm,struct serverlist * serverlist,enum locate_service_type svc,k5_transport transport)658 dns_locate_server_uri(krb5_context context, const krb5_data *realm,
659 struct serverlist *serverlist,
660 enum locate_service_type svc, k5_transport transport)
661 {
662 krb5_error_code ret;
663 char *svcname;
664 int def_port;
665 krb5_boolean find_primary = FALSE;
666
667 if (!_krb5_use_dns_kdc(context) || !use_dns_uri(context))
668 return 0;
669
670 switch (svc) {
671 case locate_service_primary_kdc:
672 find_primary = TRUE;
673 /* Fall through */
674 case locate_service_kdc:
675 svcname = "_kerberos";
676 def_port = 88;
677 break;
678 case locate_service_kadmin:
679 svcname = "_kerberos-adm";
680 def_port = 749;
681 break;
682 case locate_service_kpasswd:
683 svcname = "_kpasswd";
684 def_port = 464;
685 break;
686 default:
687 return 0;
688 }
689
690 ret = locate_uri(context, realm, svcname, serverlist, transport, def_port,
691 find_primary);
692
693 if (serverlist->nservers == 0)
694 TRACE_DNS_URI_NOTFOUND(context);
695
696 return ret;
697 }
698
699 static krb5_error_code
dns_locate_server_srv(krb5_context context,const krb5_data * realm,struct serverlist * serverlist,enum locate_service_type svc,k5_transport transport)700 dns_locate_server_srv(krb5_context context, const krb5_data *realm,
701 struct serverlist *serverlist,
702 enum locate_service_type svc, k5_transport transport)
703 {
704 const char *dnsname;
705 int use_dns = _krb5_use_dns_kdc(context);
706 krb5_error_code code;
707
708 if (!use_dns)
709 return 0;
710
711 switch (svc) {
712 case locate_service_kdc:
713 dnsname = "_kerberos";
714 break;
715 case locate_service_primary_kdc:
716 dnsname = "_kerberos-master";
717 break;
718 case locate_service_kadmin:
719 dnsname = "_kerberos-adm";
720 break;
721 case locate_service_krb524:
722 dnsname = "_krb524";
723 break;
724 case locate_service_kpasswd:
725 dnsname = "_kpasswd";
726 break;
727 default:
728 return 0;
729 }
730
731 code = 0;
732 if (transport == UDP || transport == TCP_OR_UDP)
733 code = locate_srv_dns_1(context, realm, dnsname, "_udp", serverlist);
734
735 if ((transport == TCP || transport == TCP_OR_UDP) && code == 0)
736 code = locate_srv_dns_1(context, realm, dnsname, "_tcp", serverlist);
737
738 if (serverlist->nservers == 0)
739 TRACE_DNS_SRV_NOTFOUND(context);
740
741 return code;
742 }
743 #endif /* KRB5_DNS_LOOKUP */
744
745 /*
746 * Try all of the server location methods in sequence. transport must be
747 * TCP_OR_UDP, TCP, or UDP. It is applied to hostname entries in the profile
748 * and affects whether we query modules or DNS for UDP or TCP or both, but does
749 * not restrict a method from returning entries of other transports.
750 */
751 static krb5_error_code
locate_server(krb5_context context,const krb5_data * realm,struct serverlist * serverlist,enum locate_service_type svc,k5_transport transport)752 locate_server(krb5_context context, const krb5_data *realm,
753 struct serverlist *serverlist, enum locate_service_type svc,
754 k5_transport transport)
755 {
756 krb5_error_code ret;
757 struct serverlist list = SERVERLIST_INIT;
758
759 *serverlist = list;
760
761 /* Try modules. If a module returns 0 but leaves the list empty, return an
762 * empty list. */
763 ret = module_locate_server(context, realm, &list, svc, transport);
764 if (ret != KRB5_PLUGIN_NO_HANDLE)
765 goto done;
766
767 /* Try the profile. Fall back to DNS if it returns an empty list. */
768 ret = prof_locate_server(context, realm, &list, svc, transport);
769 if (ret)
770 goto done;
771
772 #ifdef KRB5_DNS_LOOKUP
773 if (list.nservers == 0) {
774 ret = dns_locate_server_uri(context, realm, &list, svc, transport);
775 if (ret)
776 goto done;
777 }
778
779 if (list.nservers == 0)
780 ret = dns_locate_server_srv(context, realm, &list, svc, transport);
781 #endif
782
783 done:
784 if (ret) {
785 k5_free_serverlist(&list);
786 return ret;
787 }
788 *serverlist = list;
789 return 0;
790 }
791
792 /*
793 * Wrapper function for the various backends
794 */
795
796 krb5_error_code
k5_locate_server(krb5_context context,const krb5_data * realm,struct serverlist * serverlist,enum locate_service_type svc,krb5_boolean no_udp)797 k5_locate_server(krb5_context context, const krb5_data *realm,
798 struct serverlist *serverlist, enum locate_service_type svc,
799 krb5_boolean no_udp)
800 {
801 krb5_error_code ret;
802 k5_transport transport = no_udp ? TCP : TCP_OR_UDP;
803
804 memset(serverlist, 0, sizeof(*serverlist));
805 if (realm == NULL || realm->data == NULL || realm->data[0] == 0) {
806 k5_setmsg(context, KRB5_REALM_CANT_RESOLVE,
807 "Cannot find KDC for invalid realm name \"\"");
808 return KRB5_REALM_CANT_RESOLVE;
809 }
810
811 ret = locate_server(context, realm, serverlist, svc, transport);
812 if (ret)
813 return ret;
814
815 if (serverlist->nservers == 0) {
816 k5_free_serverlist(serverlist);
817 k5_setmsg(context, KRB5_REALM_UNKNOWN,
818 _("Cannot find KDC for realm \"%.*s\""),
819 realm->length, realm->data);
820 return KRB5_REALM_UNKNOWN;
821 }
822 return 0;
823 }
824
825 krb5_error_code
k5_locate_kdc(krb5_context context,const krb5_data * realm,struct serverlist * serverlist,krb5_boolean get_primaries,krb5_boolean no_udp)826 k5_locate_kdc(krb5_context context, const krb5_data *realm,
827 struct serverlist *serverlist, krb5_boolean get_primaries,
828 krb5_boolean no_udp)
829 {
830 enum locate_service_type stype;
831
832 stype = get_primaries ? locate_service_primary_kdc : locate_service_kdc;
833 return k5_locate_server(context, realm, serverlist, stype, no_udp);
834 }
835
836 krb5_boolean
k5_kdc_is_primary(krb5_context context,const krb5_data * realm,struct server_entry * server)837 k5_kdc_is_primary(krb5_context context, const krb5_data *realm,
838 struct server_entry *server)
839 {
840 struct serverlist list;
841 krb5_boolean found;
842
843 if (server->primary != -1)
844 return server->primary;
845
846 if (locate_server(context, realm, &list, locate_service_primary_kdc,
847 server->transport) != 0)
848 return FALSE;
849 found = server_list_contains(&list, server);
850 k5_free_serverlist(&list);
851 return found;
852 }
853