• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.5.8/source4/heimdal/lib/krb5/
1/*
2 * Copyright (c) 2001 - 2003 Kungliga Tekniska H��gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "krb5_locl.h"
35#include <resolve.h>
36#include "locate_plugin.h"
37
38static int
39string_to_proto(const char *string)
40{
41    if(strcasecmp(string, "udp") == 0)
42	return KRB5_KRBHST_UDP;
43    else if(strcasecmp(string, "tcp") == 0)
44	return KRB5_KRBHST_TCP;
45    else if(strcasecmp(string, "http") == 0)
46	return KRB5_KRBHST_HTTP;
47    return -1;
48}
49
50/*
51 * set `res' and `count' to the result of looking up SRV RR in DNS for
52 * `proto', `proto', `realm' using `dns_type'.
53 * if `port' != 0, force that port number
54 */
55
56static krb5_error_code
57srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
58	       const char *realm, const char *dns_type,
59	       const char *proto, const char *service, int port)
60{
61    char domain[1024];
62    struct rk_dns_reply *r;
63    struct rk_resource_record *rr;
64    int num_srv;
65    int proto_num;
66    int def_port;
67
68    *res = NULL;
69    *count = 0;
70
71    proto_num = string_to_proto(proto);
72    if(proto_num < 0) {
73	krb5_set_error_message(context, EINVAL,
74			       N_("unknown protocol `%s' to lookup", ""),
75			       proto);
76	return EINVAL;
77    }
78
79    if(proto_num == KRB5_KRBHST_HTTP)
80	def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
81    else if(port == 0)
82	def_port = ntohs(krb5_getportbyname (context, service, proto, 88));
83    else
84	def_port = port;
85
86    snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm);
87
88    r = rk_dns_lookup(domain, dns_type);
89    if(r == NULL)
90	return KRB5_KDC_UNREACH;
91
92    for(num_srv = 0, rr = r->head; rr; rr = rr->next)
93	if(rr->type == rk_ns_t_srv)
94	    num_srv++;
95
96    *res = malloc(num_srv * sizeof(**res));
97    if(*res == NULL) {
98	rk_dns_free_data(r);
99	krb5_set_error_message(context, ENOMEM,
100			       N_("malloc: out of memory", ""));
101	return ENOMEM;
102    }
103
104    rk_dns_srv_order(r);
105
106    for(num_srv = 0, rr = r->head; rr; rr = rr->next)
107	if(rr->type == rk_ns_t_srv) {
108	    krb5_krbhst_info *hi;
109	    size_t len = strlen(rr->u.srv->target);
110
111	    hi = calloc(1, sizeof(*hi) + len);
112	    if(hi == NULL) {
113		rk_dns_free_data(r);
114		while(--num_srv >= 0)
115		    free((*res)[num_srv]);
116		free(*res);
117		*res = NULL;
118		return ENOMEM;
119	    }
120	    (*res)[num_srv++] = hi;
121
122	    hi->proto = proto_num;
123
124	    hi->def_port = def_port;
125	    if (port != 0)
126		hi->port = port;
127	    else
128		hi->port = rr->u.srv->port;
129
130	    strlcpy(hi->hostname, rr->u.srv->target, len + 1);
131	}
132
133    *count = num_srv;
134
135    rk_dns_free_data(r);
136    return 0;
137}
138
139
140struct krb5_krbhst_data {
141    char *realm;
142    unsigned int flags;
143    int def_port;
144    int port;			/* hardwired port number if != 0 */
145#define KD_CONFIG		 1
146#define KD_SRV_UDP		 2
147#define KD_SRV_TCP		 4
148#define KD_SRV_HTTP		 8
149#define KD_FALLBACK		16
150#define KD_CONFIG_EXISTS	32
151#define KD_LARGE_MSG		64
152#define KD_PLUGIN	       128
153    krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *,
154				krb5_krbhst_info**);
155
156    unsigned int fallback_count;
157
158    struct krb5_krbhst_info *hosts, **index, **end;
159};
160
161static krb5_boolean
162krbhst_empty(const struct krb5_krbhst_data *kd)
163{
164    return kd->index == &kd->hosts;
165}
166
167/*
168 * Return the default protocol for the `kd' (either TCP or UDP)
169 */
170
171static int
172krbhst_get_default_proto(struct krb5_krbhst_data *kd)
173{
174    if (kd->flags & KD_LARGE_MSG)
175	return KRB5_KRBHST_TCP;
176    return KRB5_KRBHST_UDP;
177}
178
179
180/*
181 * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port'
182 * and forcing it to `port' if port != 0
183 */
184
185static struct krb5_krbhst_info*
186parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
187	       const char *spec, int def_port, int port)
188{
189    const char *p = spec;
190    struct krb5_krbhst_info *hi;
191
192    hi = calloc(1, sizeof(*hi) + strlen(spec));
193    if(hi == NULL)
194	return NULL;
195
196    hi->proto = krbhst_get_default_proto(kd);
197
198    if(strncmp(p, "http://", 7) == 0){
199	hi->proto = KRB5_KRBHST_HTTP;
200	p += 7;
201    } else if(strncmp(p, "http/", 5) == 0) {
202	hi->proto = KRB5_KRBHST_HTTP;
203	p += 5;
204	def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
205    }else if(strncmp(p, "tcp/", 4) == 0){
206	hi->proto = KRB5_KRBHST_TCP;
207	p += 4;
208    } else if(strncmp(p, "udp/", 4) == 0) {
209	p += 4;
210    }
211
212    if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) {
213	free(hi);
214	return NULL;
215    }
216    /* get rid of trailing /, and convert to lower case */
217    hi->hostname[strcspn(hi->hostname, "/")] = '\0';
218    strlwr(hi->hostname);
219
220    hi->port = hi->def_port = def_port;
221    if(p != NULL) {
222	char *end;
223	hi->port = strtol(p, &end, 0);
224	if(end == p) {
225	    free(hi);
226	    return NULL;
227	}
228    }
229    if (port)
230	hi->port = port;
231    return hi;
232}
233
234void
235_krb5_free_krbhst_info(krb5_krbhst_info *hi)
236{
237    if (hi->ai != NULL)
238	freeaddrinfo(hi->ai);
239    free(hi);
240}
241
242krb5_error_code
243_krb5_krbhost_info_move(krb5_context context,
244			krb5_krbhst_info *from,
245			krb5_krbhst_info **to)
246{
247    size_t hostnamelen = strlen(from->hostname);
248    /* trailing NUL is included in structure */
249    *to = calloc(1, sizeof(**to) + hostnamelen);
250    if(*to == NULL) {
251	krb5_set_error_message(context, ENOMEM,
252			       N_("malloc: out of memory", ""));
253	return ENOMEM;
254    }
255
256    (*to)->proto = from->proto;
257    (*to)->port = from->port;
258    (*to)->def_port = from->def_port;
259    (*to)->ai = from->ai;
260    from->ai = NULL;
261    (*to)->next = NULL;
262    memcpy((*to)->hostname, from->hostname, hostnamelen + 1);
263    return 0;
264}
265
266
267static void
268append_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host)
269{
270    struct krb5_krbhst_info *h;
271
272    for(h = kd->hosts; h; h = h->next)
273	if(h->proto == host->proto &&
274	   h->port == host->port &&
275	   strcmp(h->hostname, host->hostname) == 0) {
276	    _krb5_free_krbhst_info(host);
277	    return;
278	}
279    *kd->end = host;
280    kd->end = &host->next;
281}
282
283static krb5_error_code
284append_host_string(krb5_context context, struct krb5_krbhst_data *kd,
285		   const char *host, int def_port, int port)
286{
287    struct krb5_krbhst_info *hi;
288
289    hi = parse_hostspec(context, kd, host, def_port, port);
290    if(hi == NULL)
291	return ENOMEM;
292
293    append_host_hostinfo(kd, hi);
294    return 0;
295}
296
297/*
298 * return a readable representation of `host' in `hostname, hostlen'
299 */
300
301krb5_error_code KRB5_LIB_FUNCTION
302krb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host,
303			  char *hostname, size_t hostlen)
304{
305    const char *proto = "";
306    char portstr[7] = "";
307    if(host->proto == KRB5_KRBHST_TCP)
308	proto = "tcp/";
309    else if(host->proto == KRB5_KRBHST_HTTP)
310	proto = "http://";
311    if(host->port != host->def_port)
312	snprintf(portstr, sizeof(portstr), ":%d", host->port);
313    snprintf(hostname, hostlen, "%s%s%s", proto, host->hostname, portstr);
314    return 0;
315}
316
317/*
318 * create a getaddrinfo `hints' based on `proto'
319 */
320
321static void
322make_hints(struct addrinfo *hints, int proto)
323{
324    memset(hints, 0, sizeof(*hints));
325    hints->ai_family = AF_UNSPEC;
326    switch(proto) {
327    case KRB5_KRBHST_UDP :
328	hints->ai_socktype = SOCK_DGRAM;
329	break;
330    case KRB5_KRBHST_HTTP :
331    case KRB5_KRBHST_TCP :
332	hints->ai_socktype = SOCK_STREAM;
333	break;
334    }
335}
336
337/*
338 * return an `struct addrinfo *' in `ai' corresponding to the information
339 * in `host'.  free:ing is handled by krb5_krbhst_free.
340 */
341
342krb5_error_code KRB5_LIB_FUNCTION
343krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host,
344			 struct addrinfo **ai)
345{
346    struct addrinfo hints;
347    char portstr[NI_MAXSERV];
348    int ret;
349
350    if (host->ai == NULL) {
351	make_hints(&hints, host->proto);
352	snprintf (portstr, sizeof(portstr), "%d", host->port);
353	ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai);
354	if (ret)
355	    return krb5_eai_to_heim_errno(ret, errno);
356    }
357    *ai = host->ai;
358    return 0;
359}
360
361static krb5_boolean
362get_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host)
363{
364    struct krb5_krbhst_info *hi = *kd->index;
365    if(hi != NULL) {
366	*host = hi;
367	kd->index = &(*kd->index)->next;
368	return TRUE;
369    }
370    return FALSE;
371}
372
373static void
374srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
375	      const char *proto, const char *service)
376{
377    krb5_krbhst_info **res;
378    int count, i;
379
380    if (srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service,
381		       kd->port))
382	return;
383    for(i = 0; i < count; i++)
384	append_host_hostinfo(kd, res[i]);
385    free(res);
386}
387
388/*
389 * read the configuration for `conf_string', defaulting to kd->def_port and
390 * forcing it to `kd->port' if kd->port != 0
391 */
392
393static void
394config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
395		 const char *conf_string)
396{
397    int i;
398
399    char **hostlist;
400    hostlist = krb5_config_get_strings(context, NULL,
401				       "realms", kd->realm, conf_string, NULL);
402
403    if(hostlist == NULL)
404	return;
405    kd->flags |= KD_CONFIG_EXISTS;
406    for(i = 0; hostlist && hostlist[i] != NULL; i++)
407	append_host_string(context, kd, hostlist[i], kd->def_port, kd->port);
408
409    krb5_config_free_strings(hostlist);
410}
411
412/*
413 * as a fallback, look for `serv_string.kd->realm' (typically
414 * kerberos.REALM, kerberos-1.REALM, ...
415 * `port' is the default port for the service, and `proto' the
416 * protocol
417 */
418
419static krb5_error_code
420fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
421		   const char *serv_string, int port, int proto)
422{
423    char *host;
424    int ret;
425    struct addrinfo *ai;
426    struct addrinfo hints;
427    char portstr[NI_MAXSERV];
428
429    /*
430     * Don't try forever in case the DNS server keep returning us
431     * entries (like wildcard entries or the .nu TLD)
432     */
433    if(kd->fallback_count >= 5) {
434	kd->flags |= KD_FALLBACK;
435	return 0;
436    }
437
438    if(kd->fallback_count == 0)
439	asprintf(&host, "%s.%s.", serv_string, kd->realm);
440    else
441	asprintf(&host, "%s-%d.%s.",
442		 serv_string, kd->fallback_count, kd->realm);
443
444    if (host == NULL)
445	return ENOMEM;
446
447    make_hints(&hints, proto);
448    snprintf(portstr, sizeof(portstr), "%d", port);
449    ret = getaddrinfo(host, portstr, &hints, &ai);
450    if (ret) {
451	/* no more hosts, so we're done here */
452	free(host);
453	kd->flags |= KD_FALLBACK;
454    } else {
455	struct krb5_krbhst_info *hi;
456	size_t hostlen = strlen(host);
457
458	hi = calloc(1, sizeof(*hi) + hostlen);
459	if(hi == NULL) {
460	    free(host);
461	    return ENOMEM;
462	}
463
464	hi->proto = proto;
465	hi->port  = hi->def_port = port;
466	hi->ai    = ai;
467	memmove(hi->hostname, host, hostlen);
468	hi->hostname[hostlen] = '\0';
469	free(host);
470	append_host_hostinfo(kd, hi);
471	kd->fallback_count++;
472    }
473    return 0;
474}
475
476/*
477 * Fetch hosts from plugin
478 */
479
480static krb5_error_code
481add_locate(void *ctx, int type, struct sockaddr *addr)
482{
483    struct krb5_krbhst_info *hi;
484    struct krb5_krbhst_data *kd = ctx;
485    char host[NI_MAXHOST], port[NI_MAXSERV];
486    struct addrinfo hints, *ai;
487    socklen_t socklen;
488    size_t hostlen;
489    int ret;
490
491    socklen = socket_sockaddr_size(addr);
492
493    ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port),
494		      NI_NUMERICHOST|NI_NUMERICSERV);
495    if (ret != 0)
496	return 0;
497
498    make_hints(&hints, krbhst_get_default_proto(kd));
499    ret = getaddrinfo(host, port, &hints, &ai);
500    if (ret)
501	return 0;
502
503    hostlen = strlen(host);
504
505    hi = calloc(1, sizeof(*hi) + hostlen);
506    if(hi == NULL)
507	return ENOMEM;
508
509    hi->proto = krbhst_get_default_proto(kd);
510    hi->port  = hi->def_port = socket_get_port(addr);
511    hi->ai    = ai;
512    memmove(hi->hostname, host, hostlen);
513    hi->hostname[hostlen] = '\0';
514    append_host_hostinfo(kd, hi);
515
516    return 0;
517}
518
519static void
520plugin_get_hosts(krb5_context context,
521		 struct krb5_krbhst_data *kd,
522		 enum locate_service_type type)
523{
524    struct krb5_plugin *list = NULL, *e;
525    krb5_error_code ret;
526
527    ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA,
528			    KRB5_PLUGIN_LOCATE, &list);
529    if(ret != 0 || list == NULL)
530	return;
531
532    for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
533	krb5plugin_service_locate_ftable *service;
534	void *ctx;
535
536	service = _krb5_plugin_get_symbol(e);
537	if (service->minor_version != 0)
538	    continue;
539
540	(*service->init)(context, &ctx);
541	ret = (*service->lookup)(ctx, type, kd->realm, 0, 0, add_locate, kd);
542	(*service->fini)(ctx);
543	if (ret && ret != KRB5_PLUGIN_NO_HANDLE) {
544	    krb5_set_error_message(context, ret,
545				   N_("Locate plugin failed to lookup realm %s: %d", ""),
546				   kd->realm, ret);
547	    break;
548	} else if (ret == 0)
549	    kd->flags |= KD_CONFIG_EXISTS;
550
551    }
552    _krb5_plugin_free(list);
553}
554
555/*
556 *
557 */
558
559static krb5_error_code
560kdc_get_next(krb5_context context,
561	     struct krb5_krbhst_data *kd,
562	     krb5_krbhst_info **host)
563{
564    krb5_error_code ret;
565
566    if ((kd->flags & KD_PLUGIN) == 0) {
567	plugin_get_hosts(context, kd, locate_service_kdc);
568	kd->flags |= KD_PLUGIN;
569	if(get_next(kd, host))
570	    return 0;
571    }
572
573    if((kd->flags & KD_CONFIG) == 0) {
574	config_get_hosts(context, kd, "kdc");
575	kd->flags |= KD_CONFIG;
576	if(get_next(kd, host))
577	    return 0;
578    }
579
580    if (kd->flags & KD_CONFIG_EXISTS)
581	return KRB5_KDC_UNREACH; /* XXX */
582
583    if(context->srv_lookup) {
584	if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) {
585	    srv_get_hosts(context, kd, "udp", "kerberos");
586	    kd->flags |= KD_SRV_UDP;
587	    if(get_next(kd, host))
588		return 0;
589	}
590
591	if((kd->flags & KD_SRV_TCP) == 0) {
592	    srv_get_hosts(context, kd, "tcp", "kerberos");
593	    kd->flags |= KD_SRV_TCP;
594	    if(get_next(kd, host))
595		return 0;
596	}
597	if((kd->flags & KD_SRV_HTTP) == 0) {
598	    srv_get_hosts(context, kd, "http", "kerberos");
599	    kd->flags |= KD_SRV_HTTP;
600	    if(get_next(kd, host))
601		return 0;
602	}
603    }
604
605    while((kd->flags & KD_FALLBACK) == 0) {
606	ret = fallback_get_hosts(context, kd, "kerberos",
607				 kd->def_port,
608				 krbhst_get_default_proto(kd));
609	if(ret)
610	    return ret;
611	if(get_next(kd, host))
612	    return 0;
613    }
614
615    return KRB5_KDC_UNREACH; /* XXX */
616}
617
618static krb5_error_code
619admin_get_next(krb5_context context,
620	       struct krb5_krbhst_data *kd,
621	       krb5_krbhst_info **host)
622{
623    krb5_error_code ret;
624
625    if ((kd->flags & KD_PLUGIN) == 0) {
626	plugin_get_hosts(context, kd, locate_service_kadmin);
627	kd->flags |= KD_PLUGIN;
628	if(get_next(kd, host))
629	    return 0;
630    }
631
632    if((kd->flags & KD_CONFIG) == 0) {
633	config_get_hosts(context, kd, "admin_server");
634	kd->flags |= KD_CONFIG;
635	if(get_next(kd, host))
636	    return 0;
637    }
638
639    if (kd->flags & KD_CONFIG_EXISTS)
640	return KRB5_KDC_UNREACH; /* XXX */
641
642    if(context->srv_lookup) {
643	if((kd->flags & KD_SRV_TCP) == 0) {
644	    srv_get_hosts(context, kd, "tcp", "kerberos-adm");
645	    kd->flags |= KD_SRV_TCP;
646	    if(get_next(kd, host))
647		return 0;
648	}
649    }
650
651    if (krbhst_empty(kd)
652	&& (kd->flags & KD_FALLBACK) == 0) {
653	ret = fallback_get_hosts(context, kd, "kerberos",
654				 kd->def_port,
655				 krbhst_get_default_proto(kd));
656	if(ret)
657	    return ret;
658	kd->flags |= KD_FALLBACK;
659	if(get_next(kd, host))
660	    return 0;
661    }
662
663    return KRB5_KDC_UNREACH;	/* XXX */
664}
665
666static krb5_error_code
667kpasswd_get_next(krb5_context context,
668		 struct krb5_krbhst_data *kd,
669		 krb5_krbhst_info **host)
670{
671    krb5_error_code ret;
672
673    if ((kd->flags & KD_PLUGIN) == 0) {
674	plugin_get_hosts(context, kd, locate_service_kpasswd);
675	kd->flags |= KD_PLUGIN;
676	if(get_next(kd, host))
677	    return 0;
678    }
679
680    if((kd->flags & KD_CONFIG) == 0) {
681	config_get_hosts(context, kd, "kpasswd_server");
682	kd->flags |= KD_CONFIG;
683	if(get_next(kd, host))
684	    return 0;
685    }
686
687    if (kd->flags & KD_CONFIG_EXISTS)
688	return KRB5_KDC_UNREACH; /* XXX */
689
690    if(context->srv_lookup) {
691	if((kd->flags & KD_SRV_UDP) == 0) {
692	    srv_get_hosts(context, kd, "udp", "kpasswd");
693	    kd->flags |= KD_SRV_UDP;
694	    if(get_next(kd, host))
695		return 0;
696	}
697	if((kd->flags & KD_SRV_TCP) == 0) {
698	    srv_get_hosts(context, kd, "tcp", "kpasswd");
699	    kd->flags |= KD_SRV_TCP;
700	    if(get_next(kd, host))
701		return 0;
702	}
703    }
704
705    /* no matches -> try admin */
706
707    if (krbhst_empty(kd)) {
708	kd->flags = 0;
709	kd->port  = kd->def_port;
710	kd->get_next = admin_get_next;
711	ret = (*kd->get_next)(context, kd, host);
712	if (ret == 0)
713	    (*host)->proto = krbhst_get_default_proto(kd);
714	return ret;
715    }
716
717    return KRB5_KDC_UNREACH; /* XXX */
718}
719
720static krb5_error_code
721krb524_get_next(krb5_context context,
722		struct krb5_krbhst_data *kd,
723		krb5_krbhst_info **host)
724{
725    if ((kd->flags & KD_PLUGIN) == 0) {
726	plugin_get_hosts(context, kd, locate_service_krb524);
727	kd->flags |= KD_PLUGIN;
728	if(get_next(kd, host))
729	    return 0;
730    }
731
732    if((kd->flags & KD_CONFIG) == 0) {
733	config_get_hosts(context, kd, "krb524_server");
734	if(get_next(kd, host))
735	    return 0;
736	kd->flags |= KD_CONFIG;
737    }
738
739    if (kd->flags & KD_CONFIG_EXISTS)
740	return KRB5_KDC_UNREACH; /* XXX */
741
742    if(context->srv_lookup) {
743	if((kd->flags & KD_SRV_UDP) == 0) {
744	    srv_get_hosts(context, kd, "udp", "krb524");
745	    kd->flags |= KD_SRV_UDP;
746	    if(get_next(kd, host))
747		return 0;
748	}
749
750	if((kd->flags & KD_SRV_TCP) == 0) {
751	    srv_get_hosts(context, kd, "tcp", "krb524");
752	    kd->flags |= KD_SRV_TCP;
753	    if(get_next(kd, host))
754		return 0;
755	}
756    }
757
758    /* no matches -> try kdc */
759
760    if (krbhst_empty(kd)) {
761	kd->flags = 0;
762	kd->port  = kd->def_port;
763	kd->get_next = kdc_get_next;
764	return (*kd->get_next)(context, kd, host);
765    }
766
767    return KRB5_KDC_UNREACH; /* XXX */
768}
769
770static struct krb5_krbhst_data*
771common_init(krb5_context context,
772	    const char *realm,
773	    int flags)
774{
775    struct krb5_krbhst_data *kd;
776
777    if((kd = calloc(1, sizeof(*kd))) == NULL)
778	return NULL;
779
780    if((kd->realm = strdup(realm)) == NULL) {
781	free(kd);
782	return NULL;
783    }
784
785    /* For 'realms' without a . do not even think of going to DNS */
786    if (!strchr(realm, '.'))
787	kd->flags |= KD_CONFIG_EXISTS;
788
789    if (flags & KRB5_KRBHST_FLAGS_LARGE_MSG)
790	kd->flags |= KD_LARGE_MSG;
791    kd->end = kd->index = &kd->hosts;
792    return kd;
793}
794
795/*
796 * initialize `handle' to look for hosts of type `type' in realm `realm'
797 */
798
799krb5_error_code KRB5_LIB_FUNCTION
800krb5_krbhst_init(krb5_context context,
801		 const char *realm,
802		 unsigned int type,
803		 krb5_krbhst_handle *handle)
804{
805    return krb5_krbhst_init_flags(context, realm, type, 0, handle);
806}
807
808krb5_error_code KRB5_LIB_FUNCTION
809krb5_krbhst_init_flags(krb5_context context,
810		       const char *realm,
811		       unsigned int type,
812		       int flags,
813		       krb5_krbhst_handle *handle)
814{
815    struct krb5_krbhst_data *kd;
816    krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *,
817			    krb5_krbhst_info **);
818    int def_port;
819
820    switch(type) {
821    case KRB5_KRBHST_KDC:
822	next = kdc_get_next;
823	def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88));
824	break;
825    case KRB5_KRBHST_ADMIN:
826	next = admin_get_next;
827	def_port = ntohs(krb5_getportbyname (context, "kerberos-adm",
828					     "tcp", 749));
829	break;
830    case KRB5_KRBHST_CHANGEPW:
831	next = kpasswd_get_next;
832	def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp",
833					     KPASSWD_PORT));
834	break;
835    case KRB5_KRBHST_KRB524:
836	next = krb524_get_next;
837	def_port = ntohs(krb5_getportbyname (context, "krb524", "udp", 4444));
838	break;
839    default:
840	krb5_set_error_message(context, ENOTTY,
841			       N_("unknown krbhst type (%u)", ""), type);
842	return ENOTTY;
843    }
844    if((kd = common_init(context, realm, flags)) == NULL)
845	return ENOMEM;
846    kd->get_next = next;
847    kd->def_port = def_port;
848    *handle = kd;
849    return 0;
850}
851
852/*
853 * return the next host information from `handle' in `host'
854 */
855
856krb5_error_code KRB5_LIB_FUNCTION
857krb5_krbhst_next(krb5_context context,
858		 krb5_krbhst_handle handle,
859		 krb5_krbhst_info **host)
860{
861    if(get_next(handle, host))
862	return 0;
863
864    return (*handle->get_next)(context, handle, host);
865}
866
867/*
868 * return the next host information from `handle' as a host name
869 * in `hostname' (or length `hostlen)
870 */
871
872krb5_error_code KRB5_LIB_FUNCTION
873krb5_krbhst_next_as_string(krb5_context context,
874			   krb5_krbhst_handle handle,
875			   char *hostname,
876			   size_t hostlen)
877{
878    krb5_error_code ret;
879    krb5_krbhst_info *host;
880    ret = krb5_krbhst_next(context, handle, &host);
881    if(ret)
882	return ret;
883    return krb5_krbhst_format_string(context, host, hostname, hostlen);
884}
885
886
887void KRB5_LIB_FUNCTION
888krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle)
889{
890    handle->index = &handle->hosts;
891}
892
893void KRB5_LIB_FUNCTION
894krb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle)
895{
896    krb5_krbhst_info *h, *next;
897
898    if (handle == NULL)
899	return;
900
901    for (h = handle->hosts; h != NULL; h = next) {
902	next = h->next;
903	_krb5_free_krbhst_info(h);
904    }
905
906    free(handle->realm);
907    free(handle);
908}
909
910/* backwards compatibility ahead */
911
912static krb5_error_code
913gethostlist(krb5_context context, const char *realm,
914	    unsigned int type, char ***hostlist)
915{
916    krb5_error_code ret;
917    int nhost = 0;
918    krb5_krbhst_handle handle;
919    char host[MAXHOSTNAMELEN];
920    krb5_krbhst_info *hostinfo;
921
922    ret = krb5_krbhst_init(context, realm, type, &handle);
923    if (ret)
924	return ret;
925
926    while(krb5_krbhst_next(context, handle, &hostinfo) == 0)
927	nhost++;
928    if(nhost == 0) {
929	krb5_set_error_message(context, KRB5_KDC_UNREACH,
930			       N_("No KDC found for realm %s", ""), realm);
931	return KRB5_KDC_UNREACH;
932    }
933    *hostlist = calloc(nhost + 1, sizeof(**hostlist));
934    if(*hostlist == NULL) {
935	krb5_krbhst_free(context, handle);
936	return ENOMEM;
937    }
938
939    krb5_krbhst_reset(context, handle);
940    nhost = 0;
941    while(krb5_krbhst_next_as_string(context, handle,
942				     host, sizeof(host)) == 0) {
943	if(((*hostlist)[nhost++] = strdup(host)) == NULL) {
944	    krb5_free_krbhst(context, *hostlist);
945	    krb5_krbhst_free(context, handle);
946	    return ENOMEM;
947	}
948    }
949    (*hostlist)[nhost] = NULL;
950    krb5_krbhst_free(context, handle);
951    return 0;
952}
953
954/*
955 * return an malloced list of kadmin-hosts for `realm' in `hostlist'
956 */
957
958krb5_error_code KRB5_LIB_FUNCTION
959krb5_get_krb_admin_hst (krb5_context context,
960			const krb5_realm *realm,
961			char ***hostlist)
962{
963    return gethostlist(context, *realm, KRB5_KRBHST_ADMIN, hostlist);
964}
965
966/*
967 * return an malloced list of changepw-hosts for `realm' in `hostlist'
968 */
969
970krb5_error_code KRB5_LIB_FUNCTION
971krb5_get_krb_changepw_hst (krb5_context context,
972			   const krb5_realm *realm,
973			   char ***hostlist)
974{
975    return gethostlist(context, *realm, KRB5_KRBHST_CHANGEPW, hostlist);
976}
977
978/*
979 * return an malloced list of 524-hosts for `realm' in `hostlist'
980 */
981
982krb5_error_code KRB5_LIB_FUNCTION
983krb5_get_krb524hst (krb5_context context,
984		    const krb5_realm *realm,
985		    char ***hostlist)
986{
987    return gethostlist(context, *realm, KRB5_KRBHST_KRB524, hostlist);
988}
989
990
991/*
992 * return an malloced list of KDC's for `realm' in `hostlist'
993 */
994
995krb5_error_code KRB5_LIB_FUNCTION
996krb5_get_krbhst (krb5_context context,
997		 const krb5_realm *realm,
998		 char ***hostlist)
999{
1000    return gethostlist(context, *realm, KRB5_KRBHST_KDC, hostlist);
1001}
1002
1003/*
1004 * free all the memory allocated in `hostlist'
1005 */
1006
1007krb5_error_code KRB5_LIB_FUNCTION
1008krb5_free_krbhst (krb5_context context,
1009		  char **hostlist)
1010{
1011    char **p;
1012
1013    for (p = hostlist; *p; ++p)
1014	free (*p);
1015    free (hostlist);
1016    return 0;
1017}
1018