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