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