Use anon realm for anonymous PKINIT
[heimdal.git] / lib / krb5 / krbhst.c
blob44689ff72db6abf74ed00adebe9f4175d9fa7475
1 /*
2 * Copyright (c) 2001 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "krb5_locl.h"
37 #include <resolve.h>
38 #include "locate_plugin.h"
40 static int
41 string_to_proto(const char *string)
43 if(strcasecmp(string, "udp") == 0)
44 return KRB5_KRBHST_UDP;
45 else if(strcasecmp(string, "tcp") == 0)
46 return KRB5_KRBHST_TCP;
47 else if(strcasecmp(string, "http") == 0)
48 return KRB5_KRBHST_HTTP;
49 return -1;
53 * set `res' and `count' to the result of looking up SRV RR in DNS for
54 * `proto', `proto', `realm' using `dns_type'.
55 * if `port' != 0, force that port number
58 static krb5_error_code
59 srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
60 const char *realm, const char *dns_type,
61 const char *proto, const char *service, int port)
63 char domain[1024];
64 struct rk_dns_reply *r;
65 struct rk_resource_record *rr;
66 int num_srv;
67 int proto_num;
68 int def_port;
70 *res = NULL;
71 *count = 0;
73 proto_num = string_to_proto(proto);
74 if(proto_num < 0) {
75 krb5_set_error_message(context, EINVAL,
76 N_("unknown protocol `%s' to lookup", ""),
77 proto);
78 return EINVAL;
81 if(proto_num == KRB5_KRBHST_HTTP)
82 def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
83 else if(port == 0)
84 def_port = ntohs(krb5_getportbyname (context, service, proto, 88));
85 else
86 def_port = port;
88 snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm);
90 r = rk_dns_lookup(domain, dns_type);
91 if(r == NULL) {
92 _krb5_debug(context, 0,
93 "DNS lookup failed domain: %s", domain);
94 return KRB5_KDC_UNREACH;
97 for(num_srv = 0, rr = r->head; rr; rr = rr->next)
98 if(rr->type == rk_ns_t_srv)
99 num_srv++;
101 *res = malloc(num_srv * sizeof(**res));
102 if(*res == NULL) {
103 rk_dns_free_data(r);
104 return krb5_enomem(context);
107 rk_dns_srv_order(r);
109 for(num_srv = 0, rr = r->head; rr; rr = rr->next)
110 if(rr->type == rk_ns_t_srv) {
111 krb5_krbhst_info *hi;
112 size_t len = strlen(rr->u.srv->target);
114 hi = calloc(1, sizeof(*hi) + len);
115 if(hi == NULL) {
116 rk_dns_free_data(r);
117 while(--num_srv >= 0)
118 free((*res)[num_srv]);
119 free(*res);
120 *res = NULL;
121 return krb5_enomem(context);
123 (*res)[num_srv++] = hi;
125 hi->proto = proto_num;
127 hi->def_port = def_port;
128 if (port != 0)
129 hi->port = port;
130 else
131 hi->port = rr->u.srv->port;
133 strlcpy(hi->hostname, rr->u.srv->target, len + 1);
136 *count = num_srv;
138 rk_dns_free_data(r);
139 return 0;
143 struct krb5_krbhst_data {
144 char *realm;
145 unsigned int flags;
146 int def_port;
147 int port; /* hardwired port number if != 0 */
148 #define KD_CONFIG 1
149 #define KD_SRV_UDP 2
150 #define KD_SRV_TCP 4
151 #define KD_SRV_HTTP 8
152 #define KD_FALLBACK 16
153 #define KD_CONFIG_EXISTS 32
154 #define KD_LARGE_MSG 64
155 #define KD_PLUGIN 128
156 #define KD_HOSTNAMES 256
157 krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *,
158 krb5_krbhst_info**);
160 char *hostname;
161 unsigned int fallback_count;
163 struct krb5_krbhst_info *hosts, **index, **end;
166 static krb5_boolean
167 krbhst_empty(const struct krb5_krbhst_data *kd)
169 return kd->index == &kd->hosts;
173 * Return the default protocol for the `kd' (either TCP or UDP)
176 static int
177 krbhst_get_default_proto(struct krb5_krbhst_data *kd)
179 if (kd->flags & KD_LARGE_MSG)
180 return KRB5_KRBHST_TCP;
181 return KRB5_KRBHST_UDP;
184 static int
185 krbhst_get_default_port(struct krb5_krbhst_data *kd)
187 return kd->def_port;
194 KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
195 _krb5_krbhst_get_realm(krb5_krbhst_handle handle)
197 return handle->realm;
201 * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port'
202 * and forcing it to `port' if port != 0
205 static struct krb5_krbhst_info*
206 parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
207 const char *spec, int def_port, int port)
209 const char *p = spec, *q;
210 struct krb5_krbhst_info *hi;
212 hi = calloc(1, sizeof(*hi) + strlen(spec));
213 if(hi == NULL)
214 return NULL;
216 hi->proto = krbhst_get_default_proto(kd);
218 if(strncmp(p, "http://", 7) == 0){
219 hi->proto = KRB5_KRBHST_HTTP;
220 p += 7;
221 } else if(strncmp(p, "http/", 5) == 0) {
222 hi->proto = KRB5_KRBHST_HTTP;
223 p += 5;
224 def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
225 }else if(strncmp(p, "tcp/", 4) == 0){
226 hi->proto = KRB5_KRBHST_TCP;
227 p += 4;
228 } else if(strncmp(p, "udp/", 4) == 0) {
229 hi->proto = KRB5_KRBHST_UDP;
230 p += 4;
233 if (p[0] == '[' && (q = strchr(p, ']')) != NULL) {
234 /* if address looks like [foo:bar] or [foo:bar]: its a ipv6
235 adress, strip of [] */
236 memcpy(hi->hostname, &p[1], q - p - 1);
237 hi->hostname[q - p - 1] = '\0';
238 p = q + 1;
239 /* get trailing : */
240 if (p[0] == ':')
241 p++;
242 } else if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) {
243 /* copy everything before : */
244 free(hi);
245 return NULL;
247 /* get rid of trailing /, and convert to lower case */
248 hi->hostname[strcspn(hi->hostname, "/")] = '\0';
249 strlwr(hi->hostname);
251 hi->port = hi->def_port = def_port;
252 if(p != NULL && p[0]) {
253 char *end;
254 hi->port = strtol(p, &end, 0);
255 if(end == p) {
256 free(hi);
257 return NULL;
260 if (port)
261 hi->port = port;
262 return hi;
265 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
266 _krb5_free_krbhst_info(krb5_krbhst_info *hi)
268 if (hi->ai != NULL)
269 freeaddrinfo(hi->ai);
270 free(hi);
273 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
274 _krb5_krbhost_info_move(krb5_context context,
275 krb5_krbhst_info *from,
276 krb5_krbhst_info **to)
278 size_t hostnamelen = strlen(from->hostname);
279 /* trailing NUL is included in structure */
280 *to = calloc(1, sizeof(**to) + hostnamelen);
281 if (*to == NULL)
282 return krb5_enomem(context);
284 (*to)->proto = from->proto;
285 (*to)->port = from->port;
286 (*to)->def_port = from->def_port;
287 (*to)->ai = from->ai;
288 from->ai = NULL;
289 (*to)->next = NULL;
290 memcpy((*to)->hostname, from->hostname, hostnamelen + 1);
291 return 0;
295 static void
296 append_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host)
298 struct krb5_krbhst_info *h;
300 for(h = kd->hosts; h; h = h->next)
301 if(h->proto == host->proto &&
302 h->port == host->port &&
303 strcmp(h->hostname, host->hostname) == 0) {
304 _krb5_free_krbhst_info(host);
305 return;
307 *kd->end = host;
308 kd->end = &host->next;
311 static krb5_error_code
312 append_host_string(krb5_context context, struct krb5_krbhst_data *kd,
313 const char *host, int def_port, int port)
315 struct krb5_krbhst_info *hi;
317 hi = parse_hostspec(context, kd, host, def_port, port);
318 if(hi == NULL)
319 return krb5_enomem(context);
321 append_host_hostinfo(kd, hi);
322 return 0;
326 * return a readable representation of `host' in `hostname, hostlen'
329 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
330 krb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host,
331 char *hostname, size_t hostlen)
333 const char *proto = "";
334 char portstr[7] = "";
335 if(host->proto == KRB5_KRBHST_TCP)
336 proto = "tcp/";
337 else if(host->proto == KRB5_KRBHST_HTTP)
338 proto = "http://";
339 if(host->port != host->def_port)
340 snprintf(portstr, sizeof(portstr), ":%d", host->port);
341 snprintf(hostname, hostlen, "%s%s%s", proto, host->hostname, portstr);
342 return 0;
346 * create a getaddrinfo `hints' based on `proto'
349 static void
350 make_hints(struct addrinfo *hints, int proto)
352 memset(hints, 0, sizeof(*hints));
353 hints->ai_family = AF_UNSPEC;
354 switch(proto) {
355 case KRB5_KRBHST_UDP :
356 hints->ai_socktype = SOCK_DGRAM;
357 break;
358 case KRB5_KRBHST_HTTP :
359 case KRB5_KRBHST_TCP :
360 hints->ai_socktype = SOCK_STREAM;
361 break;
366 * Return an `struct addrinfo *' for a KDC host.
368 * Returns an the struct addrinfo in in that corresponds to the
369 * information in `host'. free:ing is handled by krb5_krbhst_free, so
370 * the returned ai must not be released.
372 * @ingroup krb5
375 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
376 krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host,
377 struct addrinfo **ai)
379 int ret = 0;
381 if (host->ai == NULL) {
382 struct addrinfo hints;
383 char portstr[NI_MAXSERV];
384 char *hostname = host->hostname;
386 snprintf (portstr, sizeof(portstr), "%d", host->port);
387 make_hints(&hints, host->proto);
390 * First try this as an IP address, this allows us to add a
391 * dot at the end to stop using the search domains.
394 hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
396 ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai);
397 if (ret == 0)
398 goto out;
401 * If the hostname contains a dot, assumes it's a FQDN and
402 * don't use search domains since that might be painfully slow
403 * when machine is disconnected from that network.
406 hints.ai_flags &= ~(AI_NUMERICHOST);
408 if (strchr(hostname, '.') && hostname[strlen(hostname) - 1] != '.') {
409 ret = asprintf(&hostname, "%s.", host->hostname);
410 if (ret < 0 || hostname == NULL)
411 return ENOMEM;
414 ret = getaddrinfo(hostname, portstr, &hints, &host->ai);
415 if (hostname != host->hostname)
416 free(hostname);
417 if (ret) {
418 ret = krb5_eai_to_heim_errno(ret, errno);
419 goto out;
422 out:
423 *ai = host->ai;
424 return ret;
427 static krb5_boolean
428 get_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host)
430 struct krb5_krbhst_info *hi = *kd->index;
431 if(hi != NULL) {
432 *host = hi;
433 kd->index = &(*kd->index)->next;
434 return TRUE;
436 return FALSE;
439 static void
440 srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
441 const char *proto, const char *service)
443 krb5_error_code ret;
444 krb5_krbhst_info **res;
445 int count, i;
447 if (krb5_realm_is_lkdc(kd->realm))
448 return;
450 ret = srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service,
451 kd->port);
452 _krb5_debug(context, 2, "searching DNS for realm %s %s.%s -> %d",
453 kd->realm, proto, service, ret);
454 if (ret)
455 return;
456 for(i = 0; i < count; i++)
457 append_host_hostinfo(kd, res[i]);
458 free(res);
462 * read the configuration for `conf_string', defaulting to kd->def_port and
463 * forcing it to `kd->port' if kd->port != 0
466 static void
467 config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
468 const char *conf_string)
470 int i;
471 char **hostlist;
472 hostlist = krb5_config_get_strings(context, NULL,
473 "realms", kd->realm, conf_string, NULL);
475 _krb5_debug(context, 2, "configuration file for realm %s%s found",
476 kd->realm, hostlist ? "" : " not");
478 if(hostlist == NULL)
479 return;
480 kd->flags |= KD_CONFIG_EXISTS;
481 for(i = 0; hostlist && hostlist[i] != NULL; i++)
482 append_host_string(context, kd, hostlist[i], kd->def_port, kd->port);
484 krb5_config_free_strings(hostlist);
488 * as a fallback, look for `serv_string.kd->realm' (typically
489 * kerberos.REALM, kerberos-1.REALM, ...
490 * `port' is the default port for the service, and `proto' the
491 * protocol
494 static krb5_error_code
495 fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
496 const char *serv_string, int port, int proto)
498 char *host = NULL;
499 int ret;
500 struct addrinfo *ai;
501 struct addrinfo hints;
502 char portstr[NI_MAXSERV];
504 ret = krb5_config_get_bool_default(context, NULL, KRB5_FALLBACK_DEFAULT,
505 "libdefaults", "use_fallback", NULL);
506 if (!ret) {
507 kd->flags |= KD_FALLBACK;
508 return 0;
511 _krb5_debug(context, 2, "fallback lookup %d for realm %s (service %s)",
512 kd->fallback_count, kd->realm, serv_string);
515 * Don't try forever in case the DNS server keep returning us
516 * entries (like wildcard entries or the .nu TLD)
518 * Also don't try LKDC realms since fallback wont work on them at all.
520 if(kd->fallback_count >= 5 || krb5_realm_is_lkdc(kd->realm)) {
521 kd->flags |= KD_FALLBACK;
522 return 0;
525 if(kd->fallback_count == 0)
526 ret = asprintf(&host, "%s.%s.", serv_string, kd->realm);
527 else
528 ret = asprintf(&host, "%s-%d.%s.",
529 serv_string, kd->fallback_count, kd->realm);
531 if (ret < 0 || host == NULL)
532 return ENOMEM;
534 make_hints(&hints, proto);
535 snprintf(portstr, sizeof(portstr), "%d", port);
536 ret = getaddrinfo(host, portstr, &hints, &ai);
537 if (ret) {
538 /* no more hosts, so we're done here */
539 free(host);
540 kd->flags |= KD_FALLBACK;
541 } else {
542 struct krb5_krbhst_info *hi;
543 size_t hostlen = strlen(host);
545 hi = calloc(1, sizeof(*hi) + hostlen);
546 if(hi == NULL) {
547 free(host);
548 return ENOMEM;
551 hi->proto = proto;
552 hi->port = hi->def_port = port;
553 hi->ai = ai;
554 memmove(hi->hostname, host, hostlen);
555 hi->hostname[hostlen] = '\0';
556 free(host);
557 append_host_hostinfo(kd, hi);
558 kd->fallback_count++;
560 return 0;
564 * Fetch hosts from plugin
567 static krb5_error_code
568 add_plugin_host(struct krb5_krbhst_data *kd,
569 const char *host,
570 const char *port,
571 int portnum,
572 int proto)
574 struct krb5_krbhst_info *hi;
575 struct addrinfo hints, *ai;
576 size_t hostlen;
577 int ret;
579 make_hints(&hints, proto);
580 ret = getaddrinfo(host, port, &hints, &ai);
581 if (ret)
582 return 0;
584 hostlen = strlen(host);
586 hi = calloc(1, sizeof(*hi) + hostlen);
587 if(hi == NULL)
588 return ENOMEM;
590 hi->proto = proto;
591 hi->port = hi->def_port = portnum;
592 hi->ai = ai;
593 memmove(hi->hostname, host, hostlen);
594 hi->hostname[hostlen] = '\0';
595 append_host_hostinfo(kd, hi);
597 return 0;
600 static krb5_error_code
601 add_locate(void *ctx, int type, struct sockaddr *addr)
603 struct krb5_krbhst_data *kd = ctx;
604 char host[NI_MAXHOST], port[NI_MAXSERV];
605 socklen_t socklen;
606 krb5_error_code ret;
607 int proto, portnum;
609 socklen = socket_sockaddr_size(addr);
610 portnum = socket_get_port(addr);
612 ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port),
613 NI_NUMERICHOST|NI_NUMERICSERV);
614 if (ret != 0)
615 return 0;
617 if (kd->port)
618 snprintf(port, sizeof(port), "%d", kd->port);
619 else if (atoi(port) == 0)
620 snprintf(port, sizeof(port), "%d", krbhst_get_default_port(kd));
622 proto = krbhst_get_default_proto(kd);
624 ret = add_plugin_host(kd, host, port, portnum, proto);
625 if (ret)
626 return ret;
629 * This is really kind of broken and should be solved a different
630 * way, some sites block UDP, and we don't, in the general case,
631 * fall back to TCP, that should also be done. But since that
632 * should require us to invert the whole "find kdc" stack, let put
633 * this in for now.
636 if (proto == KRB5_KRBHST_UDP) {
637 ret = add_plugin_host(kd, host, port, portnum, KRB5_KRBHST_TCP);
638 if (ret)
639 return ret;
642 return 0;
645 struct plctx {
646 enum locate_service_type type;
647 struct krb5_krbhst_data *kd;
648 unsigned long flags;
651 static KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
652 plcallback(krb5_context context,
653 const void *plug, void *plugctx, void *userctx)
655 const krb5plugin_service_locate_ftable *locate = plug;
656 struct plctx *plctx = userctx;
658 if (locate->minor_version >= KRB5_PLUGIN_LOCATE_VERSION_2)
659 return locate->lookup(plugctx, plctx->flags, plctx->type, plctx->kd->realm, 0, 0, add_locate, plctx->kd);
661 if (plctx->flags & KRB5_PLF_ALLOW_HOMEDIR)
662 return locate->old_lookup(plugctx, plctx->type, plctx->kd->realm, 0, 0, add_locate, plctx->kd);
664 return KRB5_PLUGIN_NO_HANDLE;
667 static void
668 plugin_get_hosts(krb5_context context,
669 struct krb5_krbhst_data *kd,
670 enum locate_service_type type)
672 struct plctx ctx = { type, kd, 0 };
674 if (_krb5_homedir_access(context))
675 ctx.flags |= KRB5_PLF_ALLOW_HOMEDIR;
677 _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_LOCATE,
678 KRB5_PLUGIN_LOCATE_VERSION_0,
679 0, &ctx, plcallback);
686 static void
687 hostnames_get_hosts(krb5_context context,
688 struct krb5_krbhst_data *kd,
689 const char *type)
691 kd->flags |= KD_HOSTNAMES;
692 if (kd->hostname)
693 append_host_string(context, kd, kd->hostname, kd->def_port, kd->port);
701 static krb5_error_code
702 kdc_get_next(krb5_context context,
703 struct krb5_krbhst_data *kd,
704 krb5_krbhst_info **host)
706 krb5_error_code ret;
708 if ((kd->flags & KD_HOSTNAMES) == 0) {
709 hostnames_get_hosts(context, kd, "kdc");
710 if(get_next(kd, host))
711 return 0;
714 if ((kd->flags & KD_PLUGIN) == 0) {
715 plugin_get_hosts(context, kd, locate_service_kdc);
716 kd->flags |= KD_PLUGIN;
717 if(get_next(kd, host))
718 return 0;
721 if((kd->flags & KD_CONFIG) == 0) {
722 config_get_hosts(context, kd, "kdc");
723 kd->flags |= KD_CONFIG;
724 if(get_next(kd, host))
725 return 0;
728 if (kd->flags & KD_CONFIG_EXISTS) {
729 _krb5_debug(context, 1,
730 "Configuration exists for realm %s, wont go to DNS",
731 kd->realm);
732 return KRB5_KDC_UNREACH;
735 if(context->srv_lookup) {
736 if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) {
737 srv_get_hosts(context, kd, "udp", "kerberos");
738 kd->flags |= KD_SRV_UDP;
739 if(get_next(kd, host))
740 return 0;
743 if((kd->flags & KD_SRV_TCP) == 0) {
744 srv_get_hosts(context, kd, "tcp", "kerberos");
745 kd->flags |= KD_SRV_TCP;
746 if(get_next(kd, host))
747 return 0;
749 if((kd->flags & KD_SRV_HTTP) == 0) {
750 srv_get_hosts(context, kd, "http", "kerberos");
751 kd->flags |= KD_SRV_HTTP;
752 if(get_next(kd, host))
753 return 0;
757 while((kd->flags & KD_FALLBACK) == 0) {
758 ret = fallback_get_hosts(context, kd, "kerberos",
759 kd->def_port,
760 krbhst_get_default_proto(kd));
761 if(ret)
762 return ret;
763 if(get_next(kd, host))
764 return 0;
767 _krb5_debug(context, 0, "No KDC entries found for %s", kd->realm);
769 return KRB5_KDC_UNREACH; /* XXX */
772 static krb5_error_code
773 admin_get_next(krb5_context context,
774 struct krb5_krbhst_data *kd,
775 krb5_krbhst_info **host)
777 krb5_error_code ret;
779 if ((kd->flags & KD_PLUGIN) == 0) {
780 plugin_get_hosts(context, kd, locate_service_kadmin);
781 kd->flags |= KD_PLUGIN;
782 if(get_next(kd, host))
783 return 0;
786 if((kd->flags & KD_CONFIG) == 0) {
787 config_get_hosts(context, kd, "admin_server");
788 kd->flags |= KD_CONFIG;
789 if(get_next(kd, host))
790 return 0;
793 if (kd->flags & KD_CONFIG_EXISTS) {
794 _krb5_debug(context, 1,
795 "Configuration exists for realm %s, wont go to DNS",
796 kd->realm);
797 return KRB5_KDC_UNREACH;
800 if(context->srv_lookup) {
801 if((kd->flags & KD_SRV_TCP) == 0) {
802 srv_get_hosts(context, kd, "tcp", "kerberos-adm");
803 kd->flags |= KD_SRV_TCP;
804 if(get_next(kd, host))
805 return 0;
809 if (krbhst_empty(kd)
810 && (kd->flags & KD_FALLBACK) == 0) {
811 ret = fallback_get_hosts(context, kd, "kerberos",
812 kd->def_port,
813 krbhst_get_default_proto(kd));
814 if(ret)
815 return ret;
816 kd->flags |= KD_FALLBACK;
817 if(get_next(kd, host))
818 return 0;
821 _krb5_debug(context, 0, "No admin entries found for realm %s", kd->realm);
823 return KRB5_KDC_UNREACH; /* XXX */
826 static krb5_error_code
827 kpasswd_get_next(krb5_context context,
828 struct krb5_krbhst_data *kd,
829 krb5_krbhst_info **host)
831 krb5_error_code ret;
833 if ((kd->flags & KD_PLUGIN) == 0) {
834 plugin_get_hosts(context, kd, locate_service_kpasswd);
835 kd->flags |= KD_PLUGIN;
836 if(get_next(kd, host))
837 return 0;
840 if((kd->flags & KD_CONFIG) == 0) {
841 config_get_hosts(context, kd, "kpasswd_server");
842 kd->flags |= KD_CONFIG;
843 if(get_next(kd, host))
844 return 0;
847 if (kd->flags & KD_CONFIG_EXISTS) {
848 _krb5_debug(context, 1,
849 "Configuration exists for realm %s, wont go to DNS",
850 kd->realm);
851 return KRB5_KDC_UNREACH;
854 if(context->srv_lookup) {
855 if((kd->flags & KD_SRV_UDP) == 0) {
856 srv_get_hosts(context, kd, "udp", "kpasswd");
857 kd->flags |= KD_SRV_UDP;
858 if(get_next(kd, host))
859 return 0;
861 if((kd->flags & KD_SRV_TCP) == 0) {
862 srv_get_hosts(context, kd, "tcp", "kpasswd");
863 kd->flags |= KD_SRV_TCP;
864 if(get_next(kd, host))
865 return 0;
869 /* no matches -> try admin */
871 if (krbhst_empty(kd)) {
872 kd->flags = 0;
873 kd->port = kd->def_port;
874 kd->get_next = admin_get_next;
875 ret = (*kd->get_next)(context, kd, host);
876 if (ret == 0)
877 (*host)->proto = krbhst_get_default_proto(kd);
878 return ret;
881 _krb5_debug(context, 0, "No kpasswd entries found for realm %s", kd->realm);
883 return KRB5_KDC_UNREACH;
886 static void
887 krbhost_dealloc(void *ptr)
889 struct krb5_krbhst_data *handle = (struct krb5_krbhst_data *)ptr;
890 krb5_krbhst_info *h, *next;
892 for (h = handle->hosts; h != NULL; h = next) {
893 next = h->next;
894 _krb5_free_krbhst_info(h);
896 if (handle->hostname)
897 free(handle->hostname);
899 free(handle->realm);
902 static struct krb5_krbhst_data*
903 common_init(krb5_context context,
904 const char *service,
905 const char *realm,
906 int flags)
908 struct krb5_krbhst_data *kd;
910 if ((kd = heim_alloc(sizeof(*kd), "krbhst-context", krbhost_dealloc)) == NULL)
911 return NULL;
913 if((kd->realm = strdup(realm)) == NULL) {
914 heim_release(kd);
915 return NULL;
918 _krb5_debug(context, 2, "Trying to find service %s for realm %s flags %x",
919 service, realm, flags);
921 /* For 'realms' without a . do not even think of going to DNS */
922 if (!strchr(realm, '.'))
923 kd->flags |= KD_CONFIG_EXISTS;
925 if (flags & KRB5_KRBHST_FLAGS_LARGE_MSG)
926 kd->flags |= KD_LARGE_MSG;
927 kd->end = kd->index = &kd->hosts;
928 return kd;
932 * initialize `handle' to look for hosts of type `type' in realm `realm'
935 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
936 krb5_krbhst_init(krb5_context context,
937 const char *realm,
938 unsigned int type,
939 krb5_krbhst_handle *handle)
941 return krb5_krbhst_init_flags(context, realm, type, 0, handle);
944 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
945 krb5_krbhst_init_flags(krb5_context context,
946 const char *realm,
947 unsigned int type,
948 int flags,
949 krb5_krbhst_handle *handle)
951 struct krb5_krbhst_data *kd;
952 krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *,
953 krb5_krbhst_info **);
954 int def_port;
955 const char *service;
957 *handle = NULL;
959 switch(type) {
960 case KRB5_KRBHST_KDC:
961 next = kdc_get_next;
962 def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88));
963 service = "kdc";
964 break;
965 case KRB5_KRBHST_ADMIN:
966 next = admin_get_next;
967 def_port = ntohs(krb5_getportbyname (context, "kerberos-adm",
968 "tcp", 749));
969 service = "admin";
970 break;
971 case KRB5_KRBHST_CHANGEPW:
972 next = kpasswd_get_next;
973 def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp",
974 KPASSWD_PORT));
975 service = "change_password";
976 break;
977 default:
978 krb5_set_error_message(context, ENOTTY,
979 N_("unknown krbhst type (%u)", ""), type);
980 return ENOTTY;
982 if((kd = common_init(context, service, realm, flags)) == NULL)
983 return ENOMEM;
984 kd->get_next = next;
985 kd->def_port = def_port;
986 *handle = kd;
987 return 0;
991 * return the next host information from `handle' in `host'
994 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
995 krb5_krbhst_next(krb5_context context,
996 krb5_krbhst_handle handle,
997 krb5_krbhst_info **host)
999 if(get_next(handle, host))
1000 return 0;
1002 return (*handle->get_next)(context, handle, host);
1006 * return the next host information from `handle' as a host name
1007 * in `hostname' (or length `hostlen)
1010 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1011 krb5_krbhst_next_as_string(krb5_context context,
1012 krb5_krbhst_handle handle,
1013 char *hostname,
1014 size_t hostlen)
1016 krb5_error_code ret;
1017 krb5_krbhst_info *host;
1018 ret = krb5_krbhst_next(context, handle, &host);
1019 if(ret)
1020 return ret;
1021 return krb5_krbhst_format_string(context, host, hostname, hostlen);
1028 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1029 krb5_krbhst_set_hostname(krb5_context context,
1030 krb5_krbhst_handle handle,
1031 const char *hostname)
1033 if (handle->hostname)
1034 free(handle->hostname);
1035 handle->hostname = strdup(hostname);
1036 if (handle->hostname == NULL)
1037 return ENOMEM;
1038 return 0;
1041 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1042 krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle)
1044 handle->index = &handle->hosts;
1047 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1048 krb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle)
1050 heim_release(handle);
1053 #ifndef HEIMDAL_SMALLER
1055 /* backwards compatibility ahead */
1057 static krb5_error_code
1058 gethostlist(krb5_context context, const char *realm,
1059 unsigned int type, char ***hostlist)
1061 krb5_error_code ret;
1062 int nhost = 0;
1063 krb5_krbhst_handle handle;
1064 char host[MAXHOSTNAMELEN];
1065 krb5_krbhst_info *hostinfo;
1067 ret = krb5_krbhst_init(context, realm, type, &handle);
1068 if (ret)
1069 return ret;
1071 while(krb5_krbhst_next(context, handle, &hostinfo) == 0)
1072 nhost++;
1073 if(nhost == 0) {
1074 krb5_set_error_message(context, KRB5_KDC_UNREACH,
1075 N_("No KDC found for realm %s", ""), realm);
1076 return KRB5_KDC_UNREACH;
1078 *hostlist = calloc(nhost + 1, sizeof(**hostlist));
1079 if(*hostlist == NULL) {
1080 krb5_krbhst_free(context, handle);
1081 return krb5_enomem(context);
1084 krb5_krbhst_reset(context, handle);
1085 nhost = 0;
1086 while(krb5_krbhst_next_as_string(context, handle,
1087 host, sizeof(host)) == 0) {
1088 if(((*hostlist)[nhost++] = strdup(host)) == NULL) {
1089 krb5_free_krbhst(context, *hostlist);
1090 krb5_krbhst_free(context, handle);
1091 return krb5_enomem(context);
1094 (*hostlist)[nhost] = NULL;
1095 krb5_krbhst_free(context, handle);
1096 return 0;
1100 * return an malloced list of kadmin-hosts for `realm' in `hostlist'
1103 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1104 krb5_get_krb_admin_hst (krb5_context context,
1105 const krb5_realm *realm,
1106 char ***hostlist)
1108 return gethostlist(context, *realm, KRB5_KRBHST_ADMIN, hostlist);
1112 * return an malloced list of changepw-hosts for `realm' in `hostlist'
1115 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1116 krb5_get_krb_changepw_hst (krb5_context context,
1117 const krb5_realm *realm,
1118 char ***hostlist)
1120 return gethostlist(context, *realm, KRB5_KRBHST_CHANGEPW, hostlist);
1124 * return an malloced list of 524-hosts for `realm' in `hostlist'
1127 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1128 krb5_get_krb524hst (krb5_context context,
1129 const krb5_realm *realm,
1130 char ***hostlist)
1132 return gethostlist(context, *realm, KRB5_KRBHST_KRB524, hostlist);
1136 * return an malloced list of KDC's for `realm' in `hostlist'
1139 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1140 krb5_get_krbhst (krb5_context context,
1141 const krb5_realm *realm,
1142 char ***hostlist)
1144 return gethostlist(context, *realm, KRB5_KRBHST_KDC, hostlist);
1148 * free all the memory allocated in `hostlist'
1151 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1152 krb5_free_krbhst (krb5_context context,
1153 char **hostlist)
1155 char **p;
1157 for (p = hostlist; *p; ++p)
1158 free (*p);
1159 free (hostlist);
1160 return 0;
1163 #endif /* HEIMDAL_SMALLER */