1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Active Directory Auto-Discovery. 29 * 30 * This [project private] API allows the caller to provide whatever 31 * details it knows a priori (i.e., provided via configuration so as to 32 * override auto-discovery) and in any order. Then the caller can ask 33 * for any of the auto-discoverable parameters in any order. 34 * 35 * But there is an actual order in which discovery must be done. Given 36 * the discovery mechanism implemented here, that order is: 37 * 38 * - the domain name joined must be discovered first 39 * - then the domain controllers 40 * - then the forest name and site name 41 * - then the global catalog servers, and site-specific domain 42 * controllers and global catalog servers. 43 * 44 * The API does not require it be called in the same order because there 45 * may be other discovery mechanisms in the future, and exposing 46 * ordering requirements of the current mechanism now can create trouble 47 * down the line. Also, this makes the API easier to use now, which 48 * means less work to do some day when we make this a public API. 49 * 50 * Domain discovery is done by res_nsearch() of the DNS SRV RR name for 51 * domain controllers. As long as the joined domain appears in the DNS 52 * resolver's search list then we'll find it. 53 * 54 * Domain controller discovery is a matter of formatting the DNS SRV RR 55 * FQDN for domain controllers and doing a lookup for them. Knowledge 56 * of the domain name is not fundamentally required, but we separate the 57 * two processes, which in practice can lead to one more DNS lookup than 58 * is strictly required. 59 * 60 * Forest and site name discovery require an LDAP search of the AD 61 * "configuration partition" at a domain controller for the joined 62 * domain. Forest and site name discovery depend on knowing the joined 63 * domain name and domain controllers for that domain. 64 * 65 * Global catalog server discovery requires knowledge of the forest 66 * name in order to format the DNS SRV RR FQDN to lookup. Site-specific 67 * domain controller discovery depends on knowing the site name (and, 68 * therefore, joined domain, ...). Site-specific global catalog server 69 * discovery depends on knowledge of the forest and site names, which 70 * depend on... 71 * 72 * All the work of discovering particular items is done by functions 73 * named validate_<item>(). Each such function calls validate_<item>() 74 * for any items that it depends on. 75 * 76 * This API is not thread-safe. 77 */ 78 79 80 #include <stdio.h> 81 #include <string.h> 82 #include <strings.h> 83 #include <unistd.h> 84 #include <assert.h> 85 #include <stdlib.h> 86 #include <net/if.h> 87 #include <net/if.h> 88 #include <sys/types.h> 89 #include <sys/socket.h> 90 #include <sys/sockio.h> 91 #include <netinet/in.h> 92 #include <netinet/in.h> 93 #include <arpa/inet.h> 94 #include <arpa/nameser.h> 95 #include <resolv.h> 96 #include <netdb.h> 97 #include <ctype.h> 98 #include <errno.h> 99 #include <ldap.h> 100 #include <sasl/sasl.h> 101 #include <sys/u8_textprep.h> 102 #include <syslog.h> 103 #include "adutils_impl.h" 104 #include "addisc.h" 105 106 107 enum ad_item_state { 108 AD_STATE_INVALID = 0, /* The value is not valid */ 109 AD_STATE_FIXED, /* The value was fixed by caller */ 110 AD_STATE_AUTO /* The value is auto discovered */ 111 }; 112 113 enum ad_data_type { 114 AD_STRING = 123, 115 AD_DIRECTORY, 116 AD_DOMAINS_IN_FOREST, 117 AD_TRUSTED_DOMAINS 118 }; 119 120 121 typedef struct ad_subnet { 122 char subnet[24]; 123 } ad_subnet_t; 124 125 126 typedef struct ad_item { 127 enum ad_item_state state; 128 enum ad_data_type type; 129 void *value; 130 time_t ttl; 131 unsigned int version; /* Version is only changed */ 132 /* if the value changes */ 133 #define PARAM1 0 134 #define PARAM2 1 135 int param_version[2]; 136 /* These holds the version of */ 137 /* dependents so that a dependent */ 138 /* change can be detected */ 139 } ad_item_t; 140 141 typedef struct ad_disc { 142 struct __res_state res_state; 143 int res_ninitted; 144 ad_subnet_t *subnets; 145 boolean_t subnets_changed; 146 time_t subnets_last_check; 147 ad_item_t domain_name; /* DNS hostname string */ 148 ad_item_t domain_controller; /* Directory hostname and */ 149 /* port array */ 150 ad_item_t site_name; /* String */ 151 ad_item_t forest_name; /* DNS forestname string */ 152 ad_item_t global_catalog; /* Directory hostname and */ 153 /* port array */ 154 ad_item_t domains_in_forest; /* DNS domainname and SID */ 155 /* array */ 156 ad_item_t trusted_domains; /* DNS domainname and trust */ 157 /* direction array */ 158 /* Site specfic versions */ 159 ad_item_t site_domain_controller; /* Directory hostname and */ 160 /* port array */ 161 ad_item_t site_global_catalog; /* Directory hostname and */ 162 /* port array */ 163 } ad_disc; 164 165 166 #define DNS_MAX_NAME NS_MAXDNAME 167 168 169 /* SRV RR names for various queries */ 170 #define LDAP_SRV_HEAD "_ldap._tcp." 171 #define SITE_SRV_MIDDLE "%s._sites." 172 #define GC_SRV_TAIL "gc._msdcs" 173 #define DC_SRV_TAIL "dc._msdcs" 174 #define ALL_GC_SRV_TAIL "_gc._tcp" 175 #define PDC_SRV "_ldap._tcp.pdc._msdcs.%s" 176 177 /* A RR name for all GCs -- last resort this works */ 178 #define GC_ALL_A_NAME_FSTR "gc._msdcs.%s." 179 180 181 /* 182 * We try res_ninit() whenever we don't have one. res_ninit() fails if 183 * idmapd is running before the network is up! 184 */ 185 #define DO_RES_NINIT(ctx) if (!(ctx)->res_ninitted) \ 186 (ctx)->res_ninitted = (res_ninit(&ctx->res_state) != -1) 187 188 #define is_fixed(item) \ 189 ((item)->state == AD_STATE_FIXED) 190 191 #define is_changed(item, num, param) \ 192 ((item)->param_version[num] != (param)->version) 193 194 /*LINTLIBRARY*/ 195 196 /* 197 * Function definitions 198 */ 199 static ad_item_t * 200 validate_SiteName(ad_disc_t ctx); 201 202 203 204 static void 205 update_version(ad_item_t *item, int num, ad_item_t *param) 206 { 207 item->param_version[num] = param->version; 208 } 209 210 211 212 static boolean_t 213 is_valid(ad_item_t *item) 214 { 215 if (item->value != NULL) { 216 if (item->state == AD_STATE_FIXED) 217 return (B_TRUE); 218 if (item->state == AD_STATE_AUTO && 219 (item->ttl == 0 || item->ttl > time(NULL))) 220 return (B_TRUE); 221 } 222 return (B_FALSE); 223 } 224 225 226 static void 227 update_item(ad_item_t *item, void *value, enum ad_item_state state, 228 uint32_t ttl) 229 { 230 if (item->value != NULL && value != NULL) { 231 if ((item->type == AD_STRING && 232 strcmp(item->value, value) != 0) || 233 (item->type == AD_DIRECTORY && 234 ad_disc_compare_ds(item->value, value) != 0)|| 235 (item->type == AD_DOMAINS_IN_FOREST && 236 ad_disc_compare_domainsinforest(item->value, value) != 0) || 237 (item->type == AD_TRUSTED_DOMAINS && 238 ad_disc_compare_trusteddomains(item->value, value) != 0)) 239 item->version++; 240 } else if (item->value != value) 241 item->version++; 242 243 if (item->value != NULL) 244 free(item->value); 245 246 item->value = value; 247 item->state = state; 248 249 if (ttl == 0) 250 item->ttl = 0; 251 else 252 item->ttl = time(NULL) + ttl; 253 } 254 255 256 /* Compare DS lists */ 257 int 258 ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2) 259 { 260 int i, j; 261 int num_ds1; 262 int num_ds2; 263 boolean_t match; 264 265 for (i = 0; ds1[i].host[0] != '\0'; i++) 266 continue; 267 num_ds1 = i; 268 for (j = 0; ds2[j].host[0] != '\0'; j++) 269 continue; 270 num_ds2 = j; 271 if (num_ds1 != num_ds2) 272 return (1); 273 274 for (i = 0; i < num_ds1; i++) { 275 match = B_FALSE; 276 for (j = 0; j < num_ds2; j++) { 277 if (strcmp(ds1[i].host, ds2[j].host) == 0 && 278 ds1[i].port == ds2[j].port) { 279 match = B_TRUE; 280 break; 281 } 282 } 283 if (!match) 284 return (1); 285 } 286 return (0); 287 } 288 289 290 /* Copy a list of DSs */ 291 static idmap_ad_disc_ds_t * 292 ds_dup(const idmap_ad_disc_ds_t *srv) 293 { 294 int i; 295 int size; 296 idmap_ad_disc_ds_t *new = NULL; 297 298 for (i = 0; srv[i].host[0] != '\0'; i++) 299 continue; 300 301 size = (i + 1) * sizeof (idmap_ad_disc_ds_t); 302 new = malloc(size); 303 if (new != NULL) 304 memcpy(new, srv, size); 305 return (new); 306 } 307 308 309 int 310 ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1, 311 ad_disc_trusteddomains_t *td2) 312 { 313 int i, j; 314 int num_td1; 315 int num_td2; 316 boolean_t match; 317 318 for (i = 0; td1[i].domain[0] != '\0'; i++) 319 continue; 320 num_td1 = i; 321 322 for (j = 0; td2[j].domain[0] != '\0'; j++) 323 continue; 324 num_td2 = j; 325 326 if (num_td1 != num_td2) 327 return (1); 328 329 for (i = 0; i < num_td1; i++) { 330 match = B_FALSE; 331 for (j = 0; j < num_td2; j++) { 332 if (domain_eq(td1[i].domain, td2[j].domain)) { 333 match = B_TRUE; 334 break; 335 } 336 } 337 if (!match) 338 return (1); 339 } 340 return (0); 341 } 342 343 344 345 /* Copy a list of Trusted Domains */ 346 static ad_disc_trusteddomains_t * 347 td_dup(const ad_disc_trusteddomains_t *td) 348 { 349 int i; 350 int size; 351 ad_disc_trusteddomains_t *new = NULL; 352 353 for (i = 0; td[i].domain[0] != '\0'; i++) 354 continue; 355 356 size = (i + 1) * sizeof (ad_disc_trusteddomains_t); 357 new = malloc(size); 358 if (new != NULL) 359 memcpy(new, td, size); 360 return (new); 361 } 362 363 364 365 int 366 ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *df1, 367 ad_disc_domainsinforest_t *df2) 368 { 369 int i, j; 370 int num_df1; 371 int num_df2; 372 boolean_t match; 373 374 for (i = 0; df1[i].domain[0] != '\0'; i++) 375 continue; 376 num_df1 = i; 377 378 for (j = 0; df2[j].domain[0] != '\0'; j++) 379 continue; 380 num_df2 = j; 381 382 if (num_df1 != num_df2) 383 return (1); 384 385 for (i = 0; i < num_df1; i++) { 386 match = B_FALSE; 387 for (j = 0; j < num_df2; j++) { 388 if (domain_eq(df1[i].domain, df2[j].domain) && 389 strcmp(df1[i].sid, df2[j].sid) == 0) { 390 match = B_TRUE; 391 break; 392 } 393 } 394 if (!match) 395 return (1); 396 } 397 return (0); 398 } 399 400 401 402 /* Copy a list of Trusted Domains */ 403 static ad_disc_domainsinforest_t * 404 df_dup(const ad_disc_domainsinforest_t *df) 405 { 406 int i; 407 int size; 408 ad_disc_domainsinforest_t *new = NULL; 409 410 for (i = 0; df[i].domain[0] != '\0'; i++) 411 continue; 412 413 size = (i + 1) * sizeof (ad_disc_domainsinforest_t); 414 new = malloc(size); 415 if (new != NULL) 416 memcpy(new, df, size); 417 return (new); 418 } 419 420 421 422 423 424 /* 425 * Returns an array of IPv4 address/prefix length 426 * The last subnet is NULL 427 */ 428 static ad_subnet_t * 429 find_subnets() 430 { 431 int sock, n, i; 432 struct lifconf lifc; 433 struct lifreq lifr, *lifrp; 434 struct lifnum lifn; 435 uint32_t prefix_len; 436 char *s; 437 ad_subnet_t *results; 438 439 lifrp = &lifr; 440 441 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 442 logger(LOG_ERR, "Failed to open IPv4 socket for " 443 "listing network interfaces (%s)", strerror(errno)); 444 return (NULL); 445 } 446 447 lifn.lifn_family = AF_INET; 448 lifn.lifn_flags = 0; 449 if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) { 450 logger(LOG_ERR, 451 "Failed to find the number of network interfaces (%s)", 452 strerror(errno)); 453 close(sock); 454 return (NULL); 455 } 456 457 if (lifn.lifn_count < 1) { 458 logger(LOG_ERR, "No IPv4 network interfaces found"); 459 close(sock); 460 return (NULL); 461 } 462 463 lifc.lifc_family = AF_INET; 464 lifc.lifc_flags = 0; 465 lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq); 466 lifc.lifc_buf = malloc(lifc.lifc_len); 467 468 if (lifc.lifc_buf == NULL) { 469 logger(LOG_ERR, "Out of memory"); 470 close(sock); 471 return (NULL); 472 } 473 474 if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) { 475 logger(LOG_ERR, "Failed to list network interfaces (%s)", 476 strerror(errno)); 477 free(lifc.lifc_buf); 478 close(sock); 479 return (NULL); 480 } 481 482 n = lifc.lifc_len / (int)sizeof (struct lifreq); 483 484 if ((results = calloc(n + 1, sizeof (ad_subnet_t))) == NULL) { 485 free(lifc.lifc_buf); 486 close(sock); 487 return (NULL); 488 } 489 490 for (i = 0, lifrp = lifc.lifc_req; i < n; i++, lifrp++) { 491 if (ioctl(sock, SIOCGLIFFLAGS, lifrp) < 0) 492 continue; 493 494 if ((lifrp->lifr_flags & IFF_UP) == 0) 495 continue; 496 497 if (ioctl(sock, SIOCGLIFSUBNET, lifrp) < 0) 498 continue; 499 500 prefix_len = lifrp->lifr_addrlen; 501 502 s = inet_ntoa(((struct sockaddr_in *) 503 &lifrp->lifr_addr)->sin_addr); 504 505 (void) snprintf(results[i].subnet, sizeof (ad_subnet_t), 506 "%s/%d", s, prefix_len); 507 } 508 509 free(lifc.lifc_buf); 510 close(sock); 511 512 return (results); 513 } 514 515 static int 516 cmpsubnets(ad_subnet_t *subnets1, ad_subnet_t *subnets2) 517 { 518 int num_subnets1; 519 int num_subnets2; 520 boolean_t matched; 521 int i, j; 522 523 for (i = 0; subnets1[i].subnet[0] != '\0'; i++) 524 continue; 525 num_subnets1 = i; 526 527 for (i = 0; subnets2[i].subnet[0] != '\0'; i++) 528 continue; 529 num_subnets2 = i; 530 531 if (num_subnets1 != num_subnets2) 532 return (1); 533 534 for (i = 0; i < num_subnets1; i++) { 535 matched = B_FALSE; 536 for (j = 0; j < num_subnets2; j++) { 537 if (strcmp(subnets1[i].subnet, 538 subnets2[j].subnet) == 0) { 539 matched = B_TRUE; 540 break; 541 } 542 } 543 if (!matched) 544 return (1); 545 } 546 return (0); 547 } 548 549 550 551 552 /* Convert a DN's DC components into a DNS domainname */ 553 char * 554 DN_to_DNS(const char *dn_name) 555 { 556 char dns[DNS_MAX_NAME]; 557 char *dns_name; 558 int i, j; 559 int num = 0; 560 561 j = 0; 562 i = 0; 563 564 if (dn_name == NULL) 565 return (NULL); 566 /* 567 * Find all DC=<value> and form DNS name of the 568 * form <value1>.<value2>... 569 */ 570 while (dn_name[i] != '\0') { 571 if (strncasecmp(&dn_name[i], "DC=", 3) == 0) { 572 i += 3; 573 if (dn_name[i] != '\0' && num > 0) 574 dns[j++] = '.'; 575 while (dn_name[i] != '\0' && 576 dn_name[i] != ',' && dn_name[i] != '+') 577 dns[j++] = dn_name[i++]; 578 num++; 579 } else { 580 /* Skip attr=value as it is not DC= */ 581 while (dn_name[i] != '\0' && 582 dn_name[i] != ',' && dn_name[i] != '+') 583 i++; 584 } 585 /* Skip over separator ',' or '+' */ 586 if (dn_name[i] != '\0') i++; 587 } 588 dns[j] = '\0'; 589 dns_name = malloc(j + 1); 590 if (dns_name != NULL) 591 (void) strlcpy(dns_name, dns, j + 1); 592 return (dns_name); 593 } 594 595 596 /* Format the DN of an AD LDAP subnet object for some subnet */ 597 static char * 598 subnet_to_DN(const char *subnet, const char *baseDN) 599 { 600 char *result; 601 int len; 602 603 len = snprintf(NULL, 0, 604 "CN=%s,CN=Subnets,CN=Sites,%s", 605 subnet, baseDN) + 1; 606 607 result = malloc(len); 608 if (result != NULL) 609 (void) snprintf(result, len, 610 "CN=%s,CN=Subnets,CN=Sites,%s", 611 subnet, baseDN); 612 return (result); 613 } 614 615 616 /* Make a list of subnet object DNs from a list of subnets */ 617 static char ** 618 subnets_to_DNs(ad_subnet_t *subnets, const char *base_dn) 619 { 620 char **results; 621 int i, j; 622 623 for (i = 0; subnets[i].subnet[0] != '\0'; i++) 624 continue; 625 626 results = calloc(i + 1, sizeof (char *)); 627 if (results == NULL) 628 return (NULL); 629 630 for (i = 0; subnets[i].subnet[0] != '\0'; i++) { 631 if ((results[i] = subnet_to_DN(subnets[i].subnet, base_dn)) 632 == NULL) { 633 for (j = 0; j < i; j++) 634 free(results[j]); 635 free(results); 636 return (NULL); 637 } 638 } 639 640 return (results); 641 } 642 643 644 /* Compare SRC RRs; used with qsort() */ 645 static int 646 srvcmp(idmap_ad_disc_ds_t *s1, idmap_ad_disc_ds_t *s2) 647 { 648 if (s1->priority < s2->priority) 649 return (1); 650 else if (s1->priority > s2->priority) 651 return (-1); 652 653 if (s1->weight < s2->weight) 654 return (1); 655 else if (s1->weight > s2->weight) 656 return (-1); 657 658 return (0); 659 } 660 661 662 /* 663 * Query or search the SRV RRs for a given name. 664 * 665 * If name == NULL then search (as in res_nsearch(3RESOLV), honoring any 666 * search list/option), else query (as in res_nquery(3RESOLV)). 667 * 668 * The output TTL will be the one of the SRV RR with the lowest TTL. 669 */ 670 idmap_ad_disc_ds_t * 671 srv_query(res_state state, const char *svc_name, const char *dname, 672 char **rrname, uint32_t *ttl) 673 { 674 idmap_ad_disc_ds_t *srv; 675 idmap_ad_disc_ds_t *srv_res; 676 union { 677 HEADER hdr; 678 uchar_t buf[NS_MAXMSG]; 679 } msg; 680 int len, cnt, qdcount, ancount; 681 uchar_t *ptr, *eom; 682 uchar_t *end; 683 uint16_t type; 684 /* LINTED E_FUNC_SET_NOT_USED */ 685 uint16_t class; 686 uint32_t rttl; 687 uint16_t size; 688 char namebuf[NS_MAXDNAME]; 689 690 if (state == NULL) 691 return (NULL); 692 693 /* Set negative result TTL */ 694 *ttl = 5 * 60; 695 696 /* 1. query necessary resource records */ 697 698 /* Search, querydomain or query */ 699 if (rrname != NULL) { 700 *rrname = NULL; 701 len = res_nsearch(state, svc_name, C_IN, T_SRV, 702 msg.buf, sizeof (msg.buf)); 703 logger(LOG_DEBUG, "Searching DNS for SRV RRs named '%s'", 704 svc_name); 705 if (len < 0) { 706 logger(LOG_DEBUG, "DNS search for '%s' failed (%s)", 707 svc_name, hstrerror(state->res_h_errno)); 708 return (NULL); 709 } 710 } else if (dname != NULL) { 711 logger(LOG_DEBUG, 712 "Querying DNS for SRV RRs named '%s' for '%s' ", 713 svc_name, dname); 714 715 len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV, 716 msg.buf, sizeof (msg.buf)); 717 718 if (len < 0) { 719 logger(LOG_DEBUG, 720 "DNS query for '%s' for '%s' failed (%s)", 721 svc_name, dname, hstrerror(state->res_h_errno)); 722 return (NULL); 723 } 724 } 725 726 if (len > sizeof (msg.buf)) { 727 logger(LOG_ERR, "DNS query %ib message doesn't fit" 728 " into %ib buffer", 729 len, sizeof (msg.buf)); 730 return (NULL); 731 } 732 733 /* 2. parse the reply, skip header and question sections */ 734 735 ptr = msg.buf + sizeof (msg.hdr); 736 eom = msg.buf + len; 737 qdcount = ntohs(msg.hdr.qdcount); 738 ancount = ntohs(msg.hdr.ancount); 739 740 for (cnt = qdcount; cnt > 0; --cnt) { 741 if ((len = dn_skipname(ptr, eom)) < 0) { 742 logger(LOG_ERR, "DNS query invalid message format"); 743 return (NULL); 744 } 745 ptr += len + QFIXEDSZ; 746 } 747 748 /* 3. walk through the answer section */ 749 750 srv_res = calloc(ancount + 1, sizeof (idmap_ad_disc_ds_t)); 751 *ttl = (uint32_t)-1; 752 753 for (srv = srv_res, cnt = ancount; 754 cnt > 0; --cnt, srv++) { 755 756 len = dn_expand(msg.buf, eom, ptr, namebuf, 757 sizeof (namebuf)); 758 if (len < 0) { 759 logger(LOG_ERR, "DNS query invalid message format"); 760 return (NULL); 761 } 762 if (rrname != NULL && *rrname == NULL) 763 *rrname = strdup(namebuf); 764 ptr += len; 765 NS_GET16(type, ptr); 766 NS_GET16(class, ptr); 767 NS_GET32(rttl, ptr); 768 NS_GET16(size, ptr); 769 if ((end = ptr + size) > eom) { 770 logger(LOG_ERR, "DNS query invalid message format"); 771 return (NULL); 772 } 773 774 if (type != T_SRV) { 775 ptr = end; 776 continue; 777 } 778 779 NS_GET16(srv->priority, ptr); 780 NS_GET16(srv->weight, ptr); 781 NS_GET16(srv->port, ptr); 782 len = dn_expand(msg.buf, eom, ptr, srv->host, 783 sizeof (srv->host)); 784 if (len < 0) { 785 logger(LOG_ERR, "DNS query invalid SRV record"); 786 return (NULL); 787 } 788 789 if (rttl < *ttl) 790 *ttl = rttl; 791 792 logger(LOG_DEBUG, "Found %s %d IN SRV [%d][%d] %s:%d", 793 namebuf, rttl, srv->priority, srv->weight, srv->host, 794 srv->port); 795 796 /* 3. move ptr to the end of current record */ 797 798 ptr = end; 799 } 800 801 if (ancount > 1) 802 qsort(srv_res, ancount, sizeof (*srv_res), 803 (int (*)(const void *, const void *))srvcmp); 804 805 return (srv_res); 806 } 807 808 809 /* 810 * A utility function to bind to a Directory server 811 */ 812 813 static LDAP* 814 ldap_lookup_init(idmap_ad_disc_ds_t *ds) 815 { 816 int i; 817 int rc, ldversion; 818 int zero = 0; 819 int timeoutms = 5 * 1000; 820 char *saslmech = "GSSAPI"; 821 uint32_t saslflags = LDAP_SASL_INTERACTIVE; 822 LDAP *ld = NULL; 823 824 for (i = 0; ds[i].host[0] != '\0'; i++) { 825 ld = ldap_init(ds[i].host, ds[i].port); 826 if (ld == NULL) { 827 logger(LOG_DEBUG, "Couldn't connect to " 828 "AD DC %s:%d (%s)", 829 ds[i].host, ds[i].port, 830 strerror(errno)); 831 continue; 832 } 833 834 ldversion = LDAP_VERSION3; 835 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 836 &ldversion); 837 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, 838 LDAP_OPT_OFF); 839 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero); 840 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero); 841 /* setup TCP/IP connect timeout */ 842 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, 843 &timeoutms); 844 (void) ldap_set_option(ld, LDAP_OPT_RESTART, 845 LDAP_OPT_ON); 846 847 rc = adutils_set_thread_functions(ld); 848 if (rc != LDAP_SUCCESS) { 849 /* Error has already been logged */ 850 (void) ldap_unbind(ld); 851 ld = NULL; 852 continue; 853 } 854 855 rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */, 856 saslmech, NULL, NULL, saslflags, &saslcallback, 857 NULL /* defaults */); 858 if (rc == LDAP_SUCCESS) 859 break; 860 861 logger(LOG_INFO, "LDAP SASL bind to %s:%d failed (%s)", 862 ds[i].host, ds[i].port, ldap_err2string(rc)); 863 (void) ldap_unbind(ld); 864 ld = NULL; 865 } 866 return (ld); 867 } 868 869 870 871 /* 872 * A utility function to get the value of some attribute of one of one 873 * or more AD LDAP objects named by the dn_list; first found one wins. 874 */ 875 static char * 876 ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers, 877 char **dn_list, char *attr) 878 { 879 int i; 880 int rc; 881 int scope = LDAP_SCOPE_BASE; 882 char *attrs[2]; 883 LDAPMessage *results = NULL; 884 LDAPMessage *entry; 885 char **values = NULL; 886 char *val = NULL; 887 888 attrs[0] = attr; 889 attrs[1] = NULL; 890 891 if (*ld == NULL) 892 *ld = ldap_lookup_init(domainControllers); 893 894 if (*ld == NULL) 895 return (NULL); 896 897 for (i = 0; dn_list[i] != NULL; i++) { 898 rc = ldap_search_s(*ld, dn_list[i], scope, 899 "(objectclass=*)", attrs, 0, &results); 900 if (rc == LDAP_SUCCESS) { 901 for (entry = ldap_first_entry(*ld, results); 902 entry != NULL && values == NULL; 903 entry = ldap_next_entry(*ld, entry)) { 904 values = ldap_get_values( 905 *ld, entry, attr); 906 } 907 908 if (values != NULL) { 909 (void) ldap_msgfree(results); 910 val = strdup(values[0]); 911 ldap_value_free(values); 912 return (val); 913 } 914 } 915 if (results != NULL) { 916 (void) ldap_msgfree(results); 917 results = NULL; 918 } 919 } 920 921 return (NULL); 922 } 923 924 925 /* 926 * Lookup the trusted domains in the global catalog. 927 * 928 * Returns: 929 * array of trusted domains which is terminated by 930 * an empty trusted domain. 931 * NULL an error occured 932 */ 933 ad_disc_trusteddomains_t * 934 ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog, 935 char *base_dn) 936 { 937 int scope = LDAP_SCOPE_SUBTREE; 938 char *attrs[3]; 939 int rc; 940 LDAPMessage *results = NULL; 941 LDAPMessage *entry; 942 char *filter; 943 char **partner = NULL; 944 char **direction = NULL; 945 int num = 0; 946 ad_disc_trusteddomains_t *trusted_domains = NULL; 947 948 949 if (*ld == NULL) 950 *ld = ldap_lookup_init(globalCatalog); 951 952 if (*ld == NULL) 953 return (NULL); 954 955 attrs[0] = "trustPartner"; 956 attrs[1] = "trustDirection"; 957 attrs[2] = NULL; 958 959 /* trustDirection values - inbound = 1 and bidirectional = 3 */ 960 filter = "(&(objectclass=trustedDomain)" 961 "(|(trustDirection=3)(trustDirection=1)))"; 962 963 rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results); 964 if (rc == LDAP_SUCCESS) { 965 for (entry = ldap_first_entry(*ld, results); 966 entry != NULL; entry = ldap_next_entry(*ld, entry)) { 967 partner = ldap_get_values(*ld, entry, "trustPartner"); 968 direction = ldap_get_values( 969 *ld, entry, "trustDirection"); 970 971 if (partner != NULL && direction != NULL) { 972 num++; 973 trusted_domains = realloc(trusted_domains, 974 (num + 1) * 975 sizeof (ad_disc_trusteddomains_t)); 976 if (trusted_domains == NULL) { 977 ldap_value_free(partner); 978 ldap_value_free(direction); 979 ldap_msgfree(results); 980 return (NULL); 981 } 982 /* Last element should be zero */ 983 memset(&trusted_domains[num], 0, 984 sizeof (ad_disc_trusteddomains_t)); 985 strcpy(trusted_domains[num - 1].domain, 986 partner[0]); 987 trusted_domains[num - 1].direction = 988 atoi(direction[0]); 989 } 990 if (partner != NULL) 991 ldap_value_free(partner); 992 if (direction != NULL) 993 ldap_value_free(direction); 994 } 995 } else if (rc == LDAP_NO_RESULTS_RETURNED) { 996 /* This is not an error - return empty trusted domain */ 997 trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t)); 998 } 999 if (results != NULL) 1000 ldap_msgfree(results); 1001 1002 return (trusted_domains); 1003 } 1004 1005 1006 /* 1007 * This functions finds all the domains in a forest. 1008 */ 1009 ad_disc_domainsinforest_t * 1010 ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs) 1011 { 1012 static char *attrs[] = { 1013 "objectSid", 1014 NULL, 1015 }; 1016 int rc; 1017 LDAPMessage *result = NULL; 1018 LDAPMessage *entry; 1019 int ndomains = 0; 1020 int nresults; 1021 ad_disc_domainsinforest_t *domains = NULL; 1022 1023 if (*ld == NULL) 1024 *ld = ldap_lookup_init(globalCatalogs); 1025 1026 if (*ld == NULL) 1027 return (NULL); 1028 1029 logger(LOG_DEBUG, "Looking for domains in forest..."); 1030 /* Find domains */ 1031 rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE, 1032 "(objectClass=Domain)", attrs, 0, &result); 1033 if (rc != LDAP_SUCCESS) 1034 goto err; 1035 1036 nresults = ldap_count_entries(*ld, result); 1037 domains = calloc(nresults + 1, sizeof (*domains)); 1038 if (domains == NULL) 1039 goto err; 1040 1041 for (entry = ldap_first_entry(*ld, result); 1042 entry != NULL; 1043 entry = ldap_next_entry(*ld, entry)) { 1044 struct berval **sid_ber; 1045 adutils_sid_t sid; 1046 char *sid_str; 1047 char *name; 1048 1049 sid_ber = ldap_get_values_len(*ld, entry, 1050 "objectSid"); 1051 if (sid_ber == NULL) 1052 continue; 1053 1054 rc = adutils_getsid(sid_ber[0], &sid); 1055 ldap_value_free_len(sid_ber); 1056 if (rc < 0) 1057 goto err; 1058 1059 if ((sid_str = adutils_sid2txt(&sid)) == NULL) 1060 goto err; 1061 1062 strcpy(domains[ndomains].sid, sid_str); 1063 free(sid_str); 1064 1065 name = DN_to_DNS(ldap_get_dn(*ld, entry)); 1066 if (name == NULL) 1067 goto err; 1068 1069 strcpy(domains[ndomains].domain, name); 1070 free(name); 1071 1072 logger(LOG_DEBUG, " found %s", domains[ndomains].domain); 1073 1074 ndomains++; 1075 } 1076 1077 if (ndomains == 0) 1078 goto err; 1079 1080 if (ndomains < nresults) { 1081 ad_disc_domainsinforest_t *tmp; 1082 tmp = realloc(domains, (ndomains + 1) * sizeof (*domains)); 1083 if (tmp == NULL) 1084 goto err; 1085 domains = tmp; 1086 } 1087 1088 if (result != NULL) 1089 ldap_msgfree(result); 1090 1091 return (domains); 1092 1093 err: 1094 free(domains); 1095 if (result != NULL) 1096 ldap_msgfree(result); 1097 return (NULL); 1098 } 1099 1100 1101 ad_disc_t 1102 ad_disc_init(void) 1103 { 1104 struct ad_disc *ctx; 1105 ctx = calloc(1, sizeof (struct ad_disc)); 1106 if (ctx != NULL) 1107 DO_RES_NINIT(ctx); 1108 1109 ctx->domain_name.type = AD_STRING; 1110 ctx->domain_controller.type = AD_DIRECTORY; 1111 ctx->site_name.type = AD_STRING; 1112 ctx->forest_name.type = AD_STRING; 1113 ctx->global_catalog.type = AD_DIRECTORY; 1114 ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST; 1115 ctx->trusted_domains.type = AD_TRUSTED_DOMAINS; 1116 /* Site specific versions */ 1117 ctx->site_domain_controller.type = AD_DIRECTORY; 1118 ctx->site_global_catalog.type = AD_DIRECTORY; 1119 return (ctx); 1120 } 1121 1122 1123 void 1124 ad_disc_fini(ad_disc_t ctx) 1125 { 1126 if (ctx == NULL) 1127 return; 1128 1129 if (ctx->res_ninitted) 1130 res_ndestroy(&ctx->res_state); 1131 1132 if (ctx->subnets != NULL) 1133 free(ctx->subnets); 1134 1135 if (ctx->domain_name.value != NULL) 1136 free(ctx->domain_name.value); 1137 1138 if (ctx->domain_controller.value != NULL) 1139 free(ctx->domain_controller.value); 1140 1141 if (ctx->site_name.value != NULL) 1142 free(ctx->site_name.value); 1143 1144 if (ctx->forest_name.value != NULL) 1145 free(ctx->forest_name.value); 1146 1147 if (ctx->global_catalog.value != NULL) 1148 free(ctx->global_catalog.value); 1149 1150 if (ctx->domains_in_forest.value != NULL) 1151 free(ctx->domains_in_forest.value); 1152 1153 if (ctx->trusted_domains.value != NULL) 1154 free(ctx->trusted_domains.value); 1155 1156 /* Site specific versions */ 1157 if (ctx->site_domain_controller.value != NULL) 1158 free(ctx->site_domain_controller.value); 1159 1160 if (ctx->site_global_catalog.value != NULL) 1161 free(ctx->site_global_catalog.value); 1162 1163 free(ctx); 1164 } 1165 1166 void 1167 ad_disc_refresh(ad_disc_t ctx) 1168 { 1169 if (ctx->res_ninitted) 1170 res_ndestroy(&ctx->res_state); 1171 (void) memset(&ctx->res_state, 0, sizeof (ctx->res_state)); 1172 ctx->res_ninitted = res_ninit(&ctx->res_state) != -1; 1173 1174 if (ctx->domain_name.state == AD_STATE_AUTO) 1175 ctx->domain_name.state = AD_STATE_INVALID; 1176 1177 if (ctx->domain_controller.state == AD_STATE_AUTO) 1178 ctx->domain_controller.state = AD_STATE_INVALID; 1179 1180 if (ctx->site_name.state == AD_STATE_AUTO) 1181 ctx->site_name.state = AD_STATE_INVALID; 1182 1183 if (ctx->forest_name.state == AD_STATE_AUTO) 1184 ctx->forest_name.state = AD_STATE_INVALID; 1185 1186 if (ctx->global_catalog.state == AD_STATE_AUTO) 1187 ctx->global_catalog.state = AD_STATE_INVALID; 1188 1189 if (ctx->domains_in_forest.state == AD_STATE_AUTO) 1190 ctx->domains_in_forest.state = AD_STATE_INVALID; 1191 1192 if (ctx->trusted_domains.state == AD_STATE_AUTO) 1193 ctx->trusted_domains.state = AD_STATE_INVALID; 1194 1195 if (ctx->site_domain_controller.state == AD_STATE_AUTO) 1196 ctx->site_domain_controller.state = AD_STATE_INVALID; 1197 1198 if (ctx->site_global_catalog.state == AD_STATE_AUTO) 1199 ctx->site_global_catalog.state = AD_STATE_INVALID; 1200 } 1201 1202 1203 1204 /* Discover joined Active Directory domainName */ 1205 static ad_item_t * 1206 validate_DomainName(ad_disc_t ctx) 1207 { 1208 idmap_ad_disc_ds_t *domain_controller = NULL; 1209 char *dname, *srvname; 1210 uint32_t ttl = 0; 1211 int len; 1212 1213 if (is_valid(&ctx->domain_name)) 1214 return (&ctx->domain_name); 1215 1216 1217 /* Try to find our domain by searching for DCs for it */ 1218 DO_RES_NINIT(ctx); 1219 domain_controller = srv_query(&ctx->res_state, LDAP_SRV_HEAD 1220 DC_SRV_TAIL, ctx->domain_name.value, &srvname, &ttl); 1221 1222 /* 1223 * If we can't find DCs by via res_nsearch() then there's no 1224 * point in trying anything else to discover the AD domain name. 1225 */ 1226 if (domain_controller == NULL) 1227 return (NULL); 1228 1229 free(domain_controller); 1230 /* 1231 * We have the FQDN of the SRV RR name, so now we extract the 1232 * domainname suffix from it. 1233 */ 1234 dname = strdup(srvname + strlen(LDAP_SRV_HEAD DC_SRV_TAIL) + 1235 1 /* for the dot between RR name and domainname */); 1236 1237 free(srvname); 1238 1239 if (dname == NULL) { 1240 logger(LOG_ERR, "Out of memory"); 1241 return (NULL); 1242 } 1243 1244 /* Eat any trailing dot */ 1245 len = strlen(dname); 1246 if (len > 0 && dname[len - 1] == '.') 1247 dname[len - 1] = '\0'; 1248 1249 update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl); 1250 1251 return (&ctx->domain_name); 1252 } 1253 1254 1255 char * 1256 ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered) 1257 { 1258 char *domain_name = NULL; 1259 ad_item_t *domain_name_item; 1260 1261 domain_name_item = validate_DomainName(ctx); 1262 1263 if (domain_name_item) { 1264 domain_name = strdup(domain_name_item->value); 1265 if (auto_discovered != NULL) 1266 *auto_discovered = 1267 (domain_name_item->state == AD_STATE_AUTO); 1268 } else if (auto_discovered != NULL) 1269 *auto_discovered = B_FALSE; 1270 1271 return (domain_name); 1272 } 1273 1274 1275 /* Discover domain controllers */ 1276 static ad_item_t * 1277 validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) 1278 { 1279 uint32_t ttl = 0; 1280 idmap_ad_disc_ds_t *domain_controller = NULL; 1281 boolean_t validate_global = B_FALSE; 1282 boolean_t validate_site = B_FALSE; 1283 ad_item_t *domain_name_item; 1284 ad_item_t *site_name_item = NULL; 1285 1286 /* If the values is fixed there will not be a site specific version */ 1287 if (is_fixed(&ctx->domain_controller)) 1288 return (&ctx->domain_controller); 1289 1290 domain_name_item = validate_DomainName(ctx); 1291 if (domain_name_item == NULL) 1292 return (NULL); 1293 1294 if (req == AD_DISC_GLOBAL) 1295 validate_global = B_TRUE; 1296 else { 1297 site_name_item = validate_SiteName(ctx); 1298 if (site_name_item != NULL) 1299 validate_site = B_TRUE; 1300 else if (req == AD_DISC_PREFER_SITE) 1301 validate_global = B_TRUE; 1302 } 1303 1304 if (validate_global) { 1305 if (!is_valid(&ctx->domain_controller) || 1306 is_changed(&ctx->domain_controller, PARAM1, 1307 domain_name_item)) { 1308 /* 1309 * Lookup DNS SRV RR named 1310 * _ldap._tcp.dc._msdcs.<DomainName> 1311 */ 1312 DO_RES_NINIT(ctx); 1313 domain_controller = srv_query(&ctx->res_state, 1314 LDAP_SRV_HEAD DC_SRV_TAIL, 1315 domain_name_item->value, NULL, &ttl); 1316 1317 if (domain_controller == NULL) 1318 return (NULL); 1319 1320 update_item(&ctx->domain_controller, domain_controller, 1321 AD_STATE_AUTO, ttl); 1322 update_version(&ctx->domain_controller, PARAM1, 1323 domain_name_item); 1324 } 1325 return (&ctx->domain_controller); 1326 } 1327 1328 if (validate_site) { 1329 if (!is_valid(&ctx->site_domain_controller) || 1330 is_changed(&ctx->site_domain_controller, PARAM1, 1331 domain_name_item) || 1332 is_changed(&ctx->site_domain_controller, PARAM2, 1333 site_name_item)) { 1334 char rr_name[DNS_MAX_NAME]; 1335 /* 1336 * Lookup DNS SRV RR named 1337 * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName> 1338 */ 1339 (void) snprintf(rr_name, sizeof (rr_name), 1340 LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL, 1341 site_name_item->value); 1342 DO_RES_NINIT(ctx); 1343 domain_controller = srv_query(&ctx->res_state, rr_name, 1344 domain_name_item->value, NULL, &ttl); 1345 if (domain_controller == NULL) 1346 return (NULL); 1347 1348 update_item(&ctx->site_domain_controller, 1349 domain_controller, AD_STATE_AUTO, ttl); 1350 update_version(&ctx->site_domain_controller, PARAM1, 1351 domain_name_item); 1352 update_version(&ctx->site_domain_controller, PARAM2, 1353 site_name_item); 1354 } 1355 return (&ctx->site_domain_controller); 1356 } 1357 return (NULL); 1358 } 1359 1360 idmap_ad_disc_ds_t * 1361 ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req, 1362 boolean_t *auto_discovered) 1363 { 1364 ad_item_t *domain_controller_item; 1365 idmap_ad_disc_ds_t *domain_controller = NULL; 1366 1367 domain_controller_item = validate_DomainController(ctx, req); 1368 1369 if (domain_controller_item != NULL) { 1370 domain_controller = ds_dup(domain_controller_item->value); 1371 if (auto_discovered != NULL) 1372 *auto_discovered = 1373 (domain_controller_item->state == AD_STATE_AUTO); 1374 } else if (auto_discovered != NULL) 1375 *auto_discovered = B_FALSE; 1376 1377 return (domain_controller); 1378 } 1379 1380 1381 /* Discover site name (for multi-homed systems the first one found wins) */ 1382 static ad_item_t * 1383 validate_SiteName(ad_disc_t ctx) 1384 { 1385 LDAP *ld = NULL; 1386 ad_subnet_t *subnets = NULL; 1387 char **dn_subnets = NULL; 1388 char *dn_root[2]; 1389 char *config_naming_context = NULL; 1390 char *site_object = NULL; 1391 char *site_name = NULL; 1392 char *forest_name; 1393 int len; 1394 int i; 1395 boolean_t update_required = B_FALSE; 1396 ad_item_t *domain_controller_item; 1397 1398 if (is_fixed(&ctx->site_name)) 1399 return (&ctx->site_name); 1400 1401 /* Can't rely on site-specific DCs */ 1402 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 1403 if (domain_controller_item == NULL) 1404 return (NULL); 1405 1406 if (!is_valid(&ctx->site_name) || 1407 is_changed(&ctx->site_name, PARAM1, &ctx->domain_controller) || 1408 ctx->subnets == NULL || ctx->subnets_changed) { 1409 subnets = find_subnets(); 1410 ctx->subnets_last_check = time(NULL); 1411 update_required = B_TRUE; 1412 } else if (ctx->subnets_last_check + 60 < time(NULL)) { 1413 subnets = find_subnets(); 1414 ctx->subnets_last_check = time(NULL); 1415 if (cmpsubnets(ctx->subnets, subnets) != 0) 1416 update_required = B_TRUE; 1417 } 1418 1419 if (!update_required) { 1420 free(subnets); 1421 return (&ctx->site_name); 1422 } 1423 1424 if (subnets == NULL) 1425 return (NULL); 1426 1427 dn_root[0] = ""; 1428 dn_root[1] = NULL; 1429 1430 config_naming_context = ldap_lookup_entry_attr( 1431 &ld, ctx->domain_controller.value, 1432 dn_root, "configurationNamingContext"); 1433 if (config_naming_context == NULL) 1434 goto out; 1435 /* 1436 * configurationNamingContext also provides the Forest 1437 * Name. 1438 */ 1439 if (!is_fixed(&ctx->forest_name)) { 1440 /* 1441 * The configurationNamingContext should be of 1442 * form: 1443 * CN=Configuration,<DNforestName> 1444 * Remove the first part and convert to DNS form 1445 * (replace ",DC=" with ".") 1446 */ 1447 char *str = "CN=Configuration,"; 1448 int len = strlen(str); 1449 if (strncasecmp(config_naming_context, str, len) == 0) { 1450 forest_name = DN_to_DNS(config_naming_context + len); 1451 update_item(&ctx->forest_name, forest_name, 1452 AD_STATE_AUTO, 0); 1453 } 1454 } 1455 1456 dn_subnets = subnets_to_DNs(subnets, config_naming_context); 1457 if (dn_subnets == NULL) 1458 goto out; 1459 1460 site_object = ldap_lookup_entry_attr( 1461 &ld, domain_controller_item->value, 1462 dn_subnets, "siteobject"); 1463 if (site_object != NULL) { 1464 /* 1465 * The site object should be of the form 1466 * CN=<site>,CN=Sites,CN=Configuration, 1467 * <DN Domain> 1468 */ 1469 if (strncasecmp(site_object, "CN=", 3) == 0) { 1470 for (len = 0; site_object[len + 3] != ','; len++) 1471 ; 1472 site_name = malloc(len + 1); 1473 (void) strncpy(site_name, &site_object[3], len); 1474 site_name[len] = '\0'; 1475 update_item(&ctx->site_name, site_name, 1476 AD_STATE_AUTO, 0); 1477 } 1478 } 1479 1480 if (ctx->subnets != NULL) { 1481 free(ctx->subnets); 1482 ctx->subnets = NULL; 1483 } 1484 ctx->subnets = subnets; 1485 subnets = NULL; 1486 ctx->subnets_changed = B_FALSE; 1487 1488 out: 1489 if (ld != NULL) 1490 (void) ldap_unbind(ld); 1491 1492 if (dn_subnets != NULL) { 1493 for (i = 0; dn_subnets[i] != NULL; i++) 1494 free(dn_subnets[i]); 1495 free(dn_subnets); 1496 } 1497 if (config_naming_context != NULL) 1498 free(config_naming_context); 1499 if (site_object != NULL) 1500 free(site_object); 1501 1502 free(subnets); 1503 if (site_name == NULL) 1504 return (NULL); 1505 return (&ctx->site_name); 1506 1507 } 1508 1509 1510 char * 1511 ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered) 1512 { 1513 ad_item_t *site_name_item; 1514 char *site_name = NULL; 1515 1516 site_name_item = validate_SiteName(ctx); 1517 if (site_name_item != NULL) { 1518 site_name = strdup(site_name_item->value); 1519 if (auto_discovered != NULL) 1520 *auto_discovered = 1521 (site_name_item->state == AD_STATE_AUTO); 1522 } else if (auto_discovered != NULL) 1523 *auto_discovered = B_FALSE; 1524 1525 return (site_name); 1526 } 1527 1528 1529 1530 /* Discover forest name */ 1531 static ad_item_t * 1532 validate_ForestName(ad_disc_t ctx) 1533 { 1534 LDAP *ld = NULL; 1535 char *config_naming_context; 1536 char *forest_name = NULL; 1537 char *dn_list[2]; 1538 ad_item_t *domain_controller_item; 1539 1540 if (is_fixed(&ctx->forest_name)) 1541 return (&ctx->forest_name); 1542 /* 1543 * We may not have a site name yet, so we won't rely on 1544 * site-specific DCs. (But maybe we could replace 1545 * validate_ForestName() with validate_siteName()?) 1546 */ 1547 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 1548 if (domain_controller_item == NULL) 1549 return (NULL); 1550 1551 if (!is_valid(&ctx->forest_name) || 1552 is_changed(&ctx->forest_name, PARAM1, domain_controller_item)) { 1553 1554 dn_list[0] = ""; 1555 dn_list[1] = NULL; 1556 config_naming_context = ldap_lookup_entry_attr( 1557 &ld, ctx->domain_controller.value, 1558 dn_list, "configurationNamingContext"); 1559 if (config_naming_context != NULL) { 1560 /* 1561 * The configurationNamingContext should be of 1562 * form: 1563 * CN=Configuration,<DNforestName> 1564 * Remove the first part and convert to DNS form 1565 * (replace ",DC=" with ".") 1566 */ 1567 char *str = "CN=Configuration,"; 1568 int len = strlen(str); 1569 if (strncasecmp(config_naming_context, str, len) == 0) { 1570 forest_name = DN_to_DNS( 1571 config_naming_context + len); 1572 } 1573 free(config_naming_context); 1574 } 1575 if (ld != NULL) 1576 (void) ldap_unbind(ld); 1577 1578 if (forest_name == NULL) 1579 return (NULL); 1580 1581 update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0); 1582 update_version(&ctx->forest_name, PARAM1, 1583 domain_controller_item); 1584 } 1585 return (&ctx->forest_name); 1586 } 1587 1588 1589 char * 1590 ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered) 1591 { 1592 ad_item_t *forest_name_item; 1593 char *forest_name = NULL; 1594 1595 forest_name_item = validate_ForestName(ctx); 1596 1597 if (forest_name_item != NULL) { 1598 forest_name = strdup(forest_name_item->value); 1599 if (auto_discovered != NULL) 1600 *auto_discovered = 1601 (forest_name_item->state == AD_STATE_AUTO); 1602 } else if (auto_discovered != NULL) 1603 *auto_discovered = B_FALSE; 1604 1605 return (forest_name); 1606 } 1607 1608 1609 /* Discover global catalog servers */ 1610 static ad_item_t * 1611 validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) 1612 { 1613 idmap_ad_disc_ds_t *global_catalog = NULL; 1614 uint32_t ttl = 0; 1615 boolean_t validate_global = B_FALSE; 1616 boolean_t validate_site = B_FALSE; 1617 ad_item_t *forest_name_item; 1618 ad_item_t *site_name_item; 1619 1620 /* If the values is fixed there will not be a site specific version */ 1621 if (is_fixed(&ctx->global_catalog)) 1622 return (&ctx->global_catalog); 1623 1624 forest_name_item = validate_ForestName(ctx); 1625 if (forest_name_item == NULL) 1626 return (NULL); 1627 1628 if (req == AD_DISC_GLOBAL) 1629 validate_global = B_TRUE; 1630 else { 1631 site_name_item = validate_SiteName(ctx); 1632 if (site_name_item != NULL) 1633 validate_site = B_TRUE; 1634 else if (req == AD_DISC_PREFER_SITE) 1635 validate_global = B_TRUE; 1636 } 1637 1638 if (validate_global) { 1639 if (!is_valid(&ctx->global_catalog) || 1640 is_changed(&ctx->global_catalog, PARAM1, 1641 forest_name_item)) { 1642 /* 1643 * Lookup DNS SRV RR named 1644 * _ldap._tcp.gc._msdcs.<ForestName> 1645 */ 1646 DO_RES_NINIT(ctx); 1647 global_catalog = 1648 srv_query(&ctx->res_state, 1649 LDAP_SRV_HEAD GC_SRV_TAIL, 1650 ctx->forest_name.value, NULL, &ttl); 1651 1652 if (global_catalog == NULL) 1653 return (NULL); 1654 1655 update_item(&ctx->global_catalog, global_catalog, 1656 AD_STATE_AUTO, ttl); 1657 update_version(&ctx->global_catalog, PARAM1, 1658 forest_name_item); 1659 } 1660 return (&ctx->global_catalog); 1661 } 1662 1663 if (validate_site) { 1664 if (!is_valid(&ctx->site_global_catalog) || 1665 is_changed(&ctx->site_global_catalog, PARAM1, 1666 forest_name_item) || 1667 is_changed(&ctx->site_global_catalog, PARAM2, 1668 site_name_item)) { 1669 char rr_name[DNS_MAX_NAME]; 1670 1671 /* 1672 * Lookup DNS SRV RR named: 1673 * _ldap._tcp.<siteName>._sites.gc. 1674 * _msdcs.<ForestName> 1675 */ 1676 (void) snprintf(rr_name, 1677 sizeof (rr_name), 1678 LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL, 1679 ctx->site_name.value); 1680 DO_RES_NINIT(ctx); 1681 global_catalog = srv_query(&ctx->res_state, rr_name, 1682 ctx->forest_name.value, NULL, &ttl); 1683 1684 if (global_catalog == NULL) 1685 return (NULL); 1686 update_item(&ctx->site_global_catalog, global_catalog, 1687 AD_STATE_AUTO, ttl); 1688 update_version(&ctx->site_global_catalog, PARAM1, 1689 forest_name_item); 1690 update_version(&ctx->site_global_catalog, PARAM2, 1691 site_name_item); 1692 } 1693 return (&ctx->site_global_catalog); 1694 } 1695 return (NULL); 1696 } 1697 1698 1699 idmap_ad_disc_ds_t * 1700 ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req, 1701 boolean_t *auto_discovered) 1702 { 1703 idmap_ad_disc_ds_t *global_catalog = NULL; 1704 ad_item_t *global_catalog_item; 1705 1706 global_catalog_item = validate_GlobalCatalog(ctx, req); 1707 1708 if (global_catalog_item != NULL) { 1709 global_catalog = ds_dup(global_catalog_item->value); 1710 if (auto_discovered != NULL) 1711 *auto_discovered = 1712 (global_catalog_item->state == AD_STATE_AUTO); 1713 } else if (auto_discovered != NULL) 1714 *auto_discovered = B_FALSE; 1715 1716 return (global_catalog); 1717 } 1718 1719 1720 static ad_item_t * 1721 validate_TrustedDomains(ad_disc_t ctx) 1722 { 1723 LDAP *ld = NULL; 1724 ad_item_t *global_catalog_item; 1725 ad_item_t *forest_name_item; 1726 ad_disc_trusteddomains_t *trusted_domains; 1727 char *dn = NULL; 1728 char *forest_name_dn; 1729 int len; 1730 int num_parts; 1731 1732 if (is_fixed(&ctx->trusted_domains)) 1733 return (&ctx->trusted_domains); 1734 1735 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 1736 if (global_catalog_item == NULL) 1737 return (NULL); 1738 1739 forest_name_item = validate_ForestName(ctx); 1740 if (forest_name_item == NULL) 1741 return (NULL); 1742 1743 if (!is_valid(&ctx->trusted_domains) || 1744 is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) || 1745 is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) { 1746 1747 forest_name_dn = ldap_dns_to_dn(forest_name_item->value, 1748 &num_parts); 1749 if (forest_name_dn == NULL) 1750 return (NULL); 1751 1752 len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1; 1753 dn = malloc(len); 1754 if (dn == NULL) { 1755 free(forest_name_dn); 1756 return (NULL); 1757 } 1758 (void) snprintf(dn, len, "CN=System,%s", forest_name_dn); 1759 free(forest_name_dn); 1760 1761 trusted_domains = ldap_lookup_trusted_domains( 1762 &ld, global_catalog_item->value, dn); 1763 1764 if (ld != NULL) 1765 (void) ldap_unbind(ld); 1766 free(dn); 1767 1768 if (trusted_domains == NULL) 1769 return (NULL); 1770 1771 update_item(&ctx->trusted_domains, trusted_domains, 1772 AD_STATE_AUTO, 0); 1773 update_version(&ctx->trusted_domains, PARAM1, 1774 global_catalog_item); 1775 update_version(&ctx->trusted_domains, PARAM2, 1776 forest_name_item); 1777 } 1778 1779 return (&ctx->trusted_domains); 1780 } 1781 1782 1783 ad_disc_trusteddomains_t * 1784 ad_disc_get_TrustedDomains(ad_disc_t ctx, boolean_t *auto_discovered) 1785 { 1786 ad_disc_trusteddomains_t *trusted_domains = NULL; 1787 ad_item_t *trusted_domains_item; 1788 1789 trusted_domains_item = validate_TrustedDomains(ctx); 1790 1791 if (trusted_domains_item != NULL) { 1792 trusted_domains = td_dup(trusted_domains_item->value); 1793 if (auto_discovered != NULL) 1794 *auto_discovered = 1795 (trusted_domains_item->state == AD_STATE_AUTO); 1796 } else if (auto_discovered != NULL) 1797 *auto_discovered = B_FALSE; 1798 1799 return (trusted_domains); 1800 } 1801 1802 1803 static ad_item_t * 1804 validate_DomainsInForest(ad_disc_t ctx) 1805 { 1806 ad_item_t *global_catalog_item; 1807 LDAP *ld = NULL; 1808 ad_disc_domainsinforest_t *domains_in_forest; 1809 1810 if (is_fixed(&ctx->domains_in_forest)) 1811 return (&ctx->domains_in_forest); 1812 1813 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 1814 if (global_catalog_item == NULL) 1815 return (NULL); 1816 1817 if (!is_valid(&ctx->domains_in_forest) || 1818 is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) { 1819 1820 domains_in_forest = ldap_lookup_domains_in_forest( 1821 &ld, global_catalog_item->value); 1822 1823 if (ld != NULL) 1824 (void) ldap_unbind(ld); 1825 1826 if (domains_in_forest == NULL) 1827 return (NULL); 1828 1829 update_item(&ctx->domains_in_forest, domains_in_forest, 1830 AD_STATE_AUTO, 0); 1831 update_version(&ctx->domains_in_forest, PARAM1, 1832 global_catalog_item); 1833 } 1834 return (&ctx->domains_in_forest); 1835 } 1836 1837 1838 ad_disc_domainsinforest_t * 1839 ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered) 1840 { 1841 ad_disc_domainsinforest_t *domains_in_forest = NULL; 1842 ad_item_t *domains_in_forest_item; 1843 1844 domains_in_forest_item = validate_DomainsInForest(ctx); 1845 1846 if (domains_in_forest_item != NULL) { 1847 domains_in_forest = df_dup(domains_in_forest_item->value); 1848 if (auto_discovered != NULL) 1849 *auto_discovered = 1850 (domains_in_forest_item->state == AD_STATE_AUTO); 1851 } else if (auto_discovered != NULL) 1852 *auto_discovered = B_FALSE; 1853 1854 return (domains_in_forest); 1855 } 1856 1857 1858 1859 1860 int 1861 ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName) 1862 { 1863 char *domain_name = NULL; 1864 if (domainName != NULL) { 1865 domain_name = strdup(domainName); 1866 if (domain_name == NULL) 1867 return (-1); 1868 update_item(&ctx->domain_name, domain_name, 1869 AD_STATE_FIXED, 0); 1870 } else if (ctx->domain_name.state == AD_STATE_FIXED) 1871 ctx->domain_name.state = AD_STATE_INVALID; 1872 return (0); 1873 } 1874 1875 1876 int 1877 ad_disc_set_DomainController(ad_disc_t ctx, 1878 const idmap_ad_disc_ds_t *domainController) 1879 { 1880 idmap_ad_disc_ds_t *domain_controller = NULL; 1881 if (domainController != NULL) { 1882 domain_controller = ds_dup(domainController); 1883 if (domain_controller == NULL) 1884 return (-1); 1885 update_item(&ctx->domain_controller, domain_controller, 1886 AD_STATE_FIXED, 0); 1887 } else if (ctx->domain_controller.state == AD_STATE_FIXED) 1888 ctx->domain_controller.state = AD_STATE_INVALID; 1889 return (0); 1890 } 1891 1892 1893 int 1894 ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName) 1895 { 1896 char *site_name = NULL; 1897 if (siteName != NULL) { 1898 site_name = strdup(siteName); 1899 if (site_name == NULL) 1900 return (-1); 1901 update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0); 1902 } else if (ctx->site_name.state == AD_STATE_FIXED) 1903 ctx->site_name.state = AD_STATE_INVALID; 1904 return (0); 1905 } 1906 1907 int 1908 ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName) 1909 { 1910 char *forest_name = NULL; 1911 if (forestName != NULL) { 1912 forest_name = strdup(forestName); 1913 if (forest_name == NULL) 1914 return (-1); 1915 update_item(&ctx->forest_name, forest_name, 1916 AD_STATE_FIXED, 0); 1917 } else if (ctx->forest_name.state == AD_STATE_FIXED) 1918 ctx->forest_name.state = AD_STATE_INVALID; 1919 return (0); 1920 } 1921 1922 int 1923 ad_disc_set_GlobalCatalog(ad_disc_t ctx, 1924 const idmap_ad_disc_ds_t *globalCatalog) 1925 { 1926 idmap_ad_disc_ds_t *global_catalog = NULL; 1927 if (globalCatalog != NULL) { 1928 global_catalog = ds_dup(globalCatalog); 1929 if (global_catalog == NULL) 1930 return (-1); 1931 update_item(&ctx->global_catalog, global_catalog, 1932 AD_STATE_FIXED, 0); 1933 } else if (ctx->global_catalog.state == AD_STATE_FIXED) 1934 ctx->global_catalog.state = AD_STATE_INVALID; 1935 return (0); 1936 } 1937 1938 1939 int 1940 ad_disc_unset(ad_disc_t ctx) 1941 { 1942 if (ctx->domain_name.state == AD_STATE_FIXED) 1943 ctx->domain_name.state = AD_STATE_INVALID; 1944 1945 if (ctx->domain_controller.state == AD_STATE_FIXED) 1946 ctx->domain_controller.state = AD_STATE_INVALID; 1947 1948 if (ctx->site_name.state == AD_STATE_FIXED) 1949 ctx->site_name.state = AD_STATE_INVALID; 1950 1951 if (ctx->forest_name.state == AD_STATE_FIXED) 1952 ctx->forest_name.state = AD_STATE_INVALID; 1953 1954 if (ctx->global_catalog.state == AD_STATE_FIXED) 1955 ctx->global_catalog.state = AD_STATE_INVALID; 1956 1957 return (0); 1958 } 1959 1960 /* 1961 * ad_disc_get_TTL 1962 * 1963 * This routines the time to live for AD 1964 * auto discovered items. 1965 * 1966 * Returns: 1967 * -1 if there are no TTL items 1968 * 0 if there are expired items 1969 * else the number of seconds 1970 * 1971 * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it 1972 * is positive -- min() greater than zero. 1973 */ 1974 #define MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \ 1975 (-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x)))) 1976 int 1977 ad_disc_get_TTL(ad_disc_t ctx) 1978 { 1979 int ttl; 1980 1981 ttl = MIN_GT_ZERO(ctx->domain_controller.ttl, ctx->global_catalog.ttl); 1982 ttl = MIN_GT_ZERO(ttl, ctx->site_domain_controller.ttl); 1983 ttl = MIN_GT_ZERO(ttl, ctx->site_global_catalog.ttl); 1984 1985 if (ttl == -1) 1986 return (-1); 1987 ttl -= time(NULL); 1988 if (ttl < 0) 1989 return (0); 1990 return (ttl); 1991 } 1992 1993 boolean_t 1994 ad_disc_SubnetChanged(ad_disc_t ctx) 1995 { 1996 ad_subnet_t *subnets; 1997 1998 if (ctx->subnets_changed || ctx->subnets == NULL) 1999 return (B_TRUE); 2000 2001 if ((subnets = find_subnets()) != NULL) { 2002 if (cmpsubnets(subnets, ctx->subnets) != 0) 2003 ctx->subnets_changed = B_TRUE; 2004 free(subnets); 2005 } 2006 2007 return (ctx->subnets_changed); 2008 } 2009