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