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