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 len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV, 712 msg.buf, sizeof (msg.buf)); 713 logger(LOG_DEBUG, 714 "Querying DNS for SRV RRs named '%s' for '%s' ", 715 svc_name, dname); 716 717 if (len < 0) { 718 logger(LOG_DEBUG, 719 "DNS query for '%s' for '%s' failed (%s)", 720 svc_name, dname, hstrerror(state->res_h_errno)); 721 return (NULL); 722 } 723 } 724 725 if (len > sizeof (msg.buf)) { 726 logger(LOG_ERR, "DNS query %ib message doesn't fit" 727 " into %ib buffer", 728 len, sizeof (msg.buf)); 729 return (NULL); 730 } 731 732 /* 2. parse the reply, skip header and question sections */ 733 734 ptr = msg.buf + sizeof (msg.hdr); 735 eom = msg.buf + len; 736 qdcount = ntohs(msg.hdr.qdcount); 737 ancount = ntohs(msg.hdr.ancount); 738 739 for (cnt = qdcount; cnt > 0; --cnt) { 740 if ((len = dn_skipname(ptr, eom)) < 0) { 741 logger(LOG_ERR, "DNS query invalid message format"); 742 return (NULL); 743 } 744 ptr += len + QFIXEDSZ; 745 } 746 747 /* 3. walk through the answer section */ 748 749 srv_res = calloc(ancount + 1, sizeof (idmap_ad_disc_ds_t)); 750 *ttl = (uint32_t)-1; 751 752 for (srv = srv_res, cnt = ancount; 753 cnt > 0; --cnt, srv++) { 754 755 len = dn_expand(msg.buf, eom, ptr, namebuf, 756 sizeof (namebuf)); 757 if (len < 0) { 758 logger(LOG_ERR, "DNS query invalid message format"); 759 return (NULL); 760 } 761 if (rrname != NULL && *rrname == NULL) 762 *rrname = strdup(namebuf); 763 ptr += len; 764 NS_GET16(type, ptr); 765 NS_GET16(class, ptr); 766 NS_GET32(rttl, ptr); 767 NS_GET16(size, ptr); 768 if ((end = ptr + size) > eom) { 769 logger(LOG_ERR, "DNS query invalid message format"); 770 return (NULL); 771 } 772 773 if (type != T_SRV) { 774 ptr = end; 775 continue; 776 } 777 778 NS_GET16(srv->priority, ptr); 779 NS_GET16(srv->weight, ptr); 780 NS_GET16(srv->port, ptr); 781 len = dn_expand(msg.buf, eom, ptr, srv->host, 782 sizeof (srv->host)); 783 if (len < 0) { 784 logger(LOG_ERR, "DNS query invalid SRV record"); 785 return (NULL); 786 } 787 788 if (rttl < *ttl) 789 *ttl = rttl; 790 791 logger(LOG_DEBUG, "Found %s %d IN SRV [%d][%d] %s:%d", 792 namebuf, rttl, srv->priority, srv->weight, srv->host, 793 srv->port); 794 795 /* 3. move ptr to the end of current record */ 796 797 ptr = end; 798 } 799 800 if (ancount > 1) 801 qsort(srv_res, ancount, sizeof (*srv_res), 802 (int (*)(const void *, const void *))srvcmp); 803 804 return (srv_res); 805 } 806 807 808 /* 809 * A utility function to bind to a Directory server 810 */ 811 812 static LDAP* 813 ldap_lookup_init(idmap_ad_disc_ds_t *ds) 814 { 815 int i; 816 int rc, ldversion; 817 int zero = 0; 818 int timeoutms = 5 * 1000; 819 char *saslmech = "GSSAPI"; 820 uint32_t saslflags = LDAP_SASL_INTERACTIVE; 821 LDAP *ld = NULL; 822 823 for (i = 0; ds[i].host[0] != '\0'; i++) { 824 ld = ldap_init(ds[i].host, ds[i].port); 825 if (ld == NULL) { 826 logger(LOG_DEBUG, "Couldn't connect to " 827 "AD DC %s:%d (%s)", 828 ds[i].host, ds[i].port, 829 strerror(errno)); 830 continue; 831 } 832 833 ldversion = LDAP_VERSION3; 834 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 835 &ldversion); 836 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 = ldap_sasl_interactive_bind_s(ld, "" /* binddn */, 848 saslmech, NULL, NULL, saslflags, &saslcallback, 849 NULL /* defaults */); 850 if (rc == LDAP_SUCCESS) 851 break; 852 853 logger(LOG_INFO, "LDAP SASL bind to %s:%d failed (%s)", 854 ds[i].host, ds[i].port, ldap_err2string(rc)); 855 (void) ldap_unbind(ld); 856 ld = NULL; 857 } 858 return (ld); 859 } 860 861 862 863 /* 864 * A utility function to get the value of some attribute of one of one 865 * or more AD LDAP objects named by the dn_list; first found one wins. 866 */ 867 static char * 868 ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers, 869 char **dn_list, char *attr) 870 { 871 int i; 872 int rc; 873 int scope = LDAP_SCOPE_BASE; 874 char *attrs[2]; 875 LDAPMessage *results = NULL; 876 LDAPMessage *entry; 877 char **values = NULL; 878 char *val = NULL; 879 880 attrs[0] = attr; 881 attrs[1] = NULL; 882 883 if (*ld == NULL) 884 *ld = ldap_lookup_init(domainControllers); 885 886 if (*ld == NULL) 887 return (NULL); 888 889 for (i = 0; dn_list[i] != NULL; i++) { 890 rc = ldap_search_s(*ld, dn_list[i], scope, 891 "(objectclass=*)", attrs, 0, &results); 892 if (rc == LDAP_SUCCESS) { 893 for (entry = ldap_first_entry(*ld, results); 894 entry != NULL && values == NULL; 895 entry = ldap_next_entry(*ld, entry)) { 896 values = ldap_get_values( 897 *ld, entry, attr); 898 } 899 900 if (values != NULL) { 901 (void) ldap_msgfree(results); 902 val = strdup(values[0]); 903 ldap_value_free(values); 904 return (val); 905 } 906 } 907 if (results != NULL) { 908 (void) ldap_msgfree(results); 909 results = NULL; 910 } 911 } 912 913 return (NULL); 914 } 915 916 917 /* 918 * Lookup the trusted domains in the global catalog. 919 * 920 * Returns: 921 * array of trusted domains which is terminated by 922 * an empty trusted domain. 923 * NULL an error occured 924 */ 925 ad_disc_trusteddomains_t * 926 ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog, 927 char *base_dn) 928 { 929 int scope = LDAP_SCOPE_SUBTREE; 930 char *attrs[3]; 931 int rc; 932 LDAPMessage *results = NULL; 933 LDAPMessage *entry; 934 char *filter; 935 char **partner = NULL; 936 char **direction = NULL; 937 int num = 0; 938 ad_disc_trusteddomains_t *trusted_domains = NULL; 939 940 941 if (*ld == NULL) 942 *ld = ldap_lookup_init(globalCatalog); 943 944 if (*ld == NULL) 945 return (NULL); 946 947 attrs[0] = "trustPartner"; 948 attrs[1] = "trustDirection"; 949 attrs[2] = NULL; 950 951 /* trustDirection values - inbound = 1 and bidirectional = 3 */ 952 filter = "(&(objectclass=trustedDomain)" 953 "(|(trustDirection=3)(trustDirection=1)))"; 954 955 rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results); 956 if (rc == LDAP_SUCCESS) { 957 for (entry = ldap_first_entry(*ld, results); 958 entry != NULL; entry = ldap_next_entry(*ld, entry)) { 959 partner = ldap_get_values(*ld, entry, "trustPartner"); 960 direction = ldap_get_values( 961 *ld, entry, "trustDirection"); 962 963 if (partner != NULL && direction != NULL) { 964 num++; 965 trusted_domains = realloc(trusted_domains, 966 (num + 1) * 967 sizeof (ad_disc_trusteddomains_t)); 968 if (trusted_domains == NULL) { 969 ldap_value_free(partner); 970 ldap_value_free(direction); 971 ldap_msgfree(results); 972 return (NULL); 973 } 974 /* Last element should be zero */ 975 memset(&trusted_domains[num], 0, 976 sizeof (ad_disc_trusteddomains_t)); 977 strcpy(trusted_domains[num - 1].domain, 978 partner[0]); 979 trusted_domains[num - 1].direction = 980 atoi(direction[0]); 981 } 982 if (partner != NULL) 983 ldap_value_free(partner); 984 if (direction != NULL) 985 ldap_value_free(direction); 986 } 987 } else if (rc == LDAP_NO_RESULTS_RETURNED) { 988 /* This is not an error - return empty trusted domain */ 989 trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t)); 990 } 991 if (results != NULL) 992 ldap_msgfree(results); 993 994 return (trusted_domains); 995 } 996 997 998 /* 999 * This functions finds all the domains in a forest. 1000 */ 1001 ad_disc_domainsinforest_t * 1002 ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs) 1003 { 1004 static char *attrs[] = { 1005 "objectSid", 1006 NULL, 1007 }; 1008 int rc; 1009 LDAPMessage *result = NULL; 1010 LDAPMessage *entry; 1011 int ndomains = 0; 1012 int nresults; 1013 ad_disc_domainsinforest_t *domains = NULL; 1014 1015 if (*ld == NULL) 1016 *ld = ldap_lookup_init(globalCatalogs); 1017 1018 if (*ld == NULL) 1019 return (NULL); 1020 1021 logger(LOG_DEBUG, "Looking for domains in forest..."); 1022 /* Find domains */ 1023 rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE, 1024 "(objectClass=Domain)", attrs, 0, &result); 1025 if (rc != LDAP_SUCCESS) 1026 goto err; 1027 1028 nresults = ldap_count_entries(*ld, result); 1029 domains = calloc(nresults + 1, sizeof (*domains)); 1030 if (domains == NULL) 1031 goto err; 1032 1033 for (entry = ldap_first_entry(*ld, result); 1034 entry != NULL; 1035 entry = ldap_next_entry(*ld, entry)) { 1036 struct berval **sid_ber; 1037 adutils_sid_t sid; 1038 char *sid_str; 1039 char *name; 1040 1041 sid_ber = ldap_get_values_len(*ld, entry, 1042 "objectSid"); 1043 if (sid_ber == NULL) 1044 continue; 1045 1046 rc = adutils_getsid(sid_ber[0], &sid); 1047 ldap_value_free_len(sid_ber); 1048 if (rc < 0) 1049 goto err; 1050 1051 if ((sid_str = adutils_sid2txt(&sid)) == NULL) 1052 goto err; 1053 1054 strcpy(domains[ndomains].sid, sid_str); 1055 free(sid_str); 1056 1057 name = DN_to_DNS(ldap_get_dn(*ld, entry)); 1058 if (name == NULL) 1059 goto err; 1060 1061 strcpy(domains[ndomains].domain, name); 1062 free(name); 1063 1064 logger(LOG_DEBUG, " found %s", domains[ndomains].domain); 1065 1066 ndomains++; 1067 } 1068 1069 if (ndomains == 0) 1070 goto err; 1071 1072 if (ndomains < nresults) { 1073 ad_disc_domainsinforest_t *tmp; 1074 tmp = realloc(domains, (ndomains + 1) * sizeof (*domains)); 1075 if (tmp == NULL) 1076 goto err; 1077 domains = tmp; 1078 } 1079 1080 if (result != NULL) 1081 ldap_msgfree(result); 1082 1083 return (domains); 1084 1085 err: 1086 free(domains); 1087 if (result != NULL) 1088 ldap_msgfree(result); 1089 return (NULL); 1090 } 1091 1092 1093 ad_disc_t 1094 ad_disc_init(void) 1095 { 1096 struct ad_disc *ctx; 1097 ctx = calloc(1, sizeof (struct ad_disc)); 1098 if (ctx != NULL) 1099 DO_RES_NINIT(ctx); 1100 1101 ctx->domain_name.type = AD_STRING; 1102 ctx->domain_controller.type = AD_DIRECTORY; 1103 ctx->site_name.type = AD_STRING; 1104 ctx->forest_name.type = AD_STRING; 1105 ctx->global_catalog.type = AD_DIRECTORY; 1106 ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST; 1107 ctx->trusted_domains.type = AD_TRUSTED_DOMAINS; 1108 /* Site specific versions */ 1109 ctx->site_domain_controller.type = AD_DIRECTORY; 1110 ctx->site_global_catalog.type = AD_DIRECTORY; 1111 return (ctx); 1112 } 1113 1114 1115 void 1116 ad_disc_fini(ad_disc_t ctx) 1117 { 1118 if (ctx == NULL) 1119 return; 1120 1121 if (ctx->res_ninitted) 1122 res_ndestroy(&ctx->res_state); 1123 1124 if (ctx->subnets != NULL) 1125 free(ctx->subnets); 1126 1127 if (ctx->domain_name.value != NULL) 1128 free(ctx->domain_name.value); 1129 1130 if (ctx->domain_controller.value != NULL) 1131 free(ctx->domain_controller.value); 1132 1133 if (ctx->site_name.value != NULL) 1134 free(ctx->site_name.value); 1135 1136 if (ctx->forest_name.value != NULL) 1137 free(ctx->forest_name.value); 1138 1139 if (ctx->global_catalog.value != NULL) 1140 free(ctx->global_catalog.value); 1141 1142 if (ctx->domains_in_forest.value != NULL) 1143 free(ctx->domains_in_forest.value); 1144 1145 if (ctx->trusted_domains.value != NULL) 1146 free(ctx->trusted_domains.value); 1147 1148 /* Site specific versions */ 1149 if (ctx->site_domain_controller.value != NULL) 1150 free(ctx->site_domain_controller.value); 1151 1152 if (ctx->site_global_catalog.value != NULL) 1153 free(ctx->site_global_catalog.value); 1154 1155 free(ctx); 1156 } 1157 1158 void 1159 ad_disc_refresh(ad_disc_t ctx) 1160 { 1161 if (ctx->res_ninitted) 1162 res_ndestroy(&ctx->res_state); 1163 (void) memset(&ctx->res_state, 0, sizeof (ctx->res_state)); 1164 ctx->res_ninitted = res_ninit(&ctx->res_state) != -1; 1165 1166 if (ctx->domain_name.state == AD_STATE_AUTO) 1167 ctx->domain_name.state = AD_STATE_INVALID; 1168 1169 if (ctx->domain_controller.state == AD_STATE_AUTO) 1170 ctx->domain_controller.state = AD_STATE_INVALID; 1171 1172 if (ctx->site_name.state == AD_STATE_AUTO) 1173 ctx->site_name.state = AD_STATE_INVALID; 1174 1175 if (ctx->forest_name.state == AD_STATE_AUTO) 1176 ctx->forest_name.state = AD_STATE_INVALID; 1177 1178 if (ctx->global_catalog.state == AD_STATE_AUTO) 1179 ctx->global_catalog.state = AD_STATE_INVALID; 1180 1181 if (ctx->domains_in_forest.state == AD_STATE_AUTO) 1182 ctx->domains_in_forest.state = AD_STATE_INVALID; 1183 1184 if (ctx->trusted_domains.state == AD_STATE_AUTO) 1185 ctx->trusted_domains.state = AD_STATE_INVALID; 1186 1187 if (ctx->site_domain_controller.state == AD_STATE_AUTO) 1188 ctx->site_domain_controller.state = AD_STATE_INVALID; 1189 1190 if (ctx->site_global_catalog.state == AD_STATE_AUTO) 1191 ctx->site_global_catalog.state = AD_STATE_INVALID; 1192 } 1193 1194 1195 1196 /* Discover joined Active Directory domainName */ 1197 static ad_item_t * 1198 validate_DomainName(ad_disc_t ctx) 1199 { 1200 idmap_ad_disc_ds_t *domain_controller = NULL; 1201 char *dname, *srvname; 1202 uint32_t ttl = 0; 1203 int len; 1204 1205 if (is_valid(&ctx->domain_name)) 1206 return (&ctx->domain_name); 1207 1208 1209 /* Try to find our domain by searching for DCs for it */ 1210 DO_RES_NINIT(ctx); 1211 domain_controller = srv_query(&ctx->res_state, LDAP_SRV_HEAD 1212 DC_SRV_TAIL, ctx->domain_name.value, &srvname, &ttl); 1213 1214 /* 1215 * If we can't find DCs by via res_nsearch() then there's no 1216 * point in trying anything else to discover the AD domain name. 1217 */ 1218 if (domain_controller == NULL) 1219 return (NULL); 1220 1221 free(domain_controller); 1222 /* 1223 * We have the FQDN of the SRV RR name, so now we extract the 1224 * domainname suffix from it. 1225 */ 1226 dname = strdup(srvname + strlen(LDAP_SRV_HEAD DC_SRV_TAIL) + 1227 1 /* for the dot between RR name and domainname */); 1228 1229 free(srvname); 1230 1231 if (dname == NULL) { 1232 logger(LOG_ERR, "Out of memory"); 1233 return (NULL); 1234 } 1235 1236 /* Eat any trailing dot */ 1237 len = strlen(dname); 1238 if (len > 0 && dname[len - 1] == '.') 1239 dname[len - 1] = '\0'; 1240 1241 update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl); 1242 1243 return (&ctx->domain_name); 1244 } 1245 1246 1247 char * 1248 ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered) 1249 { 1250 char *domain_name = NULL; 1251 ad_item_t *domain_name_item; 1252 1253 domain_name_item = validate_DomainName(ctx); 1254 1255 if (domain_name_item) { 1256 domain_name = strdup(domain_name_item->value); 1257 if (auto_discovered != NULL) 1258 *auto_discovered = 1259 (domain_name_item->state == AD_STATE_AUTO); 1260 } else if (auto_discovered != NULL) 1261 *auto_discovered = B_FALSE; 1262 1263 return (domain_name); 1264 } 1265 1266 1267 /* Discover domain controllers */ 1268 static ad_item_t * 1269 validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) 1270 { 1271 uint32_t ttl = 0; 1272 idmap_ad_disc_ds_t *domain_controller = NULL; 1273 boolean_t validate_global = B_FALSE; 1274 boolean_t validate_site = B_FALSE; 1275 ad_item_t *domain_name_item; 1276 ad_item_t *site_name_item = NULL; 1277 1278 /* If the values is fixed there will not be a site specific version */ 1279 if (is_fixed(&ctx->domain_controller)) 1280 return (&ctx->domain_controller); 1281 1282 domain_name_item = validate_DomainName(ctx); 1283 if (domain_name_item == NULL) 1284 return (NULL); 1285 1286 if (req == AD_DISC_GLOBAL) 1287 validate_global = B_TRUE; 1288 else { 1289 site_name_item = validate_SiteName(ctx); 1290 if (site_name_item != NULL) 1291 validate_site = B_TRUE; 1292 else if (req == AD_DISC_PREFER_SITE) 1293 validate_global = B_TRUE; 1294 } 1295 1296 if (validate_global) { 1297 if (!is_valid(&ctx->domain_controller) || 1298 is_changed(&ctx->domain_controller, PARAM1, 1299 domain_name_item)) { 1300 /* 1301 * Lookup DNS SRV RR named 1302 * _ldap._tcp.dc._msdcs.<DomainName> 1303 */ 1304 DO_RES_NINIT(ctx); 1305 domain_controller = srv_query(&ctx->res_state, 1306 LDAP_SRV_HEAD DC_SRV_TAIL, 1307 domain_name_item->value, NULL, &ttl); 1308 1309 if (domain_controller == NULL) 1310 return (NULL); 1311 1312 update_item(&ctx->domain_controller, domain_controller, 1313 AD_STATE_AUTO, ttl); 1314 update_version(&ctx->domain_controller, PARAM1, 1315 domain_name_item); 1316 } 1317 return (&ctx->domain_controller); 1318 } 1319 1320 if (validate_site) { 1321 if (!is_valid(&ctx->site_domain_controller) || 1322 is_changed(&ctx->site_domain_controller, PARAM1, 1323 domain_name_item) || 1324 is_changed(&ctx->site_domain_controller, PARAM2, 1325 site_name_item)) { 1326 char rr_name[DNS_MAX_NAME]; 1327 /* 1328 * Lookup DNS SRV RR named 1329 * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName> 1330 */ 1331 (void) snprintf(rr_name, sizeof (rr_name), 1332 LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL, 1333 site_name_item->value); 1334 DO_RES_NINIT(ctx); 1335 domain_controller = srv_query(&ctx->res_state, rr_name, 1336 domain_name_item->value, NULL, &ttl); 1337 if (domain_controller == NULL) 1338 return (NULL); 1339 1340 update_item(&ctx->site_domain_controller, 1341 domain_controller, AD_STATE_AUTO, ttl); 1342 update_version(&ctx->site_domain_controller, PARAM1, 1343 domain_name_item); 1344 update_version(&ctx->site_domain_controller, PARAM2, 1345 site_name_item); 1346 } 1347 return (&ctx->site_domain_controller); 1348 } 1349 return (NULL); 1350 } 1351 1352 idmap_ad_disc_ds_t * 1353 ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req, 1354 boolean_t *auto_discovered) 1355 { 1356 ad_item_t *domain_controller_item; 1357 idmap_ad_disc_ds_t *domain_controller = NULL; 1358 1359 domain_controller_item = validate_DomainController(ctx, req); 1360 1361 if (domain_controller_item != NULL) { 1362 domain_controller = ds_dup(domain_controller_item->value); 1363 if (auto_discovered != NULL) 1364 *auto_discovered = 1365 (domain_controller_item->state == AD_STATE_AUTO); 1366 } else if (auto_discovered != NULL) 1367 *auto_discovered = B_FALSE; 1368 1369 return (domain_controller); 1370 } 1371 1372 1373 /* Discover site name (for multi-homed systems the first one found wins) */ 1374 static ad_item_t * 1375 validate_SiteName(ad_disc_t ctx) 1376 { 1377 LDAP *ld = NULL; 1378 ad_subnet_t *subnets = NULL; 1379 char **dn_subnets = NULL; 1380 char *dn_root[2]; 1381 char *config_naming_context = NULL; 1382 char *site_object = NULL; 1383 char *site_name = NULL; 1384 char *forest_name; 1385 int len; 1386 int i; 1387 boolean_t update_required = B_FALSE; 1388 ad_item_t *domain_controller_item; 1389 1390 if (is_fixed(&ctx->site_name)) 1391 return (&ctx->site_name); 1392 1393 /* Can't rely on site-specific DCs */ 1394 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 1395 if (domain_controller_item == NULL) 1396 return (NULL); 1397 1398 if (!is_valid(&ctx->site_name) || 1399 is_changed(&ctx->site_name, PARAM1, &ctx->domain_controller) || 1400 ctx->subnets == NULL || ctx->subnets_changed) { 1401 subnets = find_subnets(); 1402 ctx->subnets_last_check = time(NULL); 1403 update_required = B_TRUE; 1404 } else if (ctx->subnets_last_check + 60 < time(NULL)) { 1405 subnets = find_subnets(); 1406 ctx->subnets_last_check = time(NULL); 1407 if (cmpsubnets(ctx->subnets, subnets) != 0) 1408 update_required = B_TRUE; 1409 } 1410 1411 if (!update_required) { 1412 free(subnets); 1413 return (&ctx->site_name); 1414 } 1415 1416 if (subnets == NULL) 1417 return (NULL); 1418 1419 dn_root[0] = ""; 1420 dn_root[1] = NULL; 1421 1422 config_naming_context = ldap_lookup_entry_attr( 1423 &ld, ctx->domain_controller.value, 1424 dn_root, "configurationNamingContext"); 1425 if (config_naming_context == NULL) 1426 goto out; 1427 /* 1428 * configurationNamingContext also provides the Forest 1429 * Name. 1430 */ 1431 if (!is_fixed(&ctx->forest_name)) { 1432 /* 1433 * The configurationNamingContext should be of 1434 * form: 1435 * CN=Configuration,<DNforestName> 1436 * Remove the first part and convert to DNS form 1437 * (replace ",DC=" with ".") 1438 */ 1439 char *str = "CN=Configuration,"; 1440 int len = strlen(str); 1441 if (strncasecmp(config_naming_context, str, len) == 0) { 1442 forest_name = DN_to_DNS(config_naming_context + len); 1443 update_item(&ctx->forest_name, forest_name, 1444 AD_STATE_AUTO, 0); 1445 } 1446 } 1447 1448 dn_subnets = subnets_to_DNs(subnets, config_naming_context); 1449 if (dn_subnets == NULL) 1450 goto out; 1451 1452 site_object = ldap_lookup_entry_attr( 1453 &ld, domain_controller_item->value, 1454 dn_subnets, "siteobject"); 1455 if (site_object != NULL) { 1456 /* 1457 * The site object should be of the form 1458 * CN=<site>,CN=Sites,CN=Configuration, 1459 * <DN Domain> 1460 */ 1461 if (strncasecmp(site_object, "CN=", 3) == 0) { 1462 for (len = 0; site_object[len + 3] != ','; len++) 1463 ; 1464 site_name = malloc(len + 1); 1465 (void) strncpy(site_name, &site_object[3], len); 1466 site_name[len] = '\0'; 1467 update_item(&ctx->site_name, site_name, 1468 AD_STATE_AUTO, 0); 1469 } 1470 } 1471 1472 if (ctx->subnets != NULL) { 1473 free(ctx->subnets); 1474 ctx->subnets = NULL; 1475 } 1476 ctx->subnets = subnets; 1477 subnets = NULL; 1478 ctx->subnets_changed = B_FALSE; 1479 1480 out: 1481 if (ld != NULL) 1482 (void) ldap_unbind(ld); 1483 1484 if (dn_subnets != NULL) { 1485 for (i = 0; dn_subnets[i] != NULL; i++) 1486 free(dn_subnets[i]); 1487 free(dn_subnets); 1488 } 1489 if (config_naming_context != NULL) 1490 free(config_naming_context); 1491 if (site_object != NULL) 1492 free(site_object); 1493 1494 free(subnets); 1495 if (site_name == NULL) 1496 return (NULL); 1497 return (&ctx->site_name); 1498 1499 } 1500 1501 1502 char * 1503 ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered) 1504 { 1505 ad_item_t *site_name_item; 1506 char *site_name = NULL; 1507 1508 site_name_item = validate_SiteName(ctx); 1509 if (site_name_item != NULL) { 1510 site_name = strdup(site_name_item->value); 1511 if (auto_discovered != NULL) 1512 *auto_discovered = 1513 (site_name_item->state == AD_STATE_AUTO); 1514 } else if (auto_discovered != NULL) 1515 *auto_discovered = B_FALSE; 1516 1517 return (site_name); 1518 } 1519 1520 1521 1522 /* Discover forest name */ 1523 static ad_item_t * 1524 validate_ForestName(ad_disc_t ctx) 1525 { 1526 LDAP *ld = NULL; 1527 char *config_naming_context; 1528 char *forest_name = NULL; 1529 char *dn_list[2]; 1530 ad_item_t *domain_controller_item; 1531 1532 if (is_fixed(&ctx->forest_name)) 1533 return (&ctx->forest_name); 1534 /* 1535 * We may not have a site name yet, so we won't rely on 1536 * site-specific DCs. (But maybe we could replace 1537 * validate_ForestName() with validate_siteName()?) 1538 */ 1539 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 1540 if (domain_controller_item == NULL) 1541 return (NULL); 1542 1543 if (!is_valid(&ctx->forest_name) || 1544 is_changed(&ctx->forest_name, PARAM1, domain_controller_item)) { 1545 1546 dn_list[0] = ""; 1547 dn_list[1] = NULL; 1548 config_naming_context = ldap_lookup_entry_attr( 1549 &ld, ctx->domain_controller.value, 1550 dn_list, "configurationNamingContext"); 1551 if (config_naming_context != NULL) { 1552 /* 1553 * The configurationNamingContext should be of 1554 * form: 1555 * CN=Configuration,<DNforestName> 1556 * Remove the first part and convert to DNS form 1557 * (replace ",DC=" with ".") 1558 */ 1559 char *str = "CN=Configuration,"; 1560 int len = strlen(str); 1561 if (strncasecmp(config_naming_context, str, len) == 0) { 1562 forest_name = DN_to_DNS( 1563 config_naming_context + len); 1564 } 1565 free(config_naming_context); 1566 } 1567 if (ld != NULL) 1568 (void) ldap_unbind(ld); 1569 1570 if (forest_name == NULL) 1571 return (NULL); 1572 1573 update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0); 1574 update_version(&ctx->forest_name, PARAM1, 1575 domain_controller_item); 1576 } 1577 return (&ctx->forest_name); 1578 } 1579 1580 1581 char * 1582 ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered) 1583 { 1584 ad_item_t *forest_name_item; 1585 char *forest_name = NULL; 1586 1587 forest_name_item = validate_ForestName(ctx); 1588 1589 if (forest_name_item != NULL) { 1590 forest_name = strdup(forest_name_item->value); 1591 if (auto_discovered != NULL) 1592 *auto_discovered = 1593 (forest_name_item->state == AD_STATE_AUTO); 1594 } else if (auto_discovered != NULL) 1595 *auto_discovered = B_FALSE; 1596 1597 return (forest_name); 1598 } 1599 1600 1601 /* Discover global catalog servers */ 1602 static ad_item_t * 1603 validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) 1604 { 1605 idmap_ad_disc_ds_t *global_catalog = NULL; 1606 uint32_t ttl = 0; 1607 boolean_t validate_global = B_FALSE; 1608 boolean_t validate_site = B_FALSE; 1609 ad_item_t *forest_name_item; 1610 ad_item_t *site_name_item; 1611 1612 /* If the values is fixed there will not be a site specific version */ 1613 if (is_fixed(&ctx->global_catalog)) 1614 return (&ctx->global_catalog); 1615 1616 forest_name_item = validate_ForestName(ctx); 1617 if (forest_name_item == NULL) 1618 return (NULL); 1619 1620 if (req == AD_DISC_GLOBAL) 1621 validate_global = B_TRUE; 1622 else { 1623 site_name_item = validate_SiteName(ctx); 1624 if (site_name_item != NULL) 1625 validate_site = B_TRUE; 1626 else if (req == AD_DISC_PREFER_SITE) 1627 validate_global = B_TRUE; 1628 } 1629 1630 if (validate_global) { 1631 if (!is_valid(&ctx->global_catalog) || 1632 is_changed(&ctx->global_catalog, PARAM1, 1633 forest_name_item)) { 1634 /* 1635 * Lookup DNS SRV RR named 1636 * _ldap._tcp.gc._msdcs.<ForestName> 1637 */ 1638 DO_RES_NINIT(ctx); 1639 global_catalog = 1640 srv_query(&ctx->res_state, 1641 LDAP_SRV_HEAD GC_SRV_TAIL, 1642 ctx->forest_name.value, NULL, &ttl); 1643 1644 if (global_catalog == NULL) 1645 return (NULL); 1646 1647 update_item(&ctx->global_catalog, global_catalog, 1648 AD_STATE_AUTO, ttl); 1649 update_version(&ctx->global_catalog, PARAM1, 1650 forest_name_item); 1651 } 1652 return (&ctx->global_catalog); 1653 } 1654 1655 if (validate_site) { 1656 if (!is_valid(&ctx->site_global_catalog) || 1657 is_changed(&ctx->site_global_catalog, PARAM1, 1658 forest_name_item) || 1659 is_changed(&ctx->site_global_catalog, PARAM2, 1660 site_name_item)) { 1661 char rr_name[DNS_MAX_NAME]; 1662 1663 /* 1664 * Lookup DNS SRV RR named: 1665 * _ldap._tcp.<siteName>._sites.gc. 1666 * _msdcs.<ForestName> 1667 */ 1668 (void) snprintf(rr_name, 1669 sizeof (rr_name), 1670 LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL, 1671 ctx->site_name.value); 1672 DO_RES_NINIT(ctx); 1673 global_catalog = srv_query(&ctx->res_state, rr_name, 1674 ctx->forest_name.value, NULL, &ttl); 1675 1676 if (global_catalog == NULL) 1677 return (NULL); 1678 update_item(&ctx->site_global_catalog, global_catalog, 1679 AD_STATE_AUTO, ttl); 1680 update_version(&ctx->site_global_catalog, PARAM1, 1681 forest_name_item); 1682 update_version(&ctx->site_global_catalog, PARAM2, 1683 site_name_item); 1684 } 1685 return (&ctx->site_global_catalog); 1686 } 1687 return (NULL); 1688 } 1689 1690 1691 idmap_ad_disc_ds_t * 1692 ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req, 1693 boolean_t *auto_discovered) 1694 { 1695 idmap_ad_disc_ds_t *global_catalog = NULL; 1696 ad_item_t *global_catalog_item; 1697 1698 global_catalog_item = validate_GlobalCatalog(ctx, req); 1699 1700 if (global_catalog_item != NULL) { 1701 global_catalog = ds_dup(global_catalog_item->value); 1702 if (auto_discovered != NULL) 1703 *auto_discovered = 1704 (global_catalog_item->state == AD_STATE_AUTO); 1705 } else if (auto_discovered != NULL) 1706 *auto_discovered = B_FALSE; 1707 1708 return (global_catalog); 1709 } 1710 1711 1712 static ad_item_t * 1713 validate_TrustedDomains(ad_disc_t ctx) 1714 { 1715 LDAP *ld = NULL; 1716 ad_item_t *global_catalog_item; 1717 ad_item_t *forest_name_item; 1718 ad_disc_trusteddomains_t *trusted_domains; 1719 char *dn = NULL; 1720 char *forest_name_dn; 1721 int len; 1722 int num_parts; 1723 1724 if (is_fixed(&ctx->trusted_domains)) 1725 return (&ctx->trusted_domains); 1726 1727 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 1728 if (global_catalog_item == NULL) 1729 return (NULL); 1730 1731 forest_name_item = validate_ForestName(ctx); 1732 if (forest_name_item == NULL) 1733 return (NULL); 1734 1735 if (!is_valid(&ctx->trusted_domains) || 1736 is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) || 1737 is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) { 1738 1739 forest_name_dn = ldap_dns_to_dn(forest_name_item->value, 1740 &num_parts); 1741 if (forest_name_dn == NULL) 1742 return (NULL); 1743 1744 len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1; 1745 dn = malloc(len); 1746 if (dn == NULL) { 1747 free(forest_name_dn); 1748 return (NULL); 1749 } 1750 (void) snprintf(dn, len, "CN=System,%s", forest_name_dn); 1751 free(forest_name_dn); 1752 1753 trusted_domains = ldap_lookup_trusted_domains( 1754 &ld, global_catalog_item->value, dn); 1755 1756 if (ld != NULL) 1757 (void) ldap_unbind(ld); 1758 free(dn); 1759 1760 if (trusted_domains == NULL) 1761 return (NULL); 1762 1763 update_item(&ctx->trusted_domains, trusted_domains, 1764 AD_STATE_AUTO, 0); 1765 update_version(&ctx->trusted_domains, PARAM1, 1766 global_catalog_item); 1767 update_version(&ctx->trusted_domains, PARAM2, 1768 forest_name_item); 1769 } 1770 1771 return (&ctx->trusted_domains); 1772 } 1773 1774 1775 ad_disc_trusteddomains_t * 1776 ad_disc_get_TrustedDomains(ad_disc_t ctx, boolean_t *auto_discovered) 1777 { 1778 ad_disc_trusteddomains_t *trusted_domains = NULL; 1779 ad_item_t *trusted_domains_item; 1780 1781 trusted_domains_item = validate_TrustedDomains(ctx); 1782 1783 if (trusted_domains_item != NULL) { 1784 trusted_domains = td_dup(trusted_domains_item->value); 1785 if (auto_discovered != NULL) 1786 *auto_discovered = 1787 (trusted_domains_item->state == AD_STATE_AUTO); 1788 } else if (auto_discovered != NULL) 1789 *auto_discovered = B_FALSE; 1790 1791 return (trusted_domains); 1792 } 1793 1794 1795 static ad_item_t * 1796 validate_DomainsInForest(ad_disc_t ctx) 1797 { 1798 ad_item_t *global_catalog_item; 1799 LDAP *ld = NULL; 1800 ad_disc_domainsinforest_t *domains_in_forest; 1801 1802 if (is_fixed(&ctx->domains_in_forest)) 1803 return (&ctx->domains_in_forest); 1804 1805 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 1806 if (global_catalog_item == NULL) 1807 return (NULL); 1808 1809 if (!is_valid(&ctx->domains_in_forest) || 1810 is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) { 1811 1812 domains_in_forest = ldap_lookup_domains_in_forest( 1813 &ld, global_catalog_item->value); 1814 1815 if (ld != NULL) 1816 (void) ldap_unbind(ld); 1817 1818 if (domains_in_forest == NULL) 1819 return (NULL); 1820 1821 update_item(&ctx->domains_in_forest, domains_in_forest, 1822 AD_STATE_AUTO, 0); 1823 update_version(&ctx->domains_in_forest, PARAM1, 1824 global_catalog_item); 1825 } 1826 return (&ctx->domains_in_forest); 1827 } 1828 1829 1830 ad_disc_domainsinforest_t * 1831 ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered) 1832 { 1833 ad_disc_domainsinforest_t *domains_in_forest = NULL; 1834 ad_item_t *domains_in_forest_item; 1835 1836 domains_in_forest_item = validate_DomainsInForest(ctx); 1837 1838 if (domains_in_forest_item != NULL) { 1839 domains_in_forest = df_dup(domains_in_forest_item->value); 1840 if (auto_discovered != NULL) 1841 *auto_discovered = 1842 (domains_in_forest_item->state == AD_STATE_AUTO); 1843 } else if (auto_discovered != NULL) 1844 *auto_discovered = B_FALSE; 1845 1846 return (domains_in_forest); 1847 } 1848 1849 1850 1851 1852 int 1853 ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName) 1854 { 1855 char *domain_name = NULL; 1856 if (domainName != NULL) { 1857 domain_name = strdup(domainName); 1858 if (domain_name == NULL) 1859 return (-1); 1860 update_item(&ctx->domain_name, domain_name, 1861 AD_STATE_FIXED, 0); 1862 } else if (ctx->domain_name.state == AD_STATE_FIXED) 1863 ctx->domain_name.state = AD_STATE_INVALID; 1864 return (0); 1865 } 1866 1867 1868 int 1869 ad_disc_set_DomainController(ad_disc_t ctx, 1870 const idmap_ad_disc_ds_t *domainController) 1871 { 1872 idmap_ad_disc_ds_t *domain_controller = NULL; 1873 if (domainController != NULL) { 1874 domain_controller = ds_dup(domainController); 1875 if (domain_controller == NULL) 1876 return (-1); 1877 update_item(&ctx->domain_controller, domain_controller, 1878 AD_STATE_FIXED, 0); 1879 } else if (ctx->domain_controller.state == AD_STATE_FIXED) 1880 ctx->domain_controller.state = AD_STATE_INVALID; 1881 return (0); 1882 } 1883 1884 1885 int 1886 ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName) 1887 { 1888 char *site_name = NULL; 1889 if (siteName != NULL) { 1890 site_name = strdup(siteName); 1891 if (site_name == NULL) 1892 return (-1); 1893 update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0); 1894 } else if (ctx->site_name.state == AD_STATE_FIXED) 1895 ctx->site_name.state = AD_STATE_INVALID; 1896 return (0); 1897 } 1898 1899 int 1900 ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName) 1901 { 1902 char *forest_name = NULL; 1903 if (forestName != NULL) { 1904 forest_name = strdup(forestName); 1905 if (forest_name == NULL) 1906 return (-1); 1907 update_item(&ctx->forest_name, forest_name, 1908 AD_STATE_FIXED, 0); 1909 } else if (ctx->forest_name.state == AD_STATE_FIXED) 1910 ctx->forest_name.state = AD_STATE_INVALID; 1911 return (0); 1912 } 1913 1914 int 1915 ad_disc_set_GlobalCatalog(ad_disc_t ctx, 1916 const idmap_ad_disc_ds_t *globalCatalog) 1917 { 1918 idmap_ad_disc_ds_t *global_catalog = NULL; 1919 if (globalCatalog != NULL) { 1920 global_catalog = ds_dup(globalCatalog); 1921 if (global_catalog == NULL) 1922 return (-1); 1923 update_item(&ctx->global_catalog, global_catalog, 1924 AD_STATE_FIXED, 0); 1925 } else if (ctx->global_catalog.state == AD_STATE_FIXED) 1926 ctx->global_catalog.state = AD_STATE_INVALID; 1927 return (0); 1928 } 1929 1930 1931 int 1932 ad_disc_unset(ad_disc_t ctx) 1933 { 1934 if (ctx->domain_name.state == AD_STATE_FIXED) 1935 ctx->domain_name.state = AD_STATE_INVALID; 1936 1937 if (ctx->domain_controller.state == AD_STATE_FIXED) 1938 ctx->domain_controller.state = AD_STATE_INVALID; 1939 1940 if (ctx->site_name.state == AD_STATE_FIXED) 1941 ctx->site_name.state = AD_STATE_INVALID; 1942 1943 if (ctx->forest_name.state == AD_STATE_FIXED) 1944 ctx->forest_name.state = AD_STATE_INVALID; 1945 1946 if (ctx->global_catalog.state == AD_STATE_FIXED) 1947 ctx->global_catalog.state = AD_STATE_INVALID; 1948 1949 return (0); 1950 } 1951 1952 /* 1953 * ad_disc_get_TTL 1954 * 1955 * This routines the time to live for AD 1956 * auto discovered items. 1957 * 1958 * Returns: 1959 * -1 if there are no TTL items 1960 * 0 if there are expired items 1961 * else the number of seconds 1962 * 1963 * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it 1964 * is positive -- min() greater than zero. 1965 */ 1966 #define MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \ 1967 (-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x)))) 1968 int 1969 ad_disc_get_TTL(ad_disc_t ctx) 1970 { 1971 int ttl; 1972 1973 ttl = MIN_GT_ZERO(ctx->domain_controller.ttl, ctx->global_catalog.ttl); 1974 ttl = MIN_GT_ZERO(ttl, ctx->site_domain_controller.ttl); 1975 ttl = MIN_GT_ZERO(ttl, ctx->site_global_catalog.ttl); 1976 1977 if (ttl == -1) 1978 return (-1); 1979 ttl -= time(NULL); 1980 if (ttl < 0) 1981 return (0); 1982 return (ttl); 1983 } 1984 1985 boolean_t 1986 ad_disc_SubnetChanged(ad_disc_t ctx) 1987 { 1988 ad_subnet_t *subnets; 1989 1990 if (ctx->subnets_changed || ctx->subnets == NULL) 1991 return (B_TRUE); 1992 1993 if ((subnets = find_subnets()) != NULL) { 1994 if (cmpsubnets(subnets, ctx->subnets) != 0) 1995 ctx->subnets_changed = B_TRUE; 1996 free(subnets); 1997 } 1998 1999 return (ctx->subnets_changed); 2000 } 2001