1 /* 2 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 3 */ 4 5 /* 6 * lib/krb5/os/locate_kdc.c 7 * 8 * Copyright 1990,2000,2001,2002,2003,2004,2006 Massachusetts Institute of Technology. 9 * All Rights Reserved. 10 * 11 * Export of this software from the United States of America may 12 * require a specific license from the United States Government. 13 * It is the responsibility of any person or organization contemplating 14 * export to obtain such a license before exporting. 15 * 16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 17 * distribute this software and its documentation for any purpose and 18 * without fee is hereby granted, provided that the above copyright 19 * notice appear in all copies and that both that copyright notice and 20 * this permission notice appear in supporting documentation, and that 21 * the name of M.I.T. not be used in advertising or publicity pertaining 22 * to distribution of the software without specific, written prior 23 * permission. Furthermore if you modify this software you must label 24 * your software as modified software and not distribute it in such a 25 * fashion that it might be confused with the original M.I.T. software. 26 * M.I.T. makes no representations about the suitability of 27 * this software for any purpose. It is provided "as is" without express 28 * or implied warranty. 29 * 30 * 31 * get socket addresses for KDC. 32 */ 33 34 /* 35 * Solaris Kerberos 36 * Re-factored the following routines to get a clear separation of locating 37 * KDC entries (krb5.conf/DNS-SRVrecs) versus mapping them to net addresses 38 * to allow us to output better error msgs: 39 * krb5int_locate_server 40 * prof_locate_server 41 * dns_locate_server 42 * krb5_locate_srv_conf_1 (removed) 43 * krb5_locate_srv_dns_1 (removed) 44 * prof_hostnames2netaddrs (new) 45 * hostlist2str (new) 46 * dns_hostnames2netaddrs (new) 47 * dnslist2str (new) 48 * Also, for the profile get_master==1 case, the algorithm has been 49 * simplified to just do a profile_get_values on "admin_server" and 50 * not try to match those against "kdc" entries (does not seem necessary 51 * and the DNS-SRVrecs code does not do that). 52 */ 53 54 #include "fake-addrinfo.h" 55 #include "k5-int.h" 56 #include "os-proto.h" 57 #include <stdio.h> 58 #ifdef KRB5_DNS_LOOKUP 59 #ifdef WSHELPER 60 #include <wshelper.h> 61 #else /* WSHELPER */ 62 #include <netinet/in.h> 63 #include <arpa/inet.h> 64 #include <arpa/nameser.h> 65 #include <resolv.h> 66 #include <netdb.h> 67 #endif /* WSHELPER */ 68 #ifndef T_SRV 69 #define T_SRV 33 70 #endif /* T_SRV */ 71 #include <syslog.h> 72 #include <locale.h> 73 74 /* for old Unixes and friends ... */ 75 #ifndef MAXHOSTNAMELEN 76 #define MAXHOSTNAMELEN 64 77 #endif 78 79 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1) 80 81 /* Solaris Kerberos: default to dns lookup for the KDC but not the realm */ 82 #define DEFAULT_LOOKUP_KDC 1 83 #define DEFAULT_LOOKUP_REALM 0 84 85 static int 86 maybe_use_dns (krb5_context context, const char *name, int defalt) 87 { 88 krb5_error_code code; 89 char * value = NULL; 90 int use_dns = 0; 91 92 code = profile_get_string(context->profile, "libdefaults", 93 name, 0, 0, &value); 94 if (value == 0 && code == 0) 95 code = profile_get_string(context->profile, "libdefaults", 96 "dns_fallback", 0, 0, &value); 97 if (code) 98 return defalt; 99 100 if (value == 0) 101 return defalt; 102 103 use_dns = _krb5_conf_boolean(value); 104 profile_release_string(value); 105 return use_dns; 106 } 107 108 int 109 _krb5_use_dns_kdc(krb5_context context) 110 { 111 return maybe_use_dns (context, "dns_lookup_kdc", DEFAULT_LOOKUP_KDC); 112 } 113 114 int 115 _krb5_use_dns_realm(krb5_context context) 116 { 117 return maybe_use_dns (context, "dns_lookup_realm", DEFAULT_LOOKUP_REALM); 118 } 119 120 #endif /* KRB5_DNS_LOOKUP */ 121 122 int 123 krb5int_grow_addrlist (struct addrlist *lp, int nmore) 124 { 125 int i; 126 int newspace = lp->space + nmore; 127 size_t newsize = newspace * sizeof (*lp->addrs); 128 void *newaddrs; 129 130 newaddrs = realloc (lp->addrs, newsize); 131 if (newaddrs == NULL) 132 return errno; 133 lp->addrs = newaddrs; 134 for (i = lp->space; i < newspace; i++) { 135 lp->addrs[i].ai = NULL; 136 lp->addrs[i].freefn = NULL; 137 lp->addrs[i].data = NULL; 138 } 139 lp->space = newspace; 140 return 0; 141 } 142 #define grow_list krb5int_grow_addrlist 143 144 /* Free up everything pointed to by the addrlist structure, but don't 145 free the structure itself. */ 146 void 147 krb5int_free_addrlist (struct addrlist *lp) 148 { 149 int i; 150 for (i = 0; i < lp->naddrs; i++) 151 if (lp->addrs[i].freefn) 152 (lp->addrs[i].freefn)(lp->addrs[i].data); 153 free (lp->addrs); 154 lp->addrs = NULL; 155 lp->naddrs = lp->space = 0; 156 } 157 #define free_list krb5int_free_addrlist 158 159 static int translate_ai_error (int err) 160 { 161 switch (err) { 162 case 0: 163 return 0; 164 case EAI_BADFLAGS: 165 case EAI_FAMILY: 166 case EAI_SOCKTYPE: 167 case EAI_SERVICE: 168 /* All of these indicate bad inputs to getaddrinfo. */ 169 return EINVAL; 170 case EAI_AGAIN: 171 /* Translate to standard errno code. */ 172 return EAGAIN; 173 case EAI_MEMORY: 174 /* Translate to standard errno code. */ 175 return ENOMEM; 176 #ifdef EAI_ADDRFAMILY 177 case EAI_ADDRFAMILY: 178 #endif 179 #if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME 180 case EAI_NODATA: 181 #endif 182 case EAI_NONAME: 183 /* Name not known or no address data, but no error. Do 184 nothing more. */ 185 return 0; 186 #ifdef EAI_OVERFLOW 187 case EAI_OVERFLOW: 188 /* An argument buffer overflowed. */ 189 return EINVAL; /* XXX */ 190 #endif 191 #ifdef EAI_SYSTEM 192 case EAI_SYSTEM: 193 /* System error, obviously. */ 194 return errno; 195 #endif 196 default: 197 /* An error code we haven't handled? */ 198 return EINVAL; 199 } 200 } 201 202 /* Solaris Kerberos: want dbg messages to syslog */ 203 #include <stdarg.h> 204 static inline void Tprintf(const char *fmt, ...) 205 { 206 #ifdef TEST 207 va_list ap; 208 char err_str[2048]; 209 210 va_start(ap, fmt); 211 vsnprintf(err_str, sizeof (err_str), fmt, args); 212 syslog(LOG_DEBUG, err_str); 213 va_end(ap); 214 #endif 215 } 216 217 #if 0 218 extern void krb5int_debug_fprint(const char *, ...); 219 #define dprint krb5int_debug_fprint 220 #define print_addrlist krb5int_print_addrlist 221 extern void print_addrlist (const struct addrlist *a); 222 #else 223 static inline void dprint(const char *fmt, ...) { } 224 static inline void print_addrlist(const struct addrlist *a) { } 225 #endif 226 227 static int add_addrinfo_to_list (struct addrlist *lp, struct addrinfo *a, 228 void (*freefn)(void *), void *data) 229 { 230 int err; 231 232 dprint("\tadding %p=%A to %p (naddrs=%d space=%d)\n", a, a, lp, 233 lp->naddrs, lp->space); 234 235 if (lp->naddrs == lp->space) { 236 err = grow_list (lp, 1); 237 if (err) { 238 Tprintf ("grow_list failed %d\n", err); 239 return err; 240 } 241 } 242 Tprintf("setting element %d\n", lp->naddrs); 243 lp->addrs[lp->naddrs].ai = a; 244 lp->addrs[lp->naddrs].freefn = freefn; 245 lp->addrs[lp->naddrs].data = data; 246 lp->naddrs++; 247 Tprintf ("\tcount is now %d: ", lp->naddrs); 248 print_addrlist(lp); 249 Tprintf("\n"); 250 return 0; 251 } 252 253 #define add_host_to_list krb5int_add_host_to_list 254 255 static void call_freeaddrinfo(void *data) 256 { 257 /* Strict interpretation of the C standard says we can't assume 258 that the ABI for f(void*) and f(struct foo *) will be 259 compatible. Use this stub just to be paranoid. */ 260 freeaddrinfo(data); 261 } 262 263 int 264 krb5int_add_host_to_list (struct addrlist *lp, const char *hostname, 265 int port, int secport, 266 int socktype, int family) 267 { 268 struct addrinfo *addrs, *a, *anext, hint; 269 int err; 270 char portbuf[10], secportbuf[10]; 271 void (*freefn)(void *); 272 273 Tprintf ("adding hostname %s, ports %d,%d, family %d, socktype %d\n", 274 hostname, ntohs (port), ntohs (secport), 275 family, socktype); 276 277 memset(&hint, 0, sizeof(hint)); 278 hint.ai_family = family; 279 hint.ai_socktype = socktype; 280 #ifdef AI_NUMERICSERV 281 hint.ai_flags = AI_NUMERICSERV; 282 #endif 283 sprintf(portbuf, "%d", ntohs(port)); 284 sprintf(secportbuf, "%d", ntohs(secport)); 285 err = getaddrinfo (hostname, portbuf, &hint, &addrs); 286 if (err) { 287 Tprintf ("\tgetaddrinfo(\"%s\", \"%s\", ...)\n\treturns %d: %s\n", 288 hostname, portbuf, err, gai_strerror (err)); 289 return translate_ai_error (err); 290 } 291 freefn = call_freeaddrinfo; 292 anext = 0; 293 for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) { 294 anext = a->ai_next; 295 err = add_addrinfo_to_list (lp, a, freefn, a); 296 } 297 if (err || secport == 0) 298 goto egress; 299 if (socktype == 0) 300 socktype = SOCK_DGRAM; 301 else if (socktype != SOCK_DGRAM) 302 goto egress; 303 hint.ai_family = AF_INET; 304 err = getaddrinfo (hostname, secportbuf, &hint, &addrs); 305 if (err) { 306 err = translate_ai_error (err); 307 goto egress; 308 } 309 freefn = call_freeaddrinfo; 310 for (a = addrs; a != 0 && err == 0; a = anext, freefn = 0) { 311 anext = a->ai_next; 312 err = add_addrinfo_to_list (lp, a, freefn, a); 313 } 314 egress: 315 /* Solaris Kerberos */ 316 if (anext) 317 freeaddrinfo (anext); 318 return err; 319 } 320 321 #include <locate_plugin.h> 322 323 #if TARGET_OS_MAC 324 static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/libkrb5", NULL }; /* should be a list */ 325 #else 326 static const char *objdirs[] = { LIBDIR "/krb5/plugins/libkrb5", NULL }; 327 #endif 328 329 struct module_callback_data { 330 int out_of_mem; 331 struct addrlist *lp; 332 }; 333 334 static int 335 module_callback (void *cbdata, int socktype, struct sockaddr *sa) 336 { 337 struct module_callback_data *d = cbdata; 338 struct { 339 struct addrinfo ai; 340 union { 341 struct sockaddr_in sin; 342 struct sockaddr_in6 sin6; 343 } u; 344 } *x; 345 346 if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM) 347 return 0; 348 if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) 349 return 0; 350 x = malloc (sizeof (*x)); 351 if (x == 0) { 352 d->out_of_mem = 1; 353 return 1; 354 } 355 memset(x, 0, sizeof (*x)); 356 x->ai.ai_addr = (struct sockaddr *) &x->u; 357 x->ai.ai_socktype = socktype; 358 x->ai.ai_family = sa->sa_family; 359 if (sa->sa_family == AF_INET) { 360 x->u.sin = *(struct sockaddr_in *)sa; 361 x->ai.ai_addrlen = sizeof(struct sockaddr_in); 362 } 363 if (sa->sa_family == AF_INET6) { 364 x->u.sin6 = *(struct sockaddr_in6 *)sa; 365 x->ai.ai_addrlen = sizeof(struct sockaddr_in6); 366 } 367 if (add_addrinfo_to_list (d->lp, &x->ai, free, x) != 0) { 368 /* Assumes only error is ENOMEM. */ 369 d->out_of_mem = 1; 370 return 1; 371 } 372 return 0; 373 } 374 375 static krb5_error_code 376 module_locate_server (krb5_context ctx, const krb5_data *realm, 377 struct addrlist *addrlist, 378 enum locate_service_type svc, int socktype, int family) 379 { 380 struct krb5plugin_service_locate_result *res = NULL; 381 krb5_error_code code; 382 struct krb5plugin_service_locate_ftable *vtbl = NULL; 383 void **ptrs; 384 int i; 385 struct module_callback_data cbdata = { 0, }; 386 387 Tprintf("in module_locate_server\n"); 388 cbdata.lp = addrlist; 389 if (!PLUGIN_DIR_OPEN (&ctx->libkrb5_plugins)) { 390 391 code = krb5int_open_plugin_dirs (objdirs, NULL, &ctx->libkrb5_plugins, 392 &ctx->err); 393 if (code) 394 return KRB5_PLUGIN_NO_HANDLE; 395 } 396 397 code = krb5int_get_plugin_dir_data (&ctx->libkrb5_plugins, 398 "service_locator", &ptrs, &ctx->err); 399 if (code) { 400 Tprintf("error looking up plugin symbols: %s\n", 401 krb5_get_error_message(ctx, code)); 402 return KRB5_PLUGIN_NO_HANDLE; 403 } 404 405 for (i = 0; ptrs[i]; i++) { 406 void *blob; 407 408 vtbl = ptrs[i]; 409 Tprintf("element %d is %p\n", i, ptrs[i]); 410 411 /* For now, don't keep the plugin data alive. For long-lived 412 contexts, it may be desirable to change that later. */ 413 code = vtbl->init(ctx, &blob); 414 if (code) 415 continue; 416 417 code = vtbl->lookup(blob, svc, realm->data, socktype, family, 418 module_callback, &cbdata); 419 vtbl->fini(blob); 420 if (code == KRB5_PLUGIN_NO_HANDLE) { 421 /* Module passes, keep going. */ 422 /* XXX */ 423 Tprintf("plugin doesn't handle this realm (KRB5_PLUGIN_NO_HANDLE)\n"); 424 continue; 425 } 426 if (code != 0) { 427 /* Module encountered an actual error. */ 428 Tprintf("plugin lookup routine returned error %d: %s\n", 429 code, error_message(code)); 430 krb5int_free_plugin_dir_data (ptrs); 431 return code; 432 } 433 break; 434 } 435 if (ptrs[i] == NULL) { 436 Tprintf("ran off end of plugin list\n"); 437 krb5int_free_plugin_dir_data (ptrs); 438 return KRB5_PLUGIN_NO_HANDLE; 439 } 440 Tprintf("stopped with plugin #%d, res=%p\n", i, res); 441 442 /* Got something back, yippee. */ 443 Tprintf("now have %d addrs in list %p\n", addrlist->naddrs, addrlist); 444 print_addrlist(addrlist); 445 krb5int_free_plugin_dir_data (ptrs); 446 return 0; 447 } 448 449 static krb5_error_code 450 prof_locate_server (krb5_context context, const krb5_data *realm, 451 char ***hostlist, 452 enum locate_service_type svc) 453 { 454 const char *realm_srv_names[4]; 455 char **hl, *host, *profname; 456 krb5_error_code code; 457 int i, j, count; 458 459 *hostlist = NULL; /* default - indicate no KDCs found */ 460 461 switch (svc) { 462 case locate_service_kdc: 463 profname = "kdc"; 464 break; 465 case locate_service_master_kdc: 466 profname = "master_kdc"; 467 break; 468 case locate_service_kadmin: 469 profname = "admin_server"; 470 break; 471 case locate_service_krb524: 472 profname = "krb524_server"; 473 break; 474 case locate_service_kpasswd: 475 profname = "kpasswd_server"; 476 break; 477 default: 478 return EINVAL; 479 } 480 481 if ((host = malloc(realm->length + 1)) == NULL) 482 return ENOMEM; 483 484 (void) strncpy(host, realm->data, realm->length); 485 host[realm->length] = '\0'; 486 hl = 0; 487 488 realm_srv_names[0] = "realms"; 489 realm_srv_names[1] = host; 490 realm_srv_names[2] = profname; 491 realm_srv_names[3] = 0; 492 493 code = profile_get_values(context->profile, realm_srv_names, &hl); 494 if (code) { 495 Tprintf ("config file lookup failed: %s\n", 496 error_message(code)); 497 if (code == PROF_NO_SECTION || code == PROF_NO_RELATION) 498 code = KRB5_REALM_UNKNOWN; 499 krb5_xfree(host); 500 return code; 501 } 502 krb5_xfree(host); 503 504 *hostlist = hl; 505 506 return 0; 507 } 508 509 static krb5_error_code 510 dns_locate_server (krb5_context context, const krb5_data *realm, 511 struct srv_dns_entry **dns_list_head, 512 enum locate_service_type svc, int socktype, int family) 513 { 514 const char *dnsname; 515 int use_dns = _krb5_use_dns_kdc(context); 516 krb5_error_code code; 517 struct srv_dns_entry *head = NULL; 518 519 *dns_list_head = NULL; /* default: indicate we have found no KDCs */ 520 521 if (!use_dns) 522 return KRB5_PLUGIN_NO_HANDLE; 523 524 switch (svc) { 525 case locate_service_kdc: 526 dnsname = "_kerberos"; 527 break; 528 case locate_service_master_kdc: 529 dnsname = "_kerberos-master"; 530 break; 531 case locate_service_kadmin: 532 dnsname = "_kerberos-adm"; 533 break; 534 case locate_service_krb524: 535 dnsname = "_krb524"; 536 break; 537 case locate_service_kpasswd: 538 dnsname = "_kpasswd"; 539 break; 540 default: 541 return KRB5_PLUGIN_NO_HANDLE; 542 } 543 544 code = 0; 545 if (socktype == SOCK_DGRAM || socktype == 0) { 546 code = krb5int_make_srv_query_realm(realm, dnsname, "_udp", &head); 547 if (code) 548 Tprintf("dns udp lookup returned error %d\n", code); 549 } 550 if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) { 551 code = krb5int_make_srv_query_realm(realm, dnsname, "_tcp", &head); 552 if (code) 553 Tprintf("dns tcp lookup returned error %d\n", code); 554 } 555 556 if (head == NULL) 557 return 0; 558 559 /* Check for the "." case indicating no support. */ 560 if (head->next == 0 && head->host[0] == 0) { 561 free(head->host); 562 free(head); 563 return KRB5_ERR_NO_SERVICE; 564 } 565 566 /* 567 * Okay! Now we've got a linked list of entries sorted by 568 * priority. Return it so later we can map hostnames to net addresses. 569 */ 570 *dns_list_head = head; 571 572 return 0; 573 } 574 575 /* 576 * Given the list of hostnames of KDCs found in DNS SRV recs, lets go 577 * thru NSS (name svc switch) to get the net addrs. 578 */ 579 static krb5_error_code 580 dns_hostnames2netaddrs( 581 struct srv_dns_entry *head, 582 enum locate_service_type svc, 583 int socktype, 584 int family, 585 struct addrlist *addrlist) 586 { 587 struct srv_dns_entry *entry = NULL, *next; 588 krb5_error_code code; 589 590 Tprintf ("walking answer list:\n"); 591 for (entry = head; entry != NULL; entry = entry->next) { 592 code = 0; 593 if (socktype) 594 code = add_host_to_list (addrlist, entry->host, 595 htons (entry->port), 0, 596 socktype, family); 597 else { 598 (void) add_host_to_list (addrlist, entry->host, 599 htons (entry->port), 0, 600 SOCK_DGRAM, family); 601 602 code = add_host_to_list (addrlist, entry->host, 603 htons (entry->port), 0, 604 SOCK_STREAM, family); 605 } 606 if (code) { 607 Tprintf(" fail add_host code=%d %s\n", code, entry->host); 608 } 609 } 610 Tprintf ("[end]\n"); 611 612 return code; 613 } 614 615 /* 616 * Given the DNS SRV recs list, return a string of all the hosts like so: 617 * "fqdn0[,fqdn1][,fqdnN]" 618 */ 619 static char * 620 dnslist2str(struct srv_dns_entry *dns_list_head) 621 { 622 struct srv_dns_entry *head = dns_list_head; 623 struct srv_dns_entry *entry = NULL, *next; 624 unsigned int size = 0, c = 0, buf_size; 625 char *s = NULL; 626 627 for (entry = head; entry; entry = entry->next, c++) { 628 size += strlen(entry->host); 629 } 630 if (!c) 631 return NULL; 632 633 /* hostnames + commas + NULL */ 634 buf_size = size + (c - 1) + 1; 635 s = malloc(buf_size); 636 if (!s) 637 return NULL; 638 639 (void) strlcpy(s, head->host, buf_size); 640 for (entry = head->next; entry; entry = entry->next) { 641 (void) strlcat(s, ",", buf_size); 642 (void) strlcat(s, entry->host, buf_size); 643 } 644 645 return s; 646 } 647 648 /* 649 * Given the profile hostlist, return a string of all the hosts like so: 650 * "fqdn0[,fqdn1][,fqdnN]" 651 */ 652 static char * 653 hostlist2str(char **hostlist) 654 { 655 unsigned int c = 0, size = 0, buf_size; 656 char **hl = hostlist, *s = NULL; 657 658 while (hl && *hl) { 659 size += strlen(*hl); 660 hl++; 661 c++; 662 } 663 if (!c) 664 return NULL; 665 666 /* hostnames + commas + NULL */ 667 buf_size = size + (c - 1) + 1; 668 s = malloc(buf_size); 669 if (!s) 670 return NULL; 671 672 hl = hostlist; 673 (void) strlcpy(s, *hl, buf_size); 674 hl++; 675 while (hl && *hl) { 676 (void) strlcat(s, ",", buf_size); 677 (void) strlcat(s, *hl, buf_size); 678 hl++; 679 } 680 681 return s; 682 } 683 684 /* 685 * Take the profile KDC list and return a list of net addrs. 686 */ 687 static krb5_error_code 688 prof_hostnames2netaddrs( 689 char **hostlist, 690 enum locate_service_type svc, 691 int socktype, 692 int family, 693 struct addrlist *addrlist) /* output */ 694 { 695 int udpport = 0 , sec_udpport = 0; 696 int code, i; 697 struct servent *serv; 698 699 int count = 0; 700 while (hostlist && hostlist[count]) 701 count++; 702 if (count == 0) { 703 return 0; 704 } 705 706 switch (svc) { 707 case locate_service_kdc: 708 case locate_service_master_kdc: 709 /* We used to use /etc/services for these, but enough systems 710 have old, crufty, wrong settings that this is probably 711 better. */ 712 udpport = htons(KRB5_DEFAULT_PORT); 713 sec_udpport = htons(KRB5_DEFAULT_SEC_PORT); 714 break; 715 case locate_service_kadmin: 716 udpport = htons(DEFAULT_KADM5_PORT); 717 break; 718 case locate_service_krb524: 719 serv = getservbyname(KRB524_SERVICE, "udp"); 720 udpport = serv ? serv->s_port : htons (KRB524_PORT); 721 break; 722 case locate_service_kpasswd: 723 udpport = htons(DEFAULT_KPASSWD_PORT); 724 break; 725 default: 726 return EINVAL; 727 } 728 729 for (i=0; hostlist[i]; i++) { 730 int p1, p2; 731 char *cp, *port, *host; 732 733 host = hostlist[i]; 734 /* 735 * Strip off excess whitespace 736 */ 737 cp = strchr(host, ' '); 738 if (cp) 739 *cp = 0; 740 cp = strchr(host, '\t'); 741 if (cp) 742 *cp = 0; 743 port = strchr(host, ':'); 744 if (port) { 745 *port = 0; 746 port++; 747 } 748 749 if (port) { 750 unsigned long l; 751 #ifdef HAVE_STROUL 752 char *endptr; 753 l = strtoul (port, &endptr, 10); 754 if (endptr == NULL || *endptr != 0) 755 return EINVAL; 756 #else 757 l = atoi (port); 758 #endif 759 /* L is unsigned, don't need to check <0. */ 760 if (l == 0 || l > 65535) 761 return EINVAL; 762 p1 = htons (l); 763 p2 = 0; 764 } else { 765 p1 = udpport; 766 p2 = sec_udpport; 767 } 768 769 770 if (socktype != 0) { 771 code = add_host_to_list (addrlist, hostlist[i], p1, p2, 772 socktype, family); 773 } else { 774 code = add_host_to_list (addrlist, hostlist[i], p1, p2, 775 SOCK_DGRAM, family); 776 if (code == 0) 777 code = add_host_to_list (addrlist, hostlist[i], p1, p2, 778 SOCK_STREAM, family); 779 } 780 } 781 782 return code; 783 } 784 785 /* 786 * Wrapper function for the various backends 787 */ 788 789 krb5_error_code 790 krb5int_locate_server (krb5_context context, const krb5_data *realm, 791 struct addrlist *addrlist, 792 enum locate_service_type svc, 793 int socktype, int family) 794 { 795 krb5_error_code code; 796 struct addrlist al = ADDRLIST_INIT; 797 char **hostlist = NULL; 798 struct srv_dns_entry *dns_list_head = NULL; 799 800 *addrlist = al; 801 802 code = module_locate_server(context, realm, &al, svc, socktype, family); 803 Tprintf("module_locate_server returns %d\n", code); 804 if (code == KRB5_PLUGIN_NO_HANDLE) { 805 /* 806 * We always try the local file before DNS. Note that there 807 * is no way to indicate "service not available" via the 808 * config file. 809 */ 810 code = prof_locate_server(context, realm, &hostlist, svc); 811 812 /* 813 * Solaris Kerberos: 814 * If kpasswd_server has not been configured and dns_lookup_kdc - 815 * dns_fallback are not configured then admin_server should 816 * be inferred, per krb5.conf(4). 817 */ 818 if (code && svc == locate_service_kpasswd && 819 !maybe_use_dns(context, "dns_lookup_kdc", 0)) { 820 code = prof_locate_server(context, realm, &hostlist, 821 locate_service_kadmin); 822 } 823 824 #ifdef KRB5_DNS_LOOKUP 825 /* 826 * Solaris Kerberos: 827 * There is no point in trying to locate the KDC in DNS if "realm" 828 * is empty. 829 */ 830 /* Try DNS for all profile errors? */ 831 if (code && !krb5_is_referral_realm(realm)) { 832 krb5_error_code code2; 833 code2 = dns_locate_server(context, realm, &dns_list_head, 834 svc, socktype, family); 835 836 if (code2 != KRB5_PLUGIN_NO_HANDLE) 837 code = code2; 838 } 839 #endif /* KRB5_DNS_LOOKUP */ 840 841 /* We could put more heuristics here, like looking up a hostname 842 of "kerberos."+REALM, etc. */ 843 } 844 845 if (code != 0) { 846 if (al.space) 847 free_list (&al); 848 if (hostlist) 849 profile_free_list(hostlist); 850 if (dns_list_head) 851 krb5int_free_srv_dns_data(dns_list_head); 852 853 return code; 854 } 855 856 /* 857 * At this point we have no errors, let's check to see if we have 858 * any KDC entries from krb5.conf or DNS. 859 */ 860 if (!hostlist && !dns_list_head) { 861 switch(svc) { 862 case locate_service_master_kdc: 863 krb5_set_error_message(context, 864 KRB5_REALM_CANT_RESOLVE, 865 dgettext(TEXT_DOMAIN, 866 "Cannot find a master KDC entry in krb5.conf(4) or DNS Service Location records for realm '%.*s'"), 867 realm->length, realm->data); 868 break; 869 case locate_service_kadmin: 870 krb5_set_error_message(context, 871 KRB5_REALM_CANT_RESOLVE, 872 dgettext(TEXT_DOMAIN, 873 "Cannot find a kadmin KDC entry in krb5.conf(4) or DNS Service Location records for realm '%.*s'"), 874 realm->length, realm->data); 875 break; 876 case locate_service_kpasswd: 877 krb5_set_error_message(context, 878 KRB5_REALM_CANT_RESOLVE, 879 dgettext(TEXT_DOMAIN, 880 "Cannot find a kpasswd KDC entry in krb5.conf(4) or DNS Service Location records for realm '%.*s'"), 881 realm->length, realm->data); 882 break; 883 default: /* locate_service_kdc: */ 884 krb5_set_error_message(context, 885 KRB5_REALM_CANT_RESOLVE, 886 dgettext(TEXT_DOMAIN, 887 "Cannot find any KDC entries in krb5.conf(4) or DNS Service Location records for realm '%.*s'"), 888 realm->length, realm->data); 889 890 } 891 return KRB5_REALM_CANT_RESOLVE; 892 } 893 894 /* We have KDC entries, let see if we can get their net addrs. */ 895 if (hostlist) 896 code = prof_hostnames2netaddrs(hostlist, svc, 897 socktype, family, &al); 898 else if (dns_list_head) 899 code = dns_hostnames2netaddrs(dns_list_head, svc, 900 socktype, family, &al); 901 if (code) { 902 if (hostlist) 903 profile_free_list(hostlist); 904 if (dns_list_head) 905 krb5int_free_srv_dns_data(dns_list_head); 906 return code; 907 } 908 909 /* 910 * Solaris Kerberos: 911 * If an entry for _kerberos-master. does not exist (checked for 912 * above) but _kpasswd. does then treat that as an entry for the 913 * master KDC (but use port 88 not the kpasswd port). MS AD creates 914 * kpasswd entries by default in DNS. 915 */ 916 if (!dns_list_head && svc == locate_service_master_kdc && 917 al.naddrs == 0) { 918 919 /* Look for _kpasswd._tcp|udp */ 920 code = dns_locate_server(context, realm, &dns_list_head, 921 locate_service_kpasswd, socktype, family); 922 923 if (code == 0 && dns_list_head) { 924 int i; 925 struct addrinfo *a; 926 927 code = dns_hostnames2netaddrs(dns_list_head, svc, 928 socktype, family, &al); 929 930 /* Set the port to 88 instead of the kpasswd port */ 931 if (code == 0 && al.naddrs > 0) { 932 for (i = 0; i < al.naddrs; i++) { 933 if (al.addrs[i].ai->ai_family == AF_INET) 934 for (a = al.addrs[i].ai; a != NULL; a = a->ai_next) 935 ((struct sockaddr_in *)a->ai_addr)->sin_port = 936 htons(KRB5_DEFAULT_PORT); 937 938 if (al.addrs[i].ai->ai_family == AF_INET6) 939 for (a = al.addrs[i].ai; a != NULL; a = a->ai_next) 940 ((struct sockaddr_in6 *)a->ai_addr)->sin6_port = 941 htons(KRB5_DEFAULT_PORT); 942 } 943 } 944 } 945 } 946 947 /* No errors so far, lets see if we have KDC net addrs */ 948 if (al.naddrs == 0) { 949 char *hostlist_str = NULL, *dnslist_str = NULL; 950 if (al.space) 951 free_list (&al); 952 953 if (hostlist) { 954 hostlist_str = hostlist2str(hostlist); 955 krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE, 956 dgettext(TEXT_DOMAIN, 957 "Cannot resolve network address for KDCs '%s' specified in krb5.conf(4) for realm %.*s"), 958 hostlist_str ? hostlist_str : "unknown", 959 realm->length, realm->data); 960 if (hostlist_str) 961 free(hostlist_str); 962 } else if (dns_list_head) { 963 dnslist_str = dnslist2str(dns_list_head); 964 krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE, 965 dgettext(TEXT_DOMAIN, 966 "Cannot resolve network address for KDCs '%s' discovered via DNS Service Location records for realm '%.*s'"), 967 dnslist_str ? dnslist_str : "unknown", 968 realm->length, realm->data); 969 if (dnslist_str) 970 free(dnslist_str); 971 } 972 973 if (hostlist) 974 profile_free_list(hostlist); 975 if (dns_list_head) 976 krb5int_free_srv_dns_data(dns_list_head); 977 978 return KRB5_REALM_CANT_RESOLVE; 979 } 980 981 if (hostlist) 982 profile_free_list(hostlist); 983 if (dns_list_head) 984 krb5int_free_srv_dns_data(dns_list_head); 985 986 *addrlist = al; 987 return 0; 988 } 989 990 krb5_error_code 991 krb5_locate_kdc(krb5_context context, const krb5_data *realm, 992 struct addrlist *addrlist, 993 int get_masters, int socktype, int family) 994 { 995 return krb5int_locate_server(context, realm, addrlist, 996 (get_masters 997 ? locate_service_master_kdc 998 : locate_service_kdc), 999 socktype, family); 1000 } 1001 1002 /* 1003 * Solaris Kerberos: for backward compat. Avoid using this 1004 * function! 1005 */ 1006 krb5_error_code 1007 krb5_get_servername(krb5_context context, 1008 const krb5_data *realm, 1009 const char *name, const char *proto, 1010 char *srvhost, 1011 unsigned short *port) 1012 { 1013 krb5_error_code code = KRB5_REALM_UNKNOWN; 1014 1015 #ifdef KRB5_DNS_LOOKUP 1016 { 1017 int use_dns = _krb5_use_dns_kdc(context); 1018 1019 if (use_dns) { 1020 struct srv_dns_entry *head = NULL; 1021 1022 code = krb5int_make_srv_query_realm(realm, name, proto, &head); 1023 if (code) 1024 return (code); 1025 1026 if (head == NULL) 1027 return KRB5_REALM_CANT_RESOLVE; 1028 1029 *port = head->port; 1030 (void) strlcpy(srvhost, head->host, MAX_DNS_NAMELEN); 1031 1032 #ifdef DEBUG 1033 fprintf (stderr, "krb5_get_servername svrhost %s, port %d\n", 1034 srvhost, *port); 1035 #endif 1036 krb5int_free_srv_dns_data(head); 1037 } 1038 } 1039 #endif /* KRB5_DNS_LOOKUP */ 1040 1041 return (code); 1042 } 1043