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