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