1/* 2 * Copyright (c) 2001 - 2003 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2010 Apple Inc. 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 <resolve.h> 38#include "locate_plugin.h" 39 40static void append_host_hostinfo(struct krb5_krbhst_data *, struct krb5_krbhst_info *); 41 42struct krb5_krbhst_data { 43 struct heim_base_uniq base; 44 HEIMDAL_MUTEX mutex; 45 char *realm; 46 unsigned int flags; 47 int def_port; 48 int port; /* hardwired port number if != 0 */ 49#define KD_CONFIG 0x1 50#define KD_SRV_UDP 0x2 51#define KD_SRV_TCP 0x4 52#define KD_SRV_HTTP 0x8 53#define KD_SRV_KKDCP 0x10 54#define KD_FALLBACK 0x20 55#define KD_CONFIG_EXISTS 0x40 56#define KD_LARGE_MSG 0x80 57#define KD_PLUGIN 0x100 58#define KD_HOSTNAMES 0x200 59 krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *, 60 krb5_krbhst_info**); 61 62 char *hostname; 63 unsigned int fallback_count; 64 65 struct krb5_krbhst_info *hosts, **index, **end; 66 67 krb5_context context; 68 heim_queue_t process_queue; 69 heim_queue_t queue; 70 void (*callback)(void *, krb5_krbhst_info *); 71 void *userctx; 72}; 73 74static int 75string_to_proto(const char *string) 76{ 77 if(strcasecmp(string, "udp") == 0) 78 return KRB5_KRBHST_UDP; 79 else if(strcasecmp(string, "tcp") == 0) 80 return KRB5_KRBHST_TCP; 81 else if(strcasecmp(string, "http") == 0) 82 return KRB5_KRBHST_HTTP; 83 else if(strcasecmp(string, "kkdcp") == 0) 84 return KRB5_KRBHST_KKDCP; 85 return -1; 86} 87 88static void 89query_release(void *ctx) 90{ 91 struct _krb5_srv_query_ctx *query = (struct _krb5_srv_query_ctx *)ctx; 92 93 free(query->domain); 94 heim_release(query->handle); 95 if (query->sema) 96 dispatch_release((dispatch_semaphore_t)query->sema); 97#ifdef __APPLE__ 98 if (query->array) 99 free(query->array); 100#endif 101} 102 103 104 105 106#ifdef __APPLE__ 107 108#include <dns_sd.h> 109 110 111static int 112compare_srv(const void *a, const void *b) 113{ 114 const struct srv_reply *aa = a, *bb = b; 115 116 if(aa->priority == bb->priority) 117 return aa->weight - bb->weight; 118 return aa->priority - aa->priority; 119} 120 121void 122_krb5_state_srv_sort(struct _krb5_srv_query_ctx *query) 123{ 124 size_t n, m, o, prio_marker; 125 uint32_t rnd, sum = 0; 126 127 /* don't sort [0,1] srv records, they come pre-sorted */ 128 if (query->len < 2) 129 return; 130 131 /* sort them by priority and weight */ 132 qsort(query->array, query->len, sizeof(query->array[0]), compare_srv); 133 134 /* 135 * Fixup weight sorting too, assign a negative weight to elements 136 * that are picked. Negative since the protocol only defines 137 * postive values (int16_t) 138 */ 139 prio_marker = 0; 140 141 for (n = 1; n < query->len; n++) { 142 if (query->array[prio_marker].priority != query->array[n].priority) { 143 144 for (m = prio_marker; m < n && sum != 0; m++) { 145 int32_t count = -1; 146 147 rnd = rk_random() % sum; 148 149 for (o = prio_marker; o < n; o++) { 150 if (query->array[o].weight < 0) 151 continue; 152 if (rnd <= (uint32_t)query->array[o].weight) { 153 sum -= query->array[o].weight; 154 query->array[o].weight = count--; 155 break; 156 } 157 rnd -= query->array[o].weight; 158 } 159 if (o >= n) 160 _krb5_debugx(query->context, 2, 161 "o too large: sum %d", (int)sum); 162 } 163 sum = 0; 164 prio_marker = n; 165 } else { 166 sum += query->array[n].weight; 167 } 168 } 169 170 qsort(query->array, query->len, sizeof(query->array[0]), compare_srv); 171} 172 173static void 174state_append_hosts(struct _krb5_srv_query_ctx *query) 175{ 176 size_t n; 177 178 _krb5_debugx(query->context, 10, "SRV order after sorting"); 179 180 for (n = 0; n < query->len; n++) { 181 _krb5_debugx(query->context, 10, " SRV%lu kdc: %s prio: %d weight: %d", 182 (unsigned long)n, 183 query->array[n].hi->hostname, 184 query->array[n].priority, 185 query->array[n].weight); 186 187 append_host_hostinfo(query->handle, query->array[n].hi); 188 } 189 190 dispatch_semaphore_signal((dispatch_semaphore_t)query->sema); 191 192 heim_release(query); 193} 194 195static void 196QueryReplyCallback(DNSServiceRef sdRef, 197 DNSServiceFlags flags, 198 uint32_t ifIndex, 199 DNSServiceErrorType errorCode, 200 const char *fullname, 201 uint16_t rrtype, 202 uint16_t rrclass, 203 uint16_t rdlen, 204 const void *rdata, 205 uint32_t ttl, 206 void *context) 207{ 208 const uint8_t *end_rd = ((uint8_t *)rdata) + rdlen, *rd = rdata; 209 struct _krb5_srv_query_ctx *query = context; 210 uint16_t priority, weight, port; 211 struct srv_reply *tmp; 212 krb5_krbhst_info *hi; 213 int status; 214 215 if (errorCode != kDNSServiceErr_NoError) { 216 flags = 0; /* other values are undefined on failure */ 217 goto end; 218 } 219 220 if (rrtype != kDNSServiceType_SRV) 221 goto end; 222 223 if (rdlen < 7) 224 goto end; 225 226 priority = (rd[0] << 8) | rd[1]; 227 weight = (rd[2] << 8) | rd[3]; 228 port = (rd[4] << 8) | rd[5]; 229 230 if (rdlen + sizeof(*hi) < rdlen) 231 goto end; 232 233 hi = calloc(1, sizeof(*hi) + rdlen); 234 if (hi == NULL) 235 goto end; 236 237 status = dn_expand(rdata, end_rd, rd + 6, hi->hostname, rdlen); 238 if(status < 0 || (size_t)status + 6 > rdlen) { 239 free(hi); 240 goto end; 241 } 242 243 hi->proto = query->proto_num; 244 245 hi->def_port = 0; 246 if (query->port != 0) 247 hi->port = query->port; 248 else 249 hi->port = port; 250 251 if (query->path) 252 hi->path = strdup(query->path); 253 254 tmp = realloc(query->array, (query->len + 1) * sizeof(query->array[0])); 255 if (tmp == NULL) { 256 free(hi); 257 goto end; 258 } 259 query->array = tmp; 260 query->array[query->len].hi = hi; 261 query->array[query->len].priority = priority; 262 query->array[query->len].weight = weight; 263 query->len++; 264 265 end: 266 if ((flags & kDNSServiceFlagsMoreComing) == 0) { 267 _krb5_state_srv_sort(query); 268 state_append_hosts(query); 269 } 270} 271 272/* 273 * 274 */ 275 276static krb5_error_code 277srv_find_realm(krb5_context context, struct krb5_krbhst_data *handle, 278 heim_queue_t dnsQueue, 279 struct _krb5_srv_query_ctx *query) 280{ 281 DNSServiceRef client = NULL; 282 DNSServiceErrorType error; 283 krb5_error_code ret; 284 285 286 error = DNSServiceQueryRecord(&client, 287 kDNSServiceFlagsTimeout | kDNSServiceFlagsReturnIntermediates, 288 0, 289 query->domain, 290 kDNSServiceType_SRV, 291 kDNSServiceClass_IN, 292 QueryReplyCallback, 293 query); 294 if (!error) { 295 heim_retain(query); 296 297 DNSServiceSetDispatchQueue(client, (dispatch_queue_t)dnsQueue); 298 299 if (dispatch_semaphore_wait((dispatch_semaphore_t)query->sema, dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC))) { 300 _krb5_debugx(context, 2, 301 "searching DNS %s for domain timed out", 302 query->domain); 303 ret = KRB5_KDC_UNREACH; 304 } else 305 ret = 0; 306 307 /* must run the DNSServiceRefDeallocate on the same queue as dns request are processed on */ 308 dispatch_sync((dispatch_queue_t)dnsQueue, ^{ 309 DNSServiceRefDeallocate(client); 310 }); 311 } else { 312 _krb5_debugx(context, 2, 313 "searching DNS for domain %s failed: %d", 314 query->domain, error); 315 ret = KRB5_KDC_UNREACH; 316 } 317 318 heim_release(query); 319 320 return ret; 321} 322 323#else 324 325/* 326 * 327 */ 328 329static void 330srv_query_domain(void *ctx) 331{ 332 struct _krb5_srv_query_ctx *query = ctx; 333 struct rk_dns_reply *r; 334 struct rk_resource_record *rr; 335 336 r = rk_dns_lookup(query->domain, "SRV"); 337 if(r == NULL) 338 goto out; 339 340 rk_dns_srv_order(r); 341 342 for(rr = r->head; rr; rr = rr->next) { 343 if(rr->type == rk_ns_t_srv) { 344 krb5_krbhst_info *hi; 345 size_t len = strlen(rr->u.srv->target); 346 347 hi = calloc(1, sizeof(*hi) + len); 348 if(hi == NULL) 349 goto out; 350 351 hi->proto = query->proto_num; 352 353 hi->def_port = query->def_port; 354 if (handle->port != 0) 355 hi->port = handle->port; 356 else 357 hi->port = rr->u.srv->port; 358 359 if (query->path) 360 hi->path = strdup(query->path); 361 strlcpy(hi->hostname, rr->u.srv->target, len + 1); 362 363 append_host_hostinfo(kd, hi); 364 } 365 } 366 367 out: 368 if (r) 369 rk_dns_free_data(r); 370 heim_sema_signal(queue->sema); 371 heim_release(queue->handle); 372} 373 374static krb5_error_code 375srv_find_realm(krb5_context context, struct krb5_krbhst_data *handle, 376 heim_queue_t queue, 377 struct _krb5_srv_query_ctx *query) 378{ 379 heim_async_f(queue, query, srv_query_domain); 380 heim_sema_wait(query->sema, 10); 381 heim_release(query); 382} 383 384#endif 385 386 387static krb5_boolean 388krbhst_empty(struct krb5_krbhst_data *kd) 389{ 390 krb5_boolean empty; 391 392 HEIMDAL_MUTEX_lock(&kd->mutex); 393 empty = (kd->index == &kd->hosts); 394 HEIMDAL_MUTEX_unlock(&kd->mutex); 395 396 return empty; 397} 398 399/* 400 * Return the default protocol for the `kd' (either TCP or UDP) 401 */ 402 403static int 404krbhst_get_default_proto(struct krb5_krbhst_data *kd) 405{ 406 if (kd->flags & KD_LARGE_MSG) 407 return KRB5_KRBHST_TCP; 408 return KRB5_KRBHST_UDP; 409} 410 411static int 412krbhst_get_default_port(struct krb5_krbhst_data *kd) 413{ 414 return kd->def_port; 415} 416 417/* 418 * 419 */ 420 421const char * 422_krb5_krbhst_get_realm(krb5_krbhst_handle handle) 423{ 424 return handle->realm; 425} 426 427/* 428 * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port' 429 * and forcing it to `port' if port != 0 430 */ 431 432static struct krb5_krbhst_info* 433parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd, 434 const char *spec, int def_port, int port) 435{ 436 const char *p = spec, *q, *portstr = NULL; 437 struct krb5_krbhst_info *hi; 438 size_t end_hostname; 439 440 hi = calloc(1, sizeof(*hi) + strlen(spec)); 441 if(hi == NULL) 442 return NULL; 443 444 hi->proto = krbhst_get_default_proto(kd); 445 446 if(strncmp(p, "http://", 7) == 0){ 447 hi->proto = KRB5_KRBHST_HTTP; 448 p += 7; 449 } else if(strncmp(p, "http/", 5) == 0) { 450 hi->proto = KRB5_KRBHST_HTTP; 451 p += 5; 452 def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80)); 453 } else if(strncmp(p, "kkdcp://", 8) == 0) { 454 hi->proto = KRB5_KRBHST_KKDCP; 455 p += 8; 456 def_port = ntohs(krb5_getportbyname (context, "https", "tcp", 443)); 457 }else if(strncmp(p, "tcp/", 4) == 0){ 458 hi->proto = KRB5_KRBHST_TCP; 459 p += 4; 460 } else if(strncmp(p, "udp/", 4) == 0) { 461 hi->proto = KRB5_KRBHST_UDP; 462 p += 4; 463 } 464 465 if (p[0] == '[' && (q = strchr(p, ']')) != NULL) { 466 /* if address looks like [foo:bar] or [foo:bar]: its a ipv6 467 adress, strip of [] */ 468 memcpy(hi->hostname, &p[1], q - p - 1); 469 hi->hostname[q - p - 1] = '\0'; 470 p = q + 1; 471 /* get trailing : */ 472 if (p[0] == ':') 473 portstr = ++p; 474 475 p = strchr(p, '/'); 476 477 } else if ((end_hostname = strcspn(p, ":/")) != 0) { 478 memcpy(hi->hostname, p, end_hostname); 479 hi->hostname[end_hostname] = '\0'; 480 if (p[end_hostname] == ':') { 481 portstr = p + end_hostname + 1; 482 p = strchr(p, '/'); 483 } else { 484 p = p + end_hostname; 485 } 486 } else { 487 memcpy(hi->hostname, p, strlen(p) + 1); 488 } 489 490 /* if we had a path, pick it up now */ 491 if (p && p[0] == '/') 492 hi->path = strdup((char *)(p + 1)); 493 494 strlwr(hi->hostname); 495 496 hi->port = hi->def_port = def_port; 497 if(portstr != NULL && portstr[0]) { 498 char *end; 499 hi->port = strtol(portstr, &end, 0); 500 if(end == portstr) { 501 if (hi->path) 502 free(hi->path); 503 free(hi); 504 return NULL; 505 } 506 } 507 if (port) 508 hi->port = port; 509 return hi; 510} 511 512void 513_krb5_free_krbhst_info(krb5_krbhst_info *hi) 514{ 515 if (hi->ai != NULL) 516 freeaddrinfo(hi->ai); 517 if (hi->path) 518 free(hi->path); 519 free(hi); 520} 521 522krb5_error_code 523_krb5_krbhost_info_move(krb5_context context, 524 krb5_krbhst_info *from, 525 krb5_krbhst_info **to) 526{ 527 size_t hostnamelen = strlen(from->hostname); 528 /* trailing NUL is included in structure */ 529 *to = calloc(1, sizeof(**to) + hostnamelen); 530 if(*to == NULL) { 531 krb5_set_error_message(context, ENOMEM, 532 N_("malloc: out of memory", "")); 533 return ENOMEM; 534 } 535 536 (*to)->proto = from->proto; 537 (*to)->port = from->port; 538 (*to)->def_port = from->def_port; 539 (*to)->ai = from->ai; 540 from->ai = NULL; 541 (*to)->next = NULL; 542 (*to)->path = from->path; 543 from->path = NULL; 544 memcpy((*to)->hostname, from->hostname, hostnamelen + 1); 545 return 0; 546} 547 548 549static void 550append_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host) 551{ 552 struct krb5_krbhst_info *h; 553 554 HEIMDAL_MUTEX_lock(&kd->mutex); 555 556 for(h = kd->hosts; h && host; h = h->next) { 557 if(h->proto == host->proto && 558 h->port == host->port && 559 strcasecmp(h->hostname, host->hostname) == 0) 560 { 561 _krb5_free_krbhst_info(host); 562 host = NULL; 563 } 564 } 565 566 if (host) { 567 *kd->end = host; 568 kd->end = &host->next; 569 } 570 571 HEIMDAL_MUTEX_unlock(&kd->mutex); 572} 573 574static krb5_error_code 575append_host_string(krb5_context context, struct krb5_krbhst_data *kd, 576 const char *host, int def_port, int port) 577{ 578 struct krb5_krbhst_info *hi; 579 580 hi = parse_hostspec(context, kd, host, def_port, port); 581 if(hi == NULL) 582 return ENOMEM; 583 584 append_host_hostinfo(kd, hi); 585 return 0; 586} 587 588/* 589 * return a readable representation of `host' in `hostname, hostlen' 590 */ 591 592KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 593krb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host, 594 char *hostname, size_t hostlen) 595{ 596 const char *proto = ""; 597 char portstr[7] = ""; 598 if(host->proto == KRB5_KRBHST_TCP) 599 proto = "tcp/"; 600 else if(host->proto == KRB5_KRBHST_HTTP) 601 proto = "http://"; 602 else if(host->proto == KRB5_KRBHST_KKDCP) 603 proto = "kkdcp://"; 604 if(host->port != host->def_port) 605 snprintf(portstr, sizeof(portstr), ":%d", host->port); 606 snprintf(hostname, hostlen, "%s%s%s%s%s", proto, host->hostname, portstr, 607 host->proto == KRB5_KRBHST_KKDCP ? "/" : "", 608 host->proto == KRB5_KRBHST_KKDCP ? host->path : ""); 609 return 0; 610} 611 612/* 613 * create a getaddrinfo `hints' based on `proto' 614 */ 615 616static void 617make_hints(struct addrinfo *hints, int proto) 618{ 619 memset(hints, 0, sizeof(*hints)); 620 hints->ai_family = AF_UNSPEC; 621 switch(proto) { 622 case KRB5_KRBHST_UDP : 623 hints->ai_socktype = SOCK_DGRAM; 624 break; 625 case KRB5_KRBHST_KKDCP : 626 case KRB5_KRBHST_HTTP : 627 case KRB5_KRBHST_TCP : 628 hints->ai_socktype = SOCK_STREAM; 629 break; 630 } 631} 632 633/** 634 * Return an `struct addrinfo *' for a KDC host. 635 * 636 * Returns an the struct addrinfo in in that corresponds to the 637 * information in `host'. free:ing is handled by krb5_krbhst_free, so 638 * the returned ai must not be released. 639 * 640 * @ingroup krb5 641 */ 642 643KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 644krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host, 645 struct addrinfo **ai) 646{ 647 int ret = 0; 648 649 if (host->ai == NULL) { 650 struct addrinfo hints; 651 char portstr[NI_MAXSERV]; 652 char *hostname = host->hostname; 653 654 snprintf (portstr, sizeof(portstr), "%d", host->port); 655 make_hints(&hints, host->proto); 656 657 /** 658 * First try this as an IP address, this allows us to add a 659 * dot at the end to stop using the search domains. 660 */ 661 662 hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; 663 664 ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai); 665 if (ret == 0) 666 goto out; 667 668 /** 669 * If the hostname contains a dot, assumes it's a FQDN and 670 * don't use search domains since that might be painfully slow 671 * when machine is disconnected from that network. 672 */ 673 674 hints.ai_flags &= ~(AI_NUMERICHOST); 675 676 if (strchr(hostname, '.') && hostname[strlen(hostname) - 1] != '.') { 677 ret = asprintf(&hostname, "%s.", host->hostname); 678 if (ret < 0 || hostname == NULL) 679 return ENOMEM; 680 } 681 682 ret = getaddrinfo(hostname, portstr, &hints, &host->ai); 683 if (hostname != host->hostname) 684 free(hostname); 685 if (ret) { 686 ret = krb5_eai_to_heim_errno(ret, errno); 687 goto out; 688 } 689 } 690 out: 691 *ai = host->ai; 692 return ret; 693} 694 695static krb5_boolean 696get_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host) 697{ 698 struct krb5_krbhst_info *hi; 699 700 HEIMDAL_MUTEX_lock(&kd->mutex); 701 702 hi = *kd->index; 703 if(hi != NULL) { 704 *host = hi; 705 kd->index = &(*kd->index)->next; 706 } 707 HEIMDAL_MUTEX_unlock(&kd->mutex); 708 return hi ? TRUE : FALSE; 709} 710 711static void 712srv_get_dns_queue(void *ctx) 713{ 714 heim_queue_t *dnsQueue = ctx; 715 *dnsQueue = heim_queue_create("com.apple.kerberos.dns", NULL); 716} 717 718static krb5_error_code 719srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, 720 const char *proto, const char *service) 721{ 722 struct _krb5_srv_query_ctx *query = NULL; 723 static heim_queue_t dnsQueue; 724 static heim_base_once_t once; 725 int proto_num, def_port; 726 const char *path = NULL; 727 char *domain = NULL; 728 729 if (krb5_realm_is_lkdc(kd->realm)) 730 return 0; 731 732 heim_base_once_f(&once, &dnsQueue, srv_get_dns_queue); 733 734 proto_num = string_to_proto(proto); 735 if(proto_num < 0) { 736 _krb5_debugx(context, 1, N_("unknown protocol `%s' to lookup", ""), proto); 737 return 0; 738 } 739 740 if(proto_num == KRB5_KRBHST_HTTP) { 741 def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80)); 742 } else if(proto_num == KRB5_KRBHST_KKDCP) { 743 def_port = ntohs(krb5_getportbyname (context, "https", "tcp", 443)); 744 path = "kkdcp"; 745 } else if(kd->port) { 746 def_port = kd->port; 747 } else { 748 def_port = ntohs(krb5_getportbyname (context, service, proto, 88)); 749 } 750 751 asprintf(&domain, "_%s._%s.%s.", service, proto, kd->realm); 752 if (domain == NULL) { 753 return krb5_enomem(context); 754 } 755 756 query = heim_uniq_alloc(sizeof(*query), "heim-query-ctx", query_release); 757 if (query == NULL) { 758 free(domain); 759 return krb5_enomem(context); 760 } 761 762 query->context = context; 763 query->sema = heim_sema_create(0); 764 query->domain = domain; 765 if (query->sema == NULL) { 766 heim_release(query); 767 return krb5_enomem(context); 768 } 769 query->handle = heim_retain(kd); 770 query->def_port = def_port; 771 query->proto_num = proto_num; 772 query->path = path; 773#ifdef __APPLE__ 774 query->array = NULL; 775 query->len = 0; 776#endif 777 778 srv_find_realm(context, kd, dnsQueue, query); 779 780 return 0; 781} 782 783/* 784 * read the configuration for `conf_string', defaulting to kd->def_port and 785 * forcing it to `kd->port' if kd->port != 0 786 */ 787 788static void 789config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, 790 const char *conf_string) 791{ 792 int i; 793 char **hostlist; 794 hostlist = krb5_config_get_strings(context, NULL, 795 "realms", kd->realm, conf_string, NULL); 796 797 _krb5_debugx(context, 2, "configuration file for realm %s%s found", 798 kd->realm, hostlist ? "" : " not"); 799 800 if(hostlist == NULL) 801 return; 802 kd->flags |= KD_CONFIG_EXISTS; 803 for(i = 0; hostlist && hostlist[i] != NULL; i++) 804 append_host_string(context, kd, hostlist[i], kd->def_port, kd->port); 805 806 krb5_config_free_strings(hostlist); 807} 808 809/* 810 * as a fallback, look for `serv_string.kd->realm' (typically 811 * kerberos.REALM, kerberos-1.REALM, ... 812 * `port' is the default port for the service, and `proto' the 813 * protocol 814 */ 815 816static krb5_error_code 817fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, 818 const char *serv_string, int port, int proto) 819{ 820 char *host = NULL; 821 int ret; 822 struct addrinfo *ai; 823 struct addrinfo hints; 824 char portstr[NI_MAXSERV]; 825 826 ret = krb5_config_get_bool_default(context, NULL, KRB5_FALLBACK_DEFAULT, 827 "libdefaults", "use_fallback", NULL); 828 if (!ret) { 829 kd->flags |= KD_FALLBACK; 830 return 0; 831 } 832 833 _krb5_debugx(context, 2, "fallback lookup %d for realm %s (service %s)", 834 kd->fallback_count, kd->realm, serv_string); 835 836 /* 837 * Don't try forever in case the DNS server keep returning us 838 * entries (like wildcard entries or the .nu TLD) 839 * 840 * Also don't try LKDC realms since fallback wont work on them at all. 841 */ 842 if(kd->fallback_count >= 5 || krb5_realm_is_lkdc(kd->realm)) { 843 kd->flags |= KD_FALLBACK; 844 return 0; 845 } 846 847 if(kd->fallback_count == 0) 848 ret = asprintf(&host, "%s.%s.", serv_string, kd->realm); 849 else 850 ret = asprintf(&host, "%s-%d.%s.", 851 serv_string, kd->fallback_count, kd->realm); 852 853 if (ret < 0 || host == NULL) 854 return ENOMEM; 855 856 make_hints(&hints, proto); 857 snprintf(portstr, sizeof(portstr), "%d", port); 858 ret = getaddrinfo(host, portstr, &hints, &ai); 859 if (ret) { 860 /* no more hosts, so we're done here */ 861 free(host); 862 kd->flags |= KD_FALLBACK; 863 } else { 864 struct krb5_krbhst_info *hi; 865 size_t hostlen = strlen(host); 866 867 hi = calloc(1, sizeof(*hi) + hostlen); 868 if(hi == NULL) { 869 free(host); 870 return ENOMEM; 871 } 872 873 hi->proto = proto; 874 hi->port = hi->def_port = port; 875 hi->ai = ai; 876 memmove(hi->hostname, host, hostlen); 877 hi->hostname[hostlen] = '\0'; 878 free(host); 879 append_host_hostinfo(kd, hi); 880 kd->fallback_count++; 881 } 882 return 0; 883} 884 885/* 886 * Fetch hosts from plugin 887 */ 888 889static krb5_error_code 890add_plugin_host(struct krb5_krbhst_data *kd, 891 const char *host, 892 const char *port, 893 int portnum, 894 int proto) 895{ 896 struct krb5_krbhst_info *hi; 897 struct addrinfo hints, *ai; 898 size_t hostlen; 899 int ret; 900 901 make_hints(&hints, proto); 902 ret = getaddrinfo(host, port, &hints, &ai); 903 if (ret) 904 return 0; 905 906 hostlen = strlen(host); 907 908 hi = calloc(1, sizeof(*hi) + hostlen); 909 if(hi == NULL) 910 return ENOMEM; 911 912 hi->proto = proto; 913 hi->port = hi->def_port = portnum; 914 hi->ai = ai; 915 memmove(hi->hostname, host, hostlen); 916 hi->hostname[hostlen] = '\0'; 917 append_host_hostinfo(kd, hi); 918 919 return 0; 920} 921 922static krb5_error_code 923add_locate(void *ctx, int type, struct sockaddr *addr) 924{ 925 struct krb5_krbhst_data *kd = ctx; 926 char host[NI_MAXHOST], port[NI_MAXSERV]; 927 socklen_t socklen; 928 krb5_error_code ret; 929 int proto, portnum; 930 931 socklen = socket_sockaddr_size(addr); 932 portnum = socket_get_port(addr); 933 934 ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port), 935 NI_NUMERICHOST|NI_NUMERICSERV); 936 if (ret != 0) 937 return 0; 938 939 if (kd->port) 940 snprintf(port, sizeof(port), "%d", kd->port); 941 else if (atoi(port) == 0) 942 snprintf(port, sizeof(port), "%d", krbhst_get_default_port(kd)); 943 944 proto = krbhst_get_default_proto(kd); 945 946 ret = add_plugin_host(kd, host, port, portnum, proto); 947 if (ret) 948 return ret; 949 950 /* 951 * This is really kind of broken and should be solved a different 952 * way, some sites block UDP, and we don't, in the general case, 953 * fall back to TCP, that should also be done. But since that 954 * should require us to invert the whole "find kdc" stack, let put 955 * this in for now. 956 */ 957 958 if (proto == KRB5_KRBHST_UDP) { 959 ret = add_plugin_host(kd, host, port, portnum, KRB5_KRBHST_TCP); 960 if (ret) 961 return ret; 962 } 963 964 return 0; 965} 966 967struct plctx { 968 enum locate_service_type type; 969 struct krb5_krbhst_data *kd; 970 unsigned long flags; 971}; 972 973static krb5_error_code 974plcallback(krb5_context context, 975 const void *plug, void *plugctx, void *userctx) 976{ 977 const krb5plugin_service_locate_ftable *locate = plug; 978 struct plctx *plctx = userctx; 979 980 if (locate->minor_version >= KRB5_PLUGIN_LOCATE_VERSION_2) 981 return locate->lookup(plugctx, plctx->flags, plctx->type, plctx->kd->realm, 0, 0, add_locate, plctx->kd); 982 983 if (plctx->flags & KRB5_PLF_ALLOW_HOMEDIR) 984 return locate->old_lookup(plugctx, plctx->type, plctx->kd->realm, 0, 0, add_locate, plctx->kd); 985 986 return KRB5_PLUGIN_NO_HANDLE; 987} 988 989static void 990plugin_get_hosts(krb5_context context, 991 struct krb5_krbhst_data *kd, 992 enum locate_service_type type) 993{ 994 struct plctx ctx = { type, kd, 0 }; 995 996 if (krb5_homedir_access(context)) 997 ctx.flags |= KRB5_PLF_ALLOW_HOMEDIR; 998 999 krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_LOCATE, 1000 KRB5_PLUGIN_LOCATE_VERSION_0, 1001 0, &ctx, plcallback); 1002} 1003 1004/* 1005 * 1006 */ 1007 1008static void 1009hostnames_get_hosts(krb5_context context, 1010 struct krb5_krbhst_data *kd, 1011 const char *type) 1012{ 1013 kd->flags |= KD_HOSTNAMES; 1014 if (kd->hostname) 1015 append_host_string(context, kd, kd->hostname, kd->def_port, kd->port); 1016} 1017 1018 1019/* 1020 * 1021 */ 1022 1023static krb5_error_code 1024kdc_get_next(krb5_context context, 1025 struct krb5_krbhst_data *kd, 1026 krb5_krbhst_info **host) 1027{ 1028 krb5_error_code ret; 1029 1030 if ((kd->flags & KD_HOSTNAMES) == 0) { 1031 hostnames_get_hosts(context, kd, "kdc"); 1032 if(get_next(kd, host)) 1033 return 0; 1034 } 1035 1036 if ((kd->flags & KD_PLUGIN) == 0) { 1037 plugin_get_hosts(context, kd, locate_service_kdc); 1038 kd->flags |= KD_PLUGIN; 1039 if(get_next(kd, host)) 1040 return 0; 1041 } 1042 1043 if((kd->flags & KD_CONFIG) == 0) { 1044 config_get_hosts(context, kd, "kdc"); 1045 kd->flags |= KD_CONFIG; 1046 if(get_next(kd, host)) 1047 return 0; 1048 } 1049 1050 if (kd->flags & KD_CONFIG_EXISTS) { 1051 _krb5_debugx(context, 1, 1052 "Configuration exists for realm %s, wont go to DNS", 1053 kd->realm); 1054 return KRB5_KDC_UNREACH; 1055 } 1056 1057 if(context->srv_lookup) { 1058 if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) { 1059 srv_get_hosts(context, kd, "udp", "kerberos"); 1060 kd->flags |= KD_SRV_UDP; 1061 if(get_next(kd, host)) 1062 return 0; 1063 } 1064 1065 if((kd->flags & KD_SRV_TCP) == 0) { 1066 srv_get_hosts(context, kd, "tcp", "kerberos"); 1067 kd->flags |= KD_SRV_TCP; 1068 if(get_next(kd, host)) 1069 return 0; 1070 } 1071 if((kd->flags & KD_SRV_HTTP) == 0) { 1072 srv_get_hosts(context, kd, "http", "kerberos"); 1073 kd->flags |= KD_SRV_HTTP; 1074 if(get_next(kd, host)) 1075 return 0; 1076 } 1077 if((kd->flags & KD_SRV_KKDCP) == 0) { 1078 srv_get_hosts(context, kd, "kkdcp", "kerberos"); 1079 kd->flags |= KD_SRV_KKDCP; 1080 if(get_next(kd, host)) 1081 return 0; 1082 } 1083 } 1084 1085 while((kd->flags & KD_FALLBACK) == 0) { 1086 ret = fallback_get_hosts(context, kd, "kerberos", 1087 kd->def_port, 1088 krbhst_get_default_proto(kd)); 1089 if(ret) 1090 return ret; 1091 if(get_next(kd, host)) 1092 return 0; 1093 } 1094 1095 _krb5_debugx(context, 0, "No KDC entries found for %s", kd->realm); 1096 1097 return KRB5_KDC_UNREACH; /* XXX */ 1098} 1099 1100static krb5_error_code 1101admin_get_next(krb5_context context, 1102 struct krb5_krbhst_data *kd, 1103 krb5_krbhst_info **host) 1104{ 1105 krb5_error_code ret; 1106 1107 if ((kd->flags & KD_PLUGIN) == 0) { 1108 plugin_get_hosts(context, kd, locate_service_kadmin); 1109 kd->flags |= KD_PLUGIN; 1110 if(get_next(kd, host)) 1111 return 0; 1112 } 1113 1114 if((kd->flags & KD_CONFIG) == 0) { 1115 config_get_hosts(context, kd, "admin_server"); 1116 kd->flags |= KD_CONFIG; 1117 if(get_next(kd, host)) 1118 return 0; 1119 } 1120 1121 if (kd->flags & KD_CONFIG_EXISTS) { 1122 _krb5_debugx(context, 1, 1123 "Configuration exists for realm %s, wont go to DNS", 1124 kd->realm); 1125 return KRB5_KDC_UNREACH; 1126 } 1127 1128 if(context->srv_lookup) { 1129 if((kd->flags & KD_SRV_TCP) == 0) { 1130 srv_get_hosts(context, kd, "tcp", "kerberos-adm"); 1131 kd->flags |= KD_SRV_TCP; 1132 if(get_next(kd, host)) 1133 return 0; 1134 } 1135 } 1136 1137 if (krbhst_empty(kd) 1138 && (kd->flags & KD_FALLBACK) == 0) { 1139 ret = fallback_get_hosts(context, kd, "kerberos", 1140 kd->def_port, 1141 krbhst_get_default_proto(kd)); 1142 if(ret) 1143 return ret; 1144 kd->flags |= KD_FALLBACK; 1145 if(get_next(kd, host)) 1146 return 0; 1147 } 1148 1149 _krb5_debugx(context, 0, "No admin entries found for realm %s", kd->realm); 1150 1151 return KRB5_KDC_UNREACH; /* XXX */ 1152} 1153 1154static krb5_error_code 1155kpasswd_get_next(krb5_context context, 1156 struct krb5_krbhst_data *kd, 1157 krb5_krbhst_info **host) 1158{ 1159 krb5_error_code ret; 1160 1161 if ((kd->flags & KD_PLUGIN) == 0) { 1162 plugin_get_hosts(context, kd, locate_service_kpasswd); 1163 kd->flags |= KD_PLUGIN; 1164 if(get_next(kd, host)) 1165 return 0; 1166 } 1167 1168 if((kd->flags & KD_CONFIG) == 0) { 1169 config_get_hosts(context, kd, "kpasswd_server"); 1170 kd->flags |= KD_CONFIG; 1171 if(get_next(kd, host)) 1172 return 0; 1173 } 1174 1175 if (kd->flags & KD_CONFIG_EXISTS) { 1176 _krb5_debugx(context, 1, 1177 "Configuration exists for realm %s, wont go to DNS", 1178 kd->realm); 1179 return KRB5_KDC_UNREACH; 1180 } 1181 1182 if(context->srv_lookup) { 1183 if((kd->flags & KD_SRV_UDP) == 0) { 1184 srv_get_hosts(context, kd, "udp", "kpasswd"); 1185 kd->flags |= KD_SRV_UDP; 1186 if(get_next(kd, host)) 1187 return 0; 1188 } 1189 if((kd->flags & KD_SRV_TCP) == 0) { 1190 srv_get_hosts(context, kd, "tcp", "kpasswd"); 1191 kd->flags |= KD_SRV_TCP; 1192 if(get_next(kd, host)) 1193 return 0; 1194 } 1195 } 1196 1197 /* no matches -> try admin */ 1198 1199 if (krbhst_empty(kd)) { 1200 kd->flags = 0; 1201 kd->port = kd->def_port; 1202 kd->get_next = admin_get_next; 1203 ret = (*kd->get_next)(context, kd, host); 1204 if (ret == 0) 1205 (*host)->proto = krbhst_get_default_proto(kd); 1206 return ret; 1207 } 1208 1209 _krb5_debugx(context, 0, "No kpasswd entries found for realm %s", kd->realm); 1210 1211 return KRB5_KDC_UNREACH; 1212} 1213 1214static void 1215krbhost_dealloc(void *ptr) 1216{ 1217 struct krb5_krbhst_data *handle = (struct krb5_krbhst_data *)ptr; 1218 krb5_krbhst_info *h, *next; 1219 1220 for (h = handle->hosts; h != NULL; h = next) { 1221 next = h->next; 1222 _krb5_free_krbhst_info(h); 1223 } 1224 if (handle->hostname) 1225 free(handle->hostname); 1226 1227 HEIMDAL_MUTEX_destroy(&handle->mutex); 1228 1229 free(handle->realm); 1230} 1231 1232static struct krb5_krbhst_data* 1233common_init(krb5_context context, 1234 const char *service, 1235 const char *realm, 1236 int flags) 1237{ 1238 struct krb5_krbhst_data *kd; 1239 1240 if ((kd = heim_uniq_alloc(sizeof(*kd), "krbhst-context", krbhost_dealloc)) == NULL) 1241 return NULL; 1242 1243 if((kd->realm = strdup(realm)) == NULL) { 1244 heim_release(kd); 1245 return NULL; 1246 } 1247 1248 _krb5_debugx(context, 2, "Trying to find service %s for realm %s flags %x", 1249 service, realm, flags); 1250 1251 /* For 'realms' without a . do not even think of going to DNS */ 1252 if (!strchr(realm, '.')) 1253 kd->flags |= KD_CONFIG_EXISTS; 1254 1255 if (flags & KRB5_KRBHST_FLAGS_LARGE_MSG) 1256 kd->flags |= KD_LARGE_MSG; 1257 kd->end = kd->index = &kd->hosts; 1258 1259 HEIMDAL_MUTEX_init(&kd->mutex); 1260 1261 return kd; 1262} 1263 1264/* 1265 * initialize `handle' to look for hosts of type `type' in realm `realm' 1266 */ 1267 1268KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1269krb5_krbhst_init(krb5_context context, 1270 const char *realm, 1271 unsigned int type, 1272 krb5_krbhst_handle *handle) 1273{ 1274 return krb5_krbhst_init_flags(context, realm, type, 0, handle); 1275} 1276 1277KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1278krb5_krbhst_init_flags(krb5_context context, 1279 const char *realm, 1280 unsigned int type, 1281 int flags, 1282 krb5_krbhst_handle *handle) 1283{ 1284 struct krb5_krbhst_data *kd; 1285 krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *, 1286 krb5_krbhst_info **); 1287 int def_port; 1288 const char *service; 1289 1290 *handle = NULL; 1291 1292 switch(type) { 1293 case KRB5_KRBHST_KDC: 1294 next = kdc_get_next; 1295 def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88)); 1296 service = "kdc"; 1297 break; 1298 case KRB5_KRBHST_ADMIN: 1299 next = admin_get_next; 1300 def_port = ntohs(krb5_getportbyname (context, "kerberos-adm", 1301 "tcp", 749)); 1302 service = "admin"; 1303 break; 1304 case KRB5_KRBHST_CHANGEPW: 1305 next = kpasswd_get_next; 1306 def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp", 1307 KPASSWD_PORT)); 1308 service = "change_password"; 1309 break; 1310 default: 1311 krb5_set_error_message(context, ENOTTY, 1312 N_("unknown krbhst type (%u)", ""), type); 1313 return ENOTTY; 1314 } 1315 if((kd = common_init(context, service, realm, flags)) == NULL) 1316 return ENOMEM; 1317 kd->get_next = next; 1318 kd->def_port = def_port; 1319 *handle = kd; 1320 return 0; 1321} 1322 1323/* 1324 * return the next host information from `handle' in `host' 1325 */ 1326 1327KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1328krb5_krbhst_next(krb5_context context, 1329 krb5_krbhst_handle handle, 1330 krb5_krbhst_info **host) 1331{ 1332 if(get_next(handle, host)) 1333 return 0; 1334 1335 return (*handle->get_next)(context, handle, host); 1336} 1337 1338/* 1339 * 1340 */ 1341 1342static void 1343krbhst_callback(void *ctx) 1344{ 1345 krb5_krbhst_info *host = ctx; 1346 krb5_krbhst_handle handle = host->__private; 1347 1348 if (handle->callback) 1349 handle->callback(handle->userctx, host); 1350} 1351 1352static void 1353krbhst_callback_done(void *ctx) 1354{ 1355 krb5_krbhst_handle handle = ctx; 1356 void (*callback)(void *, krb5_krbhst_info *) = handle->callback; 1357 void *userctx = handle->userctx; 1358 1359 /* reset per processing data */ 1360 heim_release(handle->queue); 1361 handle->queue = NULL; 1362 1363 handle->callback = NULL; 1364 handle->userctx = NULL; 1365 1366 if (callback) 1367 callback(userctx, NULL); 1368 1369 heim_release(handle); 1370} 1371 1372static void 1373krbhst_callback_cancel(void *ctx) 1374{ 1375 krb5_krbhst_handle handle = ctx; 1376 handle->callback(handle->userctx, NULL); 1377 handle->callback = NULL; 1378} 1379 1380static void 1381process_loop(void *ctx) 1382{ 1383 krb5_krbhst_handle handle = ctx; 1384 krb5_krbhst_info *hi; 1385 1386 while(krb5_krbhst_next(handle->context, handle, &hi)) { 1387 hi->__private = heim_retain(handle); 1388 heim_async_f(handle->queue, hi, krbhst_callback); 1389 } 1390 1391 heim_async_f(handle->queue, handle, krbhst_callback_done); 1392} 1393 1394/* 1395 * 1396 */ 1397 1398KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1399_krb5_krbhst_async(krb5_context context, 1400 krb5_krbhst_handle handle, 1401 heim_queue_t queue, 1402 void *userctx, 1403 void (*callback)(void *userctx, krb5_krbhst_info *host)) 1404{ 1405 heim_assert(handle->queue == NULL, "krbhst have a outstanding request already"); 1406 1407 handle->queue = heim_retain(queue); 1408 handle->context = context; 1409 1410 if (handle->process_queue == NULL) 1411 handle->process_queue = heim_queue_create("krbhst", NULL); 1412 1413 handle->userctx = userctx; 1414 handle->callback = callback; 1415 1416 heim_async_f(handle->process_queue, heim_retain(handle), process_loop); 1417 1418 return 0; 1419} 1420 1421KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1422_krb5_krbhst_cancel(krb5_context context, 1423 krb5_krbhst_handle handle) 1424{ 1425 heim_assert(handle->process_queue != NULL, "cancel non async krbhst"); 1426 heim_assert(handle->callback != NULL, "cancel on already canceled handle"); 1427 heim_async_f(handle->queue, handle, krbhst_callback_cancel); 1428} 1429 1430/* 1431 * return the next host information from `handle' as a host name 1432 * in `hostname' (or length `hostlen) 1433 */ 1434 1435KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1436krb5_krbhst_next_as_string(krb5_context context, 1437 krb5_krbhst_handle handle, 1438 char *hostname, 1439 size_t hostlen) 1440{ 1441 krb5_error_code ret; 1442 krb5_krbhst_info *host; 1443 ret = krb5_krbhst_next(context, handle, &host); 1444 if(ret) 1445 return ret; 1446 return krb5_krbhst_format_string(context, host, hostname, hostlen); 1447} 1448 1449/* 1450 * 1451 */ 1452 1453krb5_error_code KRB5_LIB_FUNCTION 1454krb5_krbhst_set_hostname(krb5_context context, 1455 krb5_krbhst_handle handle, 1456 const char *hostname) 1457{ 1458 if (handle->hostname) 1459 free(handle->hostname); 1460 handle->hostname = strdup(hostname); 1461 if (handle->hostname == NULL) 1462 return ENOMEM; 1463 return 0; 1464} 1465 1466KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1467krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle) 1468{ 1469 HEIMDAL_MUTEX_lock(&handle->mutex); 1470 handle->index = &handle->hosts; 1471 HEIMDAL_MUTEX_unlock(&handle->mutex); 1472} 1473 1474KRB5_LIB_FUNCTION void KRB5_LIB_CALL 1475krb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle) 1476{ 1477 heim_release(handle); 1478} 1479 1480#ifndef HEIMDAL_SMALLER 1481 1482/* backwards compatibility ahead */ 1483 1484static krb5_error_code 1485gethostlist(krb5_context context, const char *realm, 1486 unsigned int type, char ***hostlist) 1487{ 1488 krb5_error_code ret; 1489 int nhost = 0; 1490 krb5_krbhst_handle handle; 1491 char host[MAXHOSTNAMELEN]; 1492 krb5_krbhst_info *hostinfo; 1493 1494 ret = krb5_krbhst_init(context, realm, type, &handle); 1495 if (ret) 1496 return ret; 1497 1498 while(krb5_krbhst_next(context, handle, &hostinfo) == 0) 1499 nhost++; 1500 if(nhost == 0) { 1501 krb5_set_error_message(context, KRB5_KDC_UNREACH, 1502 N_("No KDC found for realm %s", ""), realm); 1503 return KRB5_KDC_UNREACH; 1504 } 1505 *hostlist = calloc(nhost + 1, sizeof(**hostlist)); 1506 if(*hostlist == NULL) { 1507 krb5_krbhst_free(context, handle); 1508 return ENOMEM; 1509 } 1510 1511 krb5_krbhst_reset(context, handle); 1512 nhost = 0; 1513 while(krb5_krbhst_next_as_string(context, handle, 1514 host, sizeof(host)) == 0) { 1515 if(((*hostlist)[nhost++] = strdup(host)) == NULL) { 1516 krb5_free_krbhst(context, *hostlist); 1517 krb5_krbhst_free(context, handle); 1518 return ENOMEM; 1519 } 1520 } 1521 (*hostlist)[nhost] = NULL; 1522 krb5_krbhst_free(context, handle); 1523 return 0; 1524} 1525 1526/* 1527 * return an malloced list of kadmin-hosts for `realm' in `hostlist' 1528 */ 1529 1530KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1531krb5_get_krb_admin_hst (krb5_context context, 1532 const krb5_realm *realm, 1533 char ***hostlist) 1534{ 1535 return gethostlist(context, *realm, KRB5_KRBHST_ADMIN, hostlist); 1536} 1537 1538/* 1539 * return an malloced list of changepw-hosts for `realm' in `hostlist' 1540 */ 1541 1542KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1543krb5_get_krb_changepw_hst (krb5_context context, 1544 const krb5_realm *realm, 1545 char ***hostlist) 1546{ 1547 return gethostlist(context, *realm, KRB5_KRBHST_CHANGEPW, hostlist); 1548} 1549 1550/* 1551 * return an malloced list of 524-hosts for `realm' in `hostlist' 1552 */ 1553 1554KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1555krb5_get_krb524hst (krb5_context context, 1556 const krb5_realm *realm, 1557 char ***hostlist) 1558{ 1559 return gethostlist(context, *realm, KRB5_KRBHST_KRB524, hostlist); 1560} 1561 1562/* 1563 * return an malloced list of KDC's for `realm' in `hostlist' 1564 */ 1565 1566KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1567krb5_get_krbhst (krb5_context context, 1568 const krb5_realm *realm, 1569 char ***hostlist) 1570{ 1571 return gethostlist(context, *realm, KRB5_KRBHST_KDC, hostlist); 1572} 1573 1574/* 1575 * free all the memory allocated in `hostlist' 1576 */ 1577 1578KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1579krb5_free_krbhst (krb5_context context, 1580 char **hostlist) 1581{ 1582 char **p; 1583 1584 for (p = hostlist; *p; ++p) 1585 free (*p); 1586 free (hostlist); 1587 return 0; 1588} 1589 1590#endif /* HEIMDAL_SMALLER */ 1591