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