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