2 * Copyright (c) 2001 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
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
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
36 #include "krb5_locl.h"
38 #include "locate_plugin.h"
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
;
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
)
64 struct rk_dns_reply
*r
;
65 struct rk_resource_record
*rr
;
73 proto_num
= string_to_proto(proto
);
75 krb5_set_error_message(context
, EINVAL
,
76 N_("unknown protocol `%s' to lookup", ""),
81 if(proto_num
== KRB5_KRBHST_HTTP
)
82 def_port
= ntohs(krb5_getportbyname (context
, "http", "tcp", 80));
84 def_port
= ntohs(krb5_getportbyname (context
, service
, proto
, 88));
88 snprintf(domain
, sizeof(domain
), "_%s._%s.%s.", service
, proto
, realm
);
90 r
= rk_dns_lookup(domain
, dns_type
);
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
)
101 *res
= malloc(num_srv
* sizeof(**res
));
104 return krb5_enomem(context
);
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
);
117 while(--num_srv
>= 0)
118 free((*res
)[num_srv
]);
121 return krb5_enomem(context
);
123 (*res
)[num_srv
++] = hi
;
125 hi
->proto
= proto_num
;
127 hi
->def_port
= def_port
;
131 hi
->port
= rr
->u
.srv
->port
;
133 strlcpy(hi
->hostname
, rr
->u
.srv
->target
, len
+ 1);
143 struct krb5_krbhst_data
{
147 int port
; /* hardwired port number if != 0 */
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
*,
161 unsigned int fallback_count
;
163 struct krb5_krbhst_info
*hosts
, **index
, **end
;
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)
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
;
185 krbhst_get_default_port(struct krb5_krbhst_data
*kd
)
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
));
216 hi
->proto
= krbhst_get_default_proto(kd
);
218 if(strncmp(p
, "http://", 7) == 0){
219 hi
->proto
= KRB5_KRBHST_HTTP
;
221 } else if(strncmp(p
, "http/", 5) == 0) {
222 hi
->proto
= KRB5_KRBHST_HTTP
;
224 def_port
= ntohs(krb5_getportbyname (context
, "http", "tcp", 80));
225 }else if(strncmp(p
, "tcp/", 4) == 0){
226 hi
->proto
= KRB5_KRBHST_TCP
;
228 } else if(strncmp(p
, "udp/", 4) == 0) {
229 hi
->proto
= KRB5_KRBHST_UDP
;
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';
242 } else if(strsep_copy(&p
, ":", hi
->hostname
, strlen(spec
) + 1) < 0) {
243 /* copy everything before : */
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]) {
254 hi
->port
= strtol(p
, &end
, 0);
265 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
266 _krb5_free_krbhst_info(krb5_krbhst_info
*hi
)
269 freeaddrinfo(hi
->ai
);
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
);
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
;
290 memcpy((*to
)->hostname
, from
->hostname
, hostnamelen
+ 1);
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
);
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
);
319 return krb5_enomem(context
);
321 append_host_hostinfo(kd
, hi
);
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
)
337 else if(host
->proto
== KRB5_KRBHST_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
);
346 * create a getaddrinfo `hints' based on `proto'
350 make_hints(struct addrinfo
*hints
, int proto
)
352 memset(hints
, 0, sizeof(*hints
));
353 hints
->ai_family
= AF_UNSPEC
;
355 case KRB5_KRBHST_UDP
:
356 hints
->ai_socktype
= SOCK_DGRAM
;
358 case KRB5_KRBHST_HTTP
:
359 case KRB5_KRBHST_TCP
:
360 hints
->ai_socktype
= SOCK_STREAM
;
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.
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
)
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
);
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
)
414 ret
= getaddrinfo(hostname
, portstr
, &hints
, &host
->ai
);
415 if (hostname
!= host
->hostname
)
418 ret
= krb5_eai_to_heim_errno(ret
, errno
);
428 get_next(struct krb5_krbhst_data
*kd
, krb5_krbhst_info
**host
)
430 struct krb5_krbhst_info
*hi
= *kd
->index
;
433 kd
->index
= &(*kd
->index
)->next
;
440 srv_get_hosts(krb5_context context
, struct krb5_krbhst_data
*kd
,
441 const char *proto
, const char *service
)
444 krb5_krbhst_info
**res
;
447 if (krb5_realm_is_lkdc(kd
->realm
))
450 ret
= srv_find_realm(context
, &res
, &count
, kd
->realm
, "SRV", proto
, service
,
452 _krb5_debug(context
, 2, "searching DNS for realm %s %s.%s -> %d",
453 kd
->realm
, proto
, service
, ret
);
456 for(i
= 0; i
< count
; i
++)
457 append_host_hostinfo(kd
, res
[i
]);
462 * read the configuration for `conf_string', defaulting to kd->def_port and
463 * forcing it to `kd->port' if kd->port != 0
467 config_get_hosts(krb5_context context
, struct krb5_krbhst_data
*kd
,
468 const char *conf_string
)
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");
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
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
)
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
);
507 kd
->flags
|= KD_FALLBACK
;
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
;
525 if(kd
->fallback_count
== 0)
526 ret
= asprintf(&host
, "%s.%s.", serv_string
, kd
->realm
);
528 ret
= asprintf(&host
, "%s-%d.%s.",
529 serv_string
, kd
->fallback_count
, kd
->realm
);
531 if (ret
< 0 || host
== NULL
)
534 make_hints(&hints
, proto
);
535 snprintf(portstr
, sizeof(portstr
), "%d", port
);
536 ret
= getaddrinfo(host
, portstr
, &hints
, &ai
);
538 /* no more hosts, so we're done here */
540 kd
->flags
|= KD_FALLBACK
;
542 struct krb5_krbhst_info
*hi
;
543 size_t hostlen
= strlen(host
);
545 hi
= calloc(1, sizeof(*hi
) + hostlen
);
552 hi
->port
= hi
->def_port
= port
;
554 memmove(hi
->hostname
, host
, hostlen
);
555 hi
->hostname
[hostlen
] = '\0';
557 append_host_hostinfo(kd
, hi
);
558 kd
->fallback_count
++;
564 * Fetch hosts from plugin
567 static krb5_error_code
568 add_plugin_host(struct krb5_krbhst_data
*kd
,
574 struct krb5_krbhst_info
*hi
;
575 struct addrinfo hints
, *ai
;
579 make_hints(&hints
, proto
);
580 ret
= getaddrinfo(host
, port
, &hints
, &ai
);
584 hostlen
= strlen(host
);
586 hi
= calloc(1, sizeof(*hi
) + hostlen
);
591 hi
->port
= hi
->def_port
= portnum
;
593 memmove(hi
->hostname
, host
, hostlen
);
594 hi
->hostname
[hostlen
] = '\0';
595 append_host_hostinfo(kd
, hi
);
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
];
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
);
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
);
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
636 if (proto
== KRB5_KRBHST_UDP
) {
637 ret
= add_plugin_host(kd
, host
, port
, portnum
, KRB5_KRBHST_TCP
);
646 enum locate_service_type type
;
647 struct krb5_krbhst_data
*kd
;
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
;
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
);
687 hostnames_get_hosts(krb5_context context
,
688 struct krb5_krbhst_data
*kd
,
691 kd
->flags
|= KD_HOSTNAMES
;
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
)
708 if ((kd
->flags
& KD_HOSTNAMES
) == 0) {
709 hostnames_get_hosts(context
, kd
, "kdc");
710 if(get_next(kd
, host
))
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
))
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
))
728 if (kd
->flags
& KD_CONFIG_EXISTS
) {
729 _krb5_debug(context
, 1,
730 "Configuration exists for realm %s, wont go to DNS",
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
))
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
))
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
))
757 while((kd
->flags
& KD_FALLBACK
) == 0) {
758 ret
= fallback_get_hosts(context
, kd
, "kerberos",
760 krbhst_get_default_proto(kd
));
763 if(get_next(kd
, host
))
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
)
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
))
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
))
793 if (kd
->flags
& KD_CONFIG_EXISTS
) {
794 _krb5_debug(context
, 1,
795 "Configuration exists for realm %s, wont go to DNS",
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
))
810 && (kd
->flags
& KD_FALLBACK
) == 0) {
811 ret
= fallback_get_hosts(context
, kd
, "kerberos",
813 krbhst_get_default_proto(kd
));
816 kd
->flags
|= KD_FALLBACK
;
817 if(get_next(kd
, host
))
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
)
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
))
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
))
847 if (kd
->flags
& KD_CONFIG_EXISTS
) {
848 _krb5_debug(context
, 1,
849 "Configuration exists for realm %s, wont go to DNS",
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
))
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
))
869 /* no matches -> try admin */
871 if (krbhst_empty(kd
)) {
873 kd
->port
= kd
->def_port
;
874 kd
->get_next
= admin_get_next
;
875 ret
= (*kd
->get_next
)(context
, kd
, host
);
877 (*host
)->proto
= krbhst_get_default_proto(kd
);
881 _krb5_debug(context
, 0, "No kpasswd entries found for realm %s", kd
->realm
);
883 return KRB5_KDC_UNREACH
;
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
) {
894 _krb5_free_krbhst_info(h
);
896 if (handle
->hostname
)
897 free(handle
->hostname
);
902 static struct krb5_krbhst_data
*
903 common_init(krb5_context context
,
908 struct krb5_krbhst_data
*kd
;
910 if ((kd
= heim_alloc(sizeof(*kd
), "krbhst-context", krbhost_dealloc
)) == NULL
)
913 if((kd
->realm
= strdup(realm
)) == 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
;
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
,
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
,
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
**);
960 case KRB5_KRBHST_KDC
:
962 def_port
= ntohs(krb5_getportbyname (context
, "kerberos", "udp", 88));
965 case KRB5_KRBHST_ADMIN
:
966 next
= admin_get_next
;
967 def_port
= ntohs(krb5_getportbyname (context
, "kerberos-adm",
971 case KRB5_KRBHST_CHANGEPW
:
972 next
= kpasswd_get_next
;
973 def_port
= ntohs(krb5_getportbyname (context
, "kpasswd", "udp",
975 service
= "change_password";
978 krb5_set_error_message(context
, ENOTTY
,
979 N_("unknown krbhst type (%u)", ""), type
);
982 if((kd
= common_init(context
, service
, realm
, flags
)) == NULL
)
985 kd
->def_port
= def_port
;
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
))
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
,
1016 krb5_error_code ret
;
1017 krb5_krbhst_info
*host
;
1018 ret
= krb5_krbhst_next(context
, handle
, &host
);
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
)
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
;
1063 krb5_krbhst_handle handle
;
1064 char host
[MAXHOSTNAMELEN
];
1065 krb5_krbhst_info
*hostinfo
;
1067 ret
= krb5_krbhst_init(context
, realm
, type
, &handle
);
1071 while(krb5_krbhst_next(context
, handle
, &hostinfo
) == 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
);
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
);
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
,
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
,
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
,
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
,
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
,
1157 for (p
= hostlist
; *p
; ++p
)
1163 #endif /* HEIMDAL_SMALLER */