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