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