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