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 int err; 318 319 for (i = 0; td1[i].domain[0] != '\0'; i++) 320 continue; 321 num_td1 = i; 322 323 for (j = 0; td2[j].domain[0] != '\0'; j++) 324 continue; 325 num_td2 = j; 326 327 if (num_td1 != num_td2) 328 return (1); 329 330 for (i = 0; i < num_td1; i++) { 331 match = B_FALSE; 332 for (j = 0; j < num_td2; j++) { 333 if (u8_strcmp(td1[i].domain, td2[j].domain, 0, 334 U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) == 0 && 335 err == 0) { 336 match = B_TRUE; 337 break; 338 } 339 } 340 if (!match) 341 return (1); 342 } 343 return (0); 344 } 345 346 347 348 /* Copy a list of Trusted Domains */ 349 static ad_disc_trusteddomains_t * 350 td_dup(const ad_disc_trusteddomains_t *td) 351 { 352 int i; 353 int size; 354 ad_disc_trusteddomains_t *new = NULL; 355 356 for (i = 0; td[i].domain[0] != '\0'; i++) 357 continue; 358 359 size = (i + 1) * sizeof (ad_disc_trusteddomains_t); 360 new = malloc(size); 361 if (new != NULL) 362 memcpy(new, td, size); 363 return (new); 364 } 365 366 367 368 int 369 ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *df1, 370 ad_disc_domainsinforest_t *df2) 371 { 372 int i, j; 373 int num_df1; 374 int num_df2; 375 boolean_t match; 376 int err; 377 378 for (i = 0; df1[i].domain[0] != '\0'; i++) 379 continue; 380 num_df1 = i; 381 382 for (j = 0; df2[j].domain[0] != '\0'; j++) 383 continue; 384 num_df2 = j; 385 386 if (num_df1 != num_df2) 387 return (1); 388 389 for (i = 0; i < num_df1; i++) { 390 match = B_FALSE; 391 for (j = 0; j < num_df2; j++) { 392 if (u8_strcmp(df1[i].domain, df2[j].domain, 0, 393 U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &err) == 0 && 394 err == 0 && 395 strcmp(df1[i].sid, df2[j].sid) == 0) { 396 match = B_TRUE; 397 break; 398 } 399 } 400 if (!match) 401 return (1); 402 } 403 return (0); 404 } 405 406 407 408 /* Copy a list of Trusted Domains */ 409 static ad_disc_domainsinforest_t * 410 df_dup(const ad_disc_domainsinforest_t *df) 411 { 412 int i; 413 int size; 414 ad_disc_domainsinforest_t *new = NULL; 415 416 for (i = 0; df[i].domain[0] != '\0'; i++) 417 continue; 418 419 size = (i + 1) * sizeof (ad_disc_domainsinforest_t); 420 new = malloc(size); 421 if (new != NULL) 422 memcpy(new, df, size); 423 return (new); 424 } 425 426 427 428 429 430 /* 431 * Returns an array of IPv4 address/prefix length 432 * The last subnet is NULL 433 */ 434 static ad_subnet_t * 435 find_subnets() 436 { 437 int sock, n, i; 438 struct lifconf lifc; 439 struct lifreq lifr, *lifrp; 440 struct lifnum lifn; 441 uint32_t prefix_len; 442 char *s; 443 ad_subnet_t *results; 444 445 lifrp = &lifr; 446 447 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 448 logger(LOG_ERR, "Failed to open IPv4 socket for " 449 "listing network interfaces (%s)", strerror(errno)); 450 return (NULL); 451 } 452 453 lifn.lifn_family = AF_INET; 454 lifn.lifn_flags = 0; 455 if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) { 456 logger(LOG_ERR, 457 "Failed to find the number of network interfaces (%s)", 458 strerror(errno)); 459 close(sock); 460 return (NULL); 461 } 462 463 if (lifn.lifn_count < 1) { 464 logger(LOG_ERR, "No IPv4 network interfaces found"); 465 close(sock); 466 return (NULL); 467 } 468 469 lifc.lifc_family = AF_INET; 470 lifc.lifc_flags = 0; 471 lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq); 472 lifc.lifc_buf = malloc(lifc.lifc_len); 473 474 if (lifc.lifc_buf == NULL) { 475 logger(LOG_ERR, "Out of memory"); 476 close(sock); 477 return (NULL); 478 } 479 480 if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) { 481 logger(LOG_ERR, "Failed to list network interfaces (%s)", 482 strerror(errno)); 483 free(lifc.lifc_buf); 484 close(sock); 485 return (NULL); 486 } 487 488 n = lifc.lifc_len / (int)sizeof (struct lifreq); 489 490 if ((results = calloc(n + 1, sizeof (ad_subnet_t))) == NULL) { 491 free(lifc.lifc_buf); 492 close(sock); 493 return (NULL); 494 } 495 496 for (i = 0, lifrp = lifc.lifc_req; i < n; i++, lifrp++) { 497 if (ioctl(sock, SIOCGLIFFLAGS, lifrp) < 0) 498 continue; 499 500 if ((lifrp->lifr_flags & IFF_UP) == 0) 501 continue; 502 503 if (ioctl(sock, SIOCGLIFSUBNET, lifrp) < 0) 504 continue; 505 506 prefix_len = lifrp->lifr_addrlen; 507 508 s = inet_ntoa(((struct sockaddr_in *) 509 &lifrp->lifr_addr)->sin_addr); 510 511 (void) snprintf(results[i].subnet, sizeof (ad_subnet_t), 512 "%s/%d", s, prefix_len); 513 } 514 515 free(lifc.lifc_buf); 516 close(sock); 517 518 return (results); 519 } 520 521 static int 522 cmpsubnets(ad_subnet_t *subnets1, ad_subnet_t *subnets2) 523 { 524 int num_subnets1; 525 int num_subnets2; 526 boolean_t matched; 527 int i, j; 528 529 for (i = 0; subnets1[i].subnet[0] != '\0'; i++) 530 continue; 531 num_subnets1 = i; 532 533 for (i = 0; subnets2[i].subnet[0] != '\0'; i++) 534 continue; 535 num_subnets2 = i; 536 537 if (num_subnets1 != num_subnets2) 538 return (1); 539 540 for (i = 0; i < num_subnets1; i++) { 541 matched = B_FALSE; 542 for (j = 0; j < num_subnets2; j++) { 543 if (strcmp(subnets1[i].subnet, 544 subnets2[j].subnet) == 0) { 545 matched = B_TRUE; 546 break; 547 } 548 } 549 if (!matched) 550 return (1); 551 } 552 return (0); 553 } 554 555 556 557 558 /* Convert a DN's DC components into a DNS domainname */ 559 char * 560 DN_to_DNS(const char *dn_name) 561 { 562 char dns[DNS_MAX_NAME]; 563 char *dns_name; 564 int i, j; 565 int num = 0; 566 567 j = 0; 568 i = 0; 569 570 if (dn_name == NULL) 571 return (NULL); 572 /* 573 * Find all DC=<value> and form DNS name of the 574 * form <value1>.<value2>... 575 */ 576 while (dn_name[i] != '\0') { 577 if (strncasecmp(&dn_name[i], "DC=", 3) == 0) { 578 i += 3; 579 if (dn_name[i] != '\0' && num > 0) 580 dns[j++] = '.'; 581 while (dn_name[i] != '\0' && 582 dn_name[i] != ',' && dn_name[i] != '+') 583 dns[j++] = dn_name[i++]; 584 num++; 585 } else { 586 /* Skip attr=value as it is not DC= */ 587 while (dn_name[i] != '\0' && 588 dn_name[i] != ',' && dn_name[i] != '+') 589 i++; 590 } 591 /* Skip over separator ',' or '+' */ 592 if (dn_name[i] != '\0') i++; 593 } 594 dns[j] = '\0'; 595 dns_name = malloc(j + 1); 596 if (dns_name != NULL) 597 (void) strlcpy(dns_name, dns, j + 1); 598 return (dns_name); 599 } 600 601 602 /* Format the DN of an AD LDAP subnet object for some subnet */ 603 static char * 604 subnet_to_DN(const char *subnet, const char *baseDN) 605 { 606 char *result; 607 int len; 608 609 len = snprintf(NULL, 0, 610 "CN=%s,CN=Subnets,CN=Sites,%s", 611 subnet, baseDN) + 1; 612 613 result = malloc(len); 614 if (result != NULL) 615 (void) snprintf(result, len, 616 "CN=%s,CN=Subnets,CN=Sites,%s", 617 subnet, baseDN); 618 return (result); 619 } 620 621 622 /* Make a list of subnet object DNs from a list of subnets */ 623 static char ** 624 subnets_to_DNs(ad_subnet_t *subnets, const char *base_dn) 625 { 626 char **results; 627 int i, j; 628 629 for (i = 0; subnets[i].subnet[0] != '\0'; i++) 630 continue; 631 632 results = calloc(i + 1, sizeof (char *)); 633 if (results == NULL) 634 return (NULL); 635 636 for (i = 0; subnets[i].subnet[0] != '\0'; i++) { 637 if ((results[i] = subnet_to_DN(subnets[i].subnet, base_dn)) 638 == NULL) { 639 for (j = 0; j < i; j++) 640 free(results[j]); 641 free(results); 642 return (NULL); 643 } 644 } 645 646 return (results); 647 } 648 649 650 /* Compare SRC RRs; used with qsort() */ 651 static int 652 srvcmp(idmap_ad_disc_ds_t *s1, idmap_ad_disc_ds_t *s2) 653 { 654 if (s1->priority < s2->priority) 655 return (1); 656 else if (s1->priority > s2->priority) 657 return (-1); 658 659 if (s1->weight < s2->weight) 660 return (1); 661 else if (s1->weight > s2->weight) 662 return (-1); 663 664 return (0); 665 } 666 667 668 /* 669 * Query or search the SRV RRs for a given name. 670 * 671 * If name == NULL then search (as in res_nsearch(3RESOLV), honoring any 672 * search list/option), else query (as in res_nquery(3RESOLV)). 673 * 674 * The output TTL will be the one of the SRV RR with the lowest TTL. 675 */ 676 idmap_ad_disc_ds_t * 677 srv_query(res_state state, const char *svc_name, const char *dname, 678 char **rrname, uint32_t *ttl) 679 { 680 idmap_ad_disc_ds_t *srv; 681 idmap_ad_disc_ds_t *srv_res; 682 union { 683 HEADER hdr; 684 uchar_t buf[NS_MAXMSG]; 685 } msg; 686 int len, cnt, qdcount, ancount; 687 uchar_t *ptr, *eom; 688 uchar_t *end; 689 uint16_t type; 690 /* LINTED E_FUNC_SET_NOT_USED */ 691 uint16_t class; 692 uint32_t rttl; 693 uint16_t size; 694 char namebuf[NS_MAXDNAME]; 695 696 if (state == NULL) 697 return (NULL); 698 699 /* Set negative result TTL */ 700 *ttl = 5 * 60; 701 702 /* 1. query necessary resource records */ 703 704 /* Search, querydomain or query */ 705 if (rrname != NULL) { 706 *rrname = NULL; 707 len = res_nsearch(state, svc_name, C_IN, T_SRV, 708 msg.buf, sizeof (msg.buf)); 709 logger(LOG_DEBUG, "Searching DNS for SRV RRs named '%s'", 710 svc_name); 711 if (len < 0) { 712 logger(LOG_DEBUG, "DNS search for '%s' failed (%s)", 713 svc_name, hstrerror(state->res_h_errno)); 714 return (NULL); 715 } 716 } else if (dname != NULL) { 717 len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV, 718 msg.buf, sizeof (msg.buf)); 719 logger(LOG_DEBUG, 720 "Querying DNS for SRV RRs named '%s' for '%s' ", 721 svc_name, dname); 722 723 if (len < 0) { 724 logger(LOG_DEBUG, 725 "DNS query for '%s' for '%s' failed (%s)", 726 svc_name, dname, hstrerror(state->res_h_errno)); 727 return (NULL); 728 } 729 } 730 731 if (len > sizeof (msg.buf)) { 732 logger(LOG_ERR, "DNS query %ib message doesn't fit" 733 " into %ib buffer", 734 len, sizeof (msg.buf)); 735 return (NULL); 736 } 737 738 /* 2. parse the reply, skip header and question sections */ 739 740 ptr = msg.buf + sizeof (msg.hdr); 741 eom = msg.buf + len; 742 qdcount = ntohs(msg.hdr.qdcount); 743 ancount = ntohs(msg.hdr.ancount); 744 745 for (cnt = qdcount; cnt > 0; --cnt) { 746 if ((len = dn_skipname(ptr, eom)) < 0) { 747 logger(LOG_ERR, "DNS query invalid message format"); 748 return (NULL); 749 } 750 ptr += len + QFIXEDSZ; 751 } 752 753 /* 3. walk through the answer section */ 754 755 srv_res = calloc(ancount + 1, sizeof (idmap_ad_disc_ds_t)); 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 return (NULL); 766 } 767 if (rrname != NULL && *rrname == NULL) 768 *rrname = strdup(namebuf); 769 ptr += len; 770 NS_GET16(type, ptr); 771 NS_GET16(class, ptr); 772 NS_GET32(rttl, ptr); 773 NS_GET16(size, ptr); 774 if ((end = ptr + size) > eom) { 775 logger(LOG_ERR, "DNS query invalid message format"); 776 return (NULL); 777 } 778 779 if (type != T_SRV) { 780 ptr = end; 781 continue; 782 } 783 784 NS_GET16(srv->priority, ptr); 785 NS_GET16(srv->weight, ptr); 786 NS_GET16(srv->port, ptr); 787 len = dn_expand(msg.buf, eom, ptr, srv->host, 788 sizeof (srv->host)); 789 if (len < 0) { 790 logger(LOG_ERR, "DNS query invalid SRV record"); 791 return (NULL); 792 } 793 794 if (rttl < *ttl) 795 *ttl = rttl; 796 797 logger(LOG_DEBUG, "Found %s %d IN SRV [%d][%d] %s:%d", 798 namebuf, rttl, srv->priority, srv->weight, srv->host, 799 srv->port); 800 801 /* 3. move ptr to the end of current record */ 802 803 ptr = end; 804 } 805 806 if (ancount > 1) 807 qsort(srv_res, ancount, sizeof (*srv_res), 808 (int (*)(const void *, const void *))srvcmp); 809 810 return (srv_res); 811 } 812 813 814 /* 815 * A utility function to bind to a Directory server 816 */ 817 818 static LDAP* 819 ldap_lookup_init(idmap_ad_disc_ds_t *ds) 820 { 821 int i; 822 int rc, ldversion; 823 int zero = 0; 824 int timeoutms = 5 * 1000; 825 char *saslmech = "GSSAPI"; 826 uint32_t saslflags = LDAP_SASL_INTERACTIVE; 827 LDAP *ld = NULL; 828 829 for (i = 0; ds[i].host[0] != '\0'; i++) { 830 ld = ldap_init(ds[i].host, ds[i].port); 831 if (ld == NULL) { 832 logger(LOG_DEBUG, "Couldn't connect to " 833 "AD DC %s:%d (%s)", 834 ds[i].host, ds[i].port, 835 strerror(errno)); 836 continue; 837 } 838 839 ldversion = LDAP_VERSION3; 840 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 841 &ldversion); 842 843 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, 844 LDAP_OPT_OFF); 845 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero); 846 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero); 847 /* setup TCP/IP connect timeout */ 848 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, 849 &timeoutms); 850 (void) ldap_set_option(ld, LDAP_OPT_RESTART, 851 LDAP_OPT_ON); 852 853 rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */, 854 saslmech, NULL, NULL, saslflags, &saslcallback, 855 NULL /* defaults */); 856 if (rc == LDAP_SUCCESS) 857 break; 858 859 logger(LOG_INFO, "LDAP SASL bind to %s:%d failed (%s)", 860 ds[i].host, ds[i].port, ldap_err2string(rc)); 861 (void) ldap_unbind(ld); 862 ld = NULL; 863 } 864 return (ld); 865 } 866 867 868 869 /* 870 * A utility function to get the value of some attribute of one of one 871 * or more AD LDAP objects named by the dn_list; first found one wins. 872 */ 873 static char * 874 ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers, 875 char **dn_list, char *attr) 876 { 877 int i; 878 int rc; 879 int scope = LDAP_SCOPE_BASE; 880 char *attrs[2]; 881 LDAPMessage *results = NULL; 882 LDAPMessage *entry; 883 char **values = NULL; 884 char *val = NULL; 885 886 attrs[0] = attr; 887 attrs[1] = NULL; 888 889 if (*ld == NULL) 890 *ld = ldap_lookup_init(domainControllers); 891 892 if (*ld == NULL) 893 return (NULL); 894 895 for (i = 0; dn_list[i] != NULL; i++) { 896 rc = ldap_search_s(*ld, dn_list[i], scope, 897 "(objectclass=*)", attrs, 0, &results); 898 if (rc == LDAP_SUCCESS) { 899 for (entry = ldap_first_entry(*ld, results); 900 entry != NULL && values == NULL; 901 entry = ldap_next_entry(*ld, entry)) { 902 values = ldap_get_values( 903 *ld, entry, attr); 904 } 905 906 if (values != NULL) { 907 (void) ldap_msgfree(results); 908 val = strdup(values[0]); 909 ldap_value_free(values); 910 return (val); 911 } 912 } 913 if (results != NULL) { 914 (void) ldap_msgfree(results); 915 results = NULL; 916 } 917 } 918 919 return (NULL); 920 } 921 922 923 /* 924 * Lookup the trusted domains in the global catalog. 925 * 926 * Returns: 927 * array of trusted domains which is terminated by 928 * an empty trusted domain. 929 * NULL an error occured 930 */ 931 ad_disc_trusteddomains_t * 932 ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog, 933 char *base_dn) 934 { 935 int scope = LDAP_SCOPE_SUBTREE; 936 char *attrs[3]; 937 int rc; 938 LDAPMessage *results = NULL; 939 LDAPMessage *entry; 940 char *filter; 941 char **partner = NULL; 942 char **direction = NULL; 943 int num = 0; 944 ad_disc_trusteddomains_t *trusted_domains = NULL; 945 946 947 if (*ld == NULL) 948 *ld = ldap_lookup_init(globalCatalog); 949 950 if (*ld == NULL) 951 return (NULL); 952 953 attrs[0] = "trustPartner"; 954 attrs[1] = "trustDirection"; 955 attrs[2] = NULL; 956 957 /* trustDirection values - inbound = 1 and bidirectional = 3 */ 958 filter = "(&(objectclass=trustedDomain)" 959 "(|(trustDirection=3)(trustDirection=1)))"; 960 961 rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results); 962 if (rc == LDAP_SUCCESS) { 963 for (entry = ldap_first_entry(*ld, results); 964 entry != NULL; entry = ldap_next_entry(*ld, entry)) { 965 partner = ldap_get_values(*ld, entry, "trustPartner"); 966 direction = ldap_get_values( 967 *ld, entry, "trustDirection"); 968 969 if (partner != NULL && direction != NULL) { 970 num++; 971 trusted_domains = realloc(trusted_domains, 972 (num + 1) * 973 sizeof (ad_disc_trusteddomains_t)); 974 if (trusted_domains == NULL) { 975 ldap_value_free(partner); 976 ldap_value_free(direction); 977 ldap_msgfree(results); 978 return (NULL); 979 } 980 /* Last element should be zero */ 981 memset(&trusted_domains[num], 0, 982 sizeof (ad_disc_trusteddomains_t)); 983 strcpy(trusted_domains[num - 1].domain, 984 partner[0]); 985 trusted_domains[num - 1].direction = 986 atoi(direction[0]); 987 } 988 if (partner != NULL) 989 ldap_value_free(partner); 990 if (direction != NULL) 991 ldap_value_free(direction); 992 } 993 } else if (rc == LDAP_NO_RESULTS_RETURNED) { 994 /* This is not an error - return empty trusted domain */ 995 trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t)); 996 } 997 if (results != NULL) 998 ldap_msgfree(results); 999 1000 return (trusted_domains); 1001 } 1002 1003 1004 /* 1005 * This functions finds all the domains in a forest. 1006 */ 1007 ad_disc_domainsinforest_t * 1008 ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs) 1009 { 1010 static char *attrs[] = { 1011 "objectSid", 1012 NULL, 1013 }; 1014 int rc; 1015 LDAPMessage *result = NULL; 1016 LDAPMessage *entry; 1017 int ndomains = 0; 1018 int nresults; 1019 ad_disc_domainsinforest_t *domains = NULL; 1020 1021 if (*ld == NULL) 1022 *ld = ldap_lookup_init(globalCatalogs); 1023 1024 if (*ld == NULL) 1025 return (NULL); 1026 1027 logger(LOG_DEBUG, "Looking for domains in forest..."); 1028 /* Find domains */ 1029 rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE, 1030 "(objectClass=Domain)", attrs, 0, &result); 1031 if (rc != LDAP_SUCCESS) 1032 goto err; 1033 1034 nresults = ldap_count_entries(*ld, result); 1035 domains = calloc(nresults + 1, sizeof (*domains)); 1036 if (domains == NULL) 1037 goto err; 1038 1039 for (entry = ldap_first_entry(*ld, result); 1040 entry != NULL; 1041 entry = ldap_next_entry(*ld, entry)) { 1042 struct berval **sid_ber; 1043 adutils_sid_t sid; 1044 char *sid_str; 1045 char *name; 1046 1047 sid_ber = ldap_get_values_len(*ld, entry, 1048 "objectSid"); 1049 if (sid_ber == NULL) 1050 continue; 1051 1052 rc = adutils_getsid(sid_ber[0], &sid); 1053 ldap_value_free_len(sid_ber); 1054 if (rc < 0) 1055 goto err; 1056 1057 if ((sid_str = adutils_sid2txt(&sid)) == NULL) 1058 goto err; 1059 1060 strcpy(domains[ndomains].sid, sid_str); 1061 free(sid_str); 1062 1063 name = DN_to_DNS(ldap_get_dn(*ld, entry)); 1064 if (name == NULL) 1065 goto err; 1066 1067 strcpy(domains[ndomains].domain, name); 1068 free(name); 1069 1070 logger(LOG_DEBUG, " found %s", domains[ndomains].domain); 1071 1072 ndomains++; 1073 } 1074 1075 if (ndomains == 0) 1076 goto err; 1077 1078 if (ndomains < nresults) { 1079 ad_disc_domainsinforest_t *tmp; 1080 tmp = realloc(domains, (ndomains+1) * sizeof (*domains)); 1081 if (tmp == NULL) 1082 goto err; 1083 domains = tmp; 1084 } 1085 1086 if (result != NULL) 1087 ldap_msgfree(result); 1088 1089 return (domains); 1090 1091 err: 1092 free(domains); 1093 if (result != NULL) 1094 ldap_msgfree(result); 1095 return (NULL); 1096 } 1097 1098 1099 ad_disc_t 1100 ad_disc_init(void) 1101 { 1102 struct ad_disc *ctx; 1103 ctx = calloc(1, sizeof (struct ad_disc)); 1104 if (ctx != NULL) 1105 DO_RES_NINIT(ctx); 1106 1107 ctx->domain_name.type = AD_STRING; 1108 ctx->domain_controller.type = AD_DIRECTORY; 1109 ctx->site_name.type = AD_STRING; 1110 ctx->forest_name.type = AD_STRING; 1111 ctx->global_catalog.type = AD_DIRECTORY; 1112 ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST; 1113 ctx->trusted_domains.type = AD_TRUSTED_DOMAINS; 1114 /* Site specific versions */ 1115 ctx->site_domain_controller.type = AD_DIRECTORY; 1116 ctx->site_global_catalog.type = AD_DIRECTORY; 1117 return (ctx); 1118 } 1119 1120 1121 void 1122 ad_disc_fini(ad_disc_t ctx) 1123 { 1124 if (ctx == NULL) 1125 return; 1126 1127 if (ctx->res_ninitted) 1128 res_ndestroy(&ctx->res_state); 1129 1130 if (ctx->subnets != NULL) 1131 free(ctx->subnets); 1132 1133 if (ctx->domain_name.value != NULL) 1134 free(ctx->domain_name.value); 1135 1136 if (ctx->domain_controller.value != NULL) 1137 free(ctx->domain_controller.value); 1138 1139 if (ctx->site_name.value != NULL) 1140 free(ctx->site_name.value); 1141 1142 if (ctx->forest_name.value != NULL) 1143 free(ctx->forest_name.value); 1144 1145 if (ctx->global_catalog.value != NULL) 1146 free(ctx->global_catalog.value); 1147 1148 if (ctx->domains_in_forest.value != NULL) 1149 free(ctx->domains_in_forest.value); 1150 1151 if (ctx->trusted_domains.value != NULL) 1152 free(ctx->trusted_domains.value); 1153 1154 /* Site specific versions */ 1155 if (ctx->site_domain_controller.value != NULL) 1156 free(ctx->site_domain_controller.value); 1157 1158 if (ctx->site_global_catalog.value != NULL) 1159 free(ctx->site_global_catalog.value); 1160 1161 free(ctx); 1162 } 1163 1164 void 1165 ad_disc_refresh(ad_disc_t ctx) 1166 { 1167 if (ctx->res_ninitted) 1168 res_ndestroy(&ctx->res_state); 1169 (void) memset(&ctx->res_state, 0, sizeof (ctx->res_state)); 1170 ctx->res_ninitted = res_ninit(&ctx->res_state) != -1; 1171 1172 if (ctx->domain_name.state == AD_STATE_AUTO) 1173 ctx->domain_name.state = AD_STATE_INVALID; 1174 1175 if (ctx->domain_controller.state == AD_STATE_AUTO) 1176 ctx->domain_controller.state = AD_STATE_INVALID; 1177 1178 if (ctx->site_name.state == AD_STATE_AUTO) 1179 ctx->site_name.state = AD_STATE_INVALID; 1180 1181 if (ctx->forest_name.state == AD_STATE_AUTO) 1182 ctx->forest_name.state = AD_STATE_INVALID; 1183 1184 if (ctx->global_catalog.state == AD_STATE_AUTO) 1185 ctx->global_catalog.state = AD_STATE_INVALID; 1186 1187 if (ctx->domains_in_forest.state == AD_STATE_AUTO) 1188 ctx->domains_in_forest.state = AD_STATE_INVALID; 1189 1190 if (ctx->trusted_domains.state == AD_STATE_AUTO) 1191 ctx->trusted_domains.state = AD_STATE_INVALID; 1192 1193 if (ctx->site_domain_controller.state == AD_STATE_AUTO) 1194 ctx->site_domain_controller.state = AD_STATE_INVALID; 1195 1196 if (ctx->site_global_catalog.state == AD_STATE_AUTO) 1197 ctx->site_global_catalog.state = AD_STATE_INVALID; 1198 } 1199 1200 1201 1202 /* Discover joined Active Directory domainName */ 1203 static ad_item_t * 1204 validate_DomainName(ad_disc_t ctx) 1205 { 1206 idmap_ad_disc_ds_t *domain_controller = NULL; 1207 char *dname, *srvname; 1208 uint32_t ttl = 0; 1209 int len; 1210 1211 if (is_valid(&ctx->domain_name)) 1212 return (&ctx->domain_name); 1213 1214 1215 /* Try to find our domain by searching for DCs for it */ 1216 DO_RES_NINIT(ctx); 1217 domain_controller = srv_query(&ctx->res_state, LDAP_SRV_HEAD 1218 DC_SRV_TAIL, ctx->domain_name.value, &srvname, &ttl); 1219 1220 /* 1221 * If we can't find DCs by via res_nsearch() then there's no 1222 * point in trying anything else to discover the AD domain name. 1223 */ 1224 if (domain_controller == NULL) 1225 return (NULL); 1226 1227 free(domain_controller); 1228 /* 1229 * We have the FQDN of the SRV RR name, so now we extract the 1230 * domainname suffix from it. 1231 */ 1232 dname = strdup(srvname + strlen(LDAP_SRV_HEAD DC_SRV_TAIL) + 1233 1 /* for the dot between RR name and domainname */); 1234 1235 free(srvname); 1236 1237 if (dname == NULL) { 1238 logger(LOG_ERR, "Out of memory"); 1239 return (NULL); 1240 } 1241 1242 /* Eat any trailing dot */ 1243 len = strlen(dname); 1244 if (len > 0 && dname[len-1] == '.') 1245 dname[len-1] = '\0'; 1246 1247 update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl); 1248 1249 return (&ctx->domain_name); 1250 } 1251 1252 1253 char * 1254 ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered) 1255 { 1256 char *domain_name = NULL; 1257 ad_item_t *domain_name_item; 1258 1259 domain_name_item = validate_DomainName(ctx); 1260 1261 if (domain_name_item) { 1262 domain_name = strdup(domain_name_item->value); 1263 if (auto_discovered != NULL) 1264 *auto_discovered = 1265 (domain_name_item->state == AD_STATE_AUTO); 1266 } else if (auto_discovered != NULL) 1267 *auto_discovered = B_FALSE; 1268 1269 return (domain_name); 1270 } 1271 1272 1273 /* Discover domain controllers */ 1274 static ad_item_t * 1275 validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) 1276 { 1277 uint32_t ttl = 0; 1278 idmap_ad_disc_ds_t *domain_controller = NULL; 1279 boolean_t validate_global = B_FALSE; 1280 boolean_t validate_site = B_FALSE; 1281 ad_item_t *domain_name_item; 1282 ad_item_t *site_name_item = NULL; 1283 1284 /* If the values is fixed there will not be a site specific version */ 1285 if (is_fixed(&ctx->domain_controller)) 1286 return (&ctx->domain_controller); 1287 1288 domain_name_item = validate_DomainName(ctx); 1289 if (domain_name_item == NULL) 1290 return (NULL); 1291 1292 if (req == AD_DISC_GLOBAL) 1293 validate_global = B_TRUE; 1294 else { 1295 site_name_item = validate_SiteName(ctx); 1296 if (site_name_item != NULL) 1297 validate_site = B_TRUE; 1298 else if (req == AD_DISC_PREFER_SITE) 1299 validate_global = B_TRUE; 1300 } 1301 1302 if (validate_global) { 1303 if (!is_valid(&ctx->domain_controller) || 1304 is_changed(&ctx->domain_controller, PARAM1, 1305 domain_name_item)) { 1306 /* 1307 * Lookup DNS SRV RR named 1308 * _ldap._tcp.dc._msdcs.<DomainName> 1309 */ 1310 DO_RES_NINIT(ctx); 1311 domain_controller = srv_query(&ctx->res_state, 1312 LDAP_SRV_HEAD DC_SRV_TAIL, 1313 domain_name_item->value, NULL, &ttl); 1314 1315 if (domain_controller == NULL) 1316 return (NULL); 1317 1318 update_item(&ctx->domain_controller, domain_controller, 1319 AD_STATE_AUTO, ttl); 1320 update_version(&ctx->domain_controller, PARAM1, 1321 domain_name_item); 1322 } 1323 return (&ctx->domain_controller); 1324 } 1325 1326 if (validate_site) { 1327 if (!is_valid(&ctx->site_domain_controller) || 1328 is_changed(&ctx->site_domain_controller, PARAM1, 1329 domain_name_item) || 1330 is_changed(&ctx->site_domain_controller, PARAM2, 1331 site_name_item)) { 1332 char rr_name[DNS_MAX_NAME]; 1333 /* 1334 * Lookup DNS SRV RR named 1335 * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName> 1336 */ 1337 (void) snprintf(rr_name, sizeof (rr_name), 1338 LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL, 1339 site_name_item->value); 1340 DO_RES_NINIT(ctx); 1341 domain_controller = srv_query(&ctx->res_state, rr_name, 1342 domain_name_item->value, NULL, &ttl); 1343 if (domain_controller == NULL) 1344 return (NULL); 1345 1346 update_item(&ctx->site_domain_controller, 1347 domain_controller, AD_STATE_AUTO, ttl); 1348 update_version(&ctx->site_domain_controller, PARAM1, 1349 domain_name_item); 1350 update_version(&ctx->site_domain_controller, PARAM2, 1351 site_name_item); 1352 } 1353 return (&ctx->site_domain_controller); 1354 } 1355 return (NULL); 1356 } 1357 1358 idmap_ad_disc_ds_t * 1359 ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req, 1360 boolean_t *auto_discovered) 1361 { 1362 ad_item_t *domain_controller_item; 1363 idmap_ad_disc_ds_t *domain_controller = NULL; 1364 1365 domain_controller_item = validate_DomainController(ctx, req); 1366 1367 if (domain_controller_item != NULL) { 1368 domain_controller = ds_dup(domain_controller_item->value); 1369 if (auto_discovered != NULL) 1370 *auto_discovered = 1371 (domain_controller_item->state == AD_STATE_AUTO); 1372 } else if (auto_discovered != NULL) 1373 *auto_discovered = B_FALSE; 1374 1375 return (domain_controller); 1376 } 1377 1378 1379 /* Discover site name (for multi-homed systems the first one found wins) */ 1380 static ad_item_t * 1381 validate_SiteName(ad_disc_t ctx) 1382 { 1383 LDAP *ld = NULL; 1384 ad_subnet_t *subnets = NULL; 1385 char **dn_subnets = NULL; 1386 char *dn_root[2]; 1387 char *config_naming_context = NULL; 1388 char *site_object = NULL; 1389 char *site_name = NULL; 1390 char *forest_name; 1391 int len; 1392 int i; 1393 boolean_t update_required = B_FALSE; 1394 ad_item_t *domain_controller_item; 1395 1396 if (is_fixed(&ctx->site_name)) 1397 return (&ctx->site_name); 1398 1399 /* Can't rely on site-specific DCs */ 1400 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 1401 if (domain_controller_item == NULL) 1402 return (NULL); 1403 1404 if (!is_valid(&ctx->site_name) || 1405 is_changed(&ctx->site_name, PARAM1, &ctx->domain_controller) || 1406 ctx->subnets == NULL || ctx->subnets_changed) { 1407 subnets = find_subnets(); 1408 ctx->subnets_last_check = time(NULL); 1409 update_required = B_TRUE; 1410 } else if (ctx->subnets_last_check + 60 < time(NULL)) { 1411 subnets = find_subnets(); 1412 ctx->subnets_last_check = time(NULL); 1413 if (cmpsubnets(ctx->subnets, subnets) != 0) 1414 update_required = B_TRUE; 1415 } 1416 1417 if (!update_required) { 1418 free(subnets); 1419 return (&ctx->site_name); 1420 } 1421 1422 if (subnets == NULL) 1423 return (NULL); 1424 1425 dn_root[0] = ""; 1426 dn_root[1] = NULL; 1427 1428 config_naming_context = ldap_lookup_entry_attr( 1429 &ld, ctx->domain_controller.value, 1430 dn_root, "configurationNamingContext"); 1431 if (config_naming_context == NULL) 1432 goto out; 1433 /* 1434 * configurationNamingContext also provides the Forest 1435 * Name. 1436 */ 1437 if (!is_fixed(&ctx->forest_name)) { 1438 /* 1439 * The configurationNamingContext should be of 1440 * form: 1441 * CN=Configuration,<DNforestName> 1442 * Remove the first part and convert to DNS form 1443 * (replace ",DC=" with ".") 1444 */ 1445 char *str = "CN=Configuration,"; 1446 int len = strlen(str); 1447 if (strncasecmp(config_naming_context, str, len) == 0) { 1448 forest_name = DN_to_DNS(config_naming_context + len); 1449 update_item(&ctx->forest_name, forest_name, 1450 AD_STATE_AUTO, 0); 1451 } 1452 } 1453 1454 dn_subnets = subnets_to_DNs(subnets, config_naming_context); 1455 if (dn_subnets == NULL) 1456 goto out; 1457 1458 site_object = ldap_lookup_entry_attr( 1459 &ld, domain_controller_item->value, 1460 dn_subnets, "siteobject"); 1461 if (site_object != NULL) { 1462 /* 1463 * The site object should be of the form 1464 * CN=<site>,CN=Sites,CN=Configuration, 1465 * <DN Domain> 1466 */ 1467 if (strncasecmp(site_object, "CN=", 3) == 0) { 1468 for (len = 0; site_object[len + 3] != ','; len++) 1469 ; 1470 site_name = malloc(len + 1); 1471 (void) strncpy(site_name, &site_object[3], len); 1472 site_name[len] = '\0'; 1473 update_item(&ctx->site_name, site_name, 1474 AD_STATE_AUTO, 0); 1475 } 1476 } 1477 1478 if (ctx->subnets != NULL) { 1479 free(ctx->subnets); 1480 ctx->subnets = NULL; 1481 } 1482 ctx->subnets = subnets; 1483 subnets = NULL; 1484 ctx->subnets_changed = B_FALSE; 1485 1486 out: 1487 if (ld != NULL) 1488 (void) ldap_unbind(ld); 1489 1490 if (dn_subnets != NULL) { 1491 for (i = 0; dn_subnets[i] != NULL; i++) 1492 free(dn_subnets[i]); 1493 free(dn_subnets); 1494 } 1495 if (config_naming_context != NULL) 1496 free(config_naming_context); 1497 if (site_object != NULL) 1498 free(site_object); 1499 1500 free(subnets); 1501 if (site_name == NULL) 1502 return (NULL); 1503 return (&ctx->site_name); 1504 1505 } 1506 1507 1508 char * 1509 ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered) 1510 { 1511 ad_item_t *site_name_item; 1512 char *site_name = NULL; 1513 1514 site_name_item = validate_SiteName(ctx); 1515 if (site_name_item != NULL) { 1516 site_name = strdup(site_name_item->value); 1517 if (auto_discovered != NULL) 1518 *auto_discovered = 1519 (site_name_item->state == AD_STATE_AUTO); 1520 } else if (auto_discovered != NULL) 1521 *auto_discovered = B_FALSE; 1522 1523 return (site_name); 1524 } 1525 1526 1527 1528 /* Discover forest name */ 1529 static ad_item_t * 1530 validate_ForestName(ad_disc_t ctx) 1531 { 1532 LDAP *ld = NULL; 1533 char *config_naming_context; 1534 char *forest_name = NULL; 1535 char *dn_list[2]; 1536 ad_item_t *domain_controller_item; 1537 1538 if (is_fixed(&ctx->forest_name)) 1539 return (&ctx->forest_name); 1540 /* 1541 * We may not have a site name yet, so we won't rely on 1542 * site-specific DCs. (But maybe we could replace 1543 * validate_ForestName() with validate_siteName()?) 1544 */ 1545 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 1546 if (domain_controller_item == NULL) 1547 return (NULL); 1548 1549 if (!is_valid(&ctx->forest_name) || 1550 is_changed(&ctx->forest_name, PARAM1, domain_controller_item)) { 1551 1552 dn_list[0] = ""; 1553 dn_list[1] = NULL; 1554 config_naming_context = ldap_lookup_entry_attr( 1555 &ld, ctx->domain_controller.value, 1556 dn_list, "configurationNamingContext"); 1557 if (config_naming_context != NULL) { 1558 /* 1559 * The configurationNamingContext should be of 1560 * form: 1561 * CN=Configuration,<DNforestName> 1562 * Remove the first part and convert to DNS form 1563 * (replace ",DC=" with ".") 1564 */ 1565 char *str = "CN=Configuration,"; 1566 int len = strlen(str); 1567 if (strncasecmp(config_naming_context, str, len) == 0) { 1568 forest_name = DN_to_DNS( 1569 config_naming_context + len); 1570 } 1571 free(config_naming_context); 1572 } 1573 if (ld != NULL) 1574 (void) ldap_unbind(ld); 1575 1576 if (forest_name == NULL) 1577 return (NULL); 1578 1579 update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0); 1580 update_version(&ctx->forest_name, PARAM1, 1581 domain_controller_item); 1582 } 1583 return (&ctx->forest_name); 1584 } 1585 1586 1587 char * 1588 ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered) 1589 { 1590 ad_item_t *forest_name_item; 1591 char *forest_name = NULL; 1592 1593 forest_name_item = validate_ForestName(ctx); 1594 1595 if (forest_name_item != NULL) { 1596 forest_name = strdup(forest_name_item->value); 1597 if (auto_discovered != NULL) 1598 *auto_discovered = 1599 (forest_name_item->state == AD_STATE_AUTO); 1600 } else if (auto_discovered != NULL) 1601 *auto_discovered = B_FALSE; 1602 1603 return (forest_name); 1604 } 1605 1606 1607 /* Discover global catalog servers */ 1608 static ad_item_t * 1609 validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) 1610 { 1611 idmap_ad_disc_ds_t *global_catalog = NULL; 1612 uint32_t ttl = 0; 1613 boolean_t validate_global = B_FALSE; 1614 boolean_t validate_site = B_FALSE; 1615 ad_item_t *forest_name_item; 1616 ad_item_t *site_name_item; 1617 1618 /* If the values is fixed there will not be a site specific version */ 1619 if (is_fixed(&ctx->global_catalog)) 1620 return (&ctx->global_catalog); 1621 1622 forest_name_item = validate_ForestName(ctx); 1623 if (forest_name_item == NULL) 1624 return (NULL); 1625 1626 if (req == AD_DISC_GLOBAL) 1627 validate_global = B_TRUE; 1628 else { 1629 site_name_item = validate_SiteName(ctx); 1630 if (site_name_item != NULL) 1631 validate_site = B_TRUE; 1632 else if (req == AD_DISC_PREFER_SITE) 1633 validate_global = B_TRUE; 1634 } 1635 1636 if (validate_global) { 1637 if (!is_valid(&ctx->global_catalog) || 1638 is_changed(&ctx->global_catalog, PARAM1, 1639 forest_name_item)) { 1640 /* 1641 * Lookup DNS SRV RR named 1642 * _ldap._tcp.gc._msdcs.<ForestName> 1643 */ 1644 DO_RES_NINIT(ctx); 1645 global_catalog = 1646 srv_query(&ctx->res_state, 1647 LDAP_SRV_HEAD GC_SRV_TAIL, 1648 ctx->forest_name.value, NULL, &ttl); 1649 1650 if (global_catalog == NULL) 1651 return (NULL); 1652 1653 update_item(&ctx->global_catalog, global_catalog, 1654 AD_STATE_AUTO, ttl); 1655 update_version(&ctx->global_catalog, PARAM1, 1656 forest_name_item); 1657 } 1658 return (&ctx->global_catalog); 1659 } 1660 1661 if (validate_site) { 1662 if (!is_valid(&ctx->site_global_catalog) || 1663 is_changed(&ctx->site_global_catalog, PARAM1, 1664 forest_name_item) || 1665 is_changed(&ctx->site_global_catalog, PARAM2, 1666 site_name_item)) { 1667 char rr_name[DNS_MAX_NAME]; 1668 1669 /* 1670 * Lookup DNS SRV RR named: 1671 * _ldap._tcp.<siteName>._sites.gc. 1672 * _msdcs.<ForestName> 1673 */ 1674 (void) snprintf(rr_name, 1675 sizeof (rr_name), 1676 LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL, 1677 ctx->site_name.value); 1678 DO_RES_NINIT(ctx); 1679 global_catalog = srv_query(&ctx->res_state, rr_name, 1680 ctx->forest_name.value, NULL, &ttl); 1681 1682 if (global_catalog == NULL) 1683 return (NULL); 1684 update_item(&ctx->site_global_catalog, global_catalog, 1685 AD_STATE_AUTO, ttl); 1686 update_version(&ctx->site_global_catalog, PARAM1, 1687 forest_name_item); 1688 update_version(&ctx->site_global_catalog, PARAM2, 1689 site_name_item); 1690 } 1691 return (&ctx->site_global_catalog); 1692 } 1693 return (NULL); 1694 } 1695 1696 1697 idmap_ad_disc_ds_t * 1698 ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req, 1699 boolean_t *auto_discovered) 1700 { 1701 idmap_ad_disc_ds_t *global_catalog = NULL; 1702 ad_item_t *global_catalog_item; 1703 1704 global_catalog_item = validate_GlobalCatalog(ctx, req); 1705 1706 if (global_catalog_item != NULL) { 1707 global_catalog = ds_dup(global_catalog_item->value); 1708 if (auto_discovered != NULL) 1709 *auto_discovered = 1710 (global_catalog_item->state == AD_STATE_AUTO); 1711 } else if (auto_discovered != NULL) 1712 *auto_discovered = B_FALSE; 1713 1714 return (global_catalog); 1715 } 1716 1717 1718 static ad_item_t * 1719 validate_TrustedDomains(ad_disc_t ctx) 1720 { 1721 LDAP *ld = NULL; 1722 ad_item_t *global_catalog_item; 1723 ad_item_t *forest_name_item; 1724 ad_disc_trusteddomains_t *trusted_domains; 1725 char *dn = NULL; 1726 char *forest_name_dn; 1727 int len; 1728 int num_parts; 1729 1730 if (is_fixed(&ctx->trusted_domains)) 1731 return (&ctx->trusted_domains); 1732 1733 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 1734 if (global_catalog_item == NULL) 1735 return (NULL); 1736 1737 forest_name_item = validate_ForestName(ctx); 1738 if (forest_name_item == NULL) 1739 return (NULL); 1740 1741 if (!is_valid(&ctx->trusted_domains) || 1742 is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) || 1743 is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) { 1744 1745 forest_name_dn = ldap_dns_to_dn(forest_name_item->value, 1746 &num_parts); 1747 if (forest_name_dn == NULL) 1748 return (NULL); 1749 1750 len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1; 1751 dn = malloc(len); 1752 if (dn == NULL) { 1753 free(forest_name_dn); 1754 return (NULL); 1755 } 1756 (void) snprintf(dn, len, "CN=System,%s", forest_name_dn); 1757 free(forest_name_dn); 1758 1759 trusted_domains = ldap_lookup_trusted_domains( 1760 &ld, global_catalog_item->value, dn); 1761 1762 if (ld != NULL) 1763 (void) ldap_unbind(ld); 1764 free(dn); 1765 1766 if (trusted_domains == NULL) 1767 return (NULL); 1768 1769 update_item(&ctx->trusted_domains, trusted_domains, 1770 AD_STATE_AUTO, 0); 1771 update_version(&ctx->trusted_domains, PARAM1, 1772 global_catalog_item); 1773 update_version(&ctx->trusted_domains, PARAM2, 1774 forest_name_item); 1775 } 1776 1777 return (&ctx->trusted_domains); 1778 } 1779 1780 1781 ad_disc_trusteddomains_t * 1782 ad_disc_get_TrustedDomains(ad_disc_t ctx, boolean_t *auto_discovered) 1783 { 1784 ad_disc_trusteddomains_t *trusted_domains = NULL; 1785 ad_item_t *trusted_domains_item; 1786 1787 trusted_domains_item = validate_TrustedDomains(ctx); 1788 1789 if (trusted_domains_item != NULL) { 1790 trusted_domains = td_dup(trusted_domains_item->value); 1791 if (auto_discovered != NULL) 1792 *auto_discovered = 1793 (trusted_domains_item->state == AD_STATE_AUTO); 1794 } else if (auto_discovered != NULL) 1795 *auto_discovered = B_FALSE; 1796 1797 return (trusted_domains); 1798 } 1799 1800 1801 static ad_item_t * 1802 validate_DomainsInForest(ad_disc_t ctx) 1803 { 1804 ad_item_t *global_catalog_item; 1805 LDAP *ld = NULL; 1806 ad_disc_domainsinforest_t *domains_in_forest; 1807 1808 if (is_fixed(&ctx->domains_in_forest)) 1809 return (&ctx->domains_in_forest); 1810 1811 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 1812 if (global_catalog_item == NULL) 1813 return (NULL); 1814 1815 if (!is_valid(&ctx->domains_in_forest) || 1816 is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) { 1817 1818 domains_in_forest = ldap_lookup_domains_in_forest( 1819 &ld, global_catalog_item->value); 1820 1821 if (ld != NULL) 1822 (void) ldap_unbind(ld); 1823 1824 if (domains_in_forest == NULL) 1825 return (NULL); 1826 1827 update_item(&ctx->domains_in_forest, domains_in_forest, 1828 AD_STATE_AUTO, 0); 1829 update_version(&ctx->domains_in_forest, PARAM1, 1830 global_catalog_item); 1831 } 1832 return (&ctx->domains_in_forest); 1833 } 1834 1835 1836 ad_disc_domainsinforest_t * 1837 ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered) 1838 { 1839 ad_disc_domainsinforest_t *domains_in_forest = NULL; 1840 ad_item_t *domains_in_forest_item; 1841 1842 domains_in_forest_item = validate_DomainsInForest(ctx); 1843 1844 if (domains_in_forest_item != NULL) { 1845 domains_in_forest = df_dup(domains_in_forest_item->value); 1846 if (auto_discovered != NULL) 1847 *auto_discovered = 1848 (domains_in_forest_item->state == AD_STATE_AUTO); 1849 } else if (auto_discovered != NULL) 1850 *auto_discovered = B_FALSE; 1851 1852 return (domains_in_forest); 1853 } 1854 1855 1856 1857 1858 int 1859 ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName) 1860 { 1861 char *domain_name = NULL; 1862 if (domainName != NULL) { 1863 domain_name = strdup(domainName); 1864 if (domain_name == NULL) 1865 return (-1); 1866 update_item(&ctx->domain_name, domain_name, 1867 AD_STATE_FIXED, 0); 1868 } else if (ctx->domain_name.state == AD_STATE_FIXED) 1869 ctx->domain_name.state = AD_STATE_INVALID; 1870 return (0); 1871 } 1872 1873 1874 int 1875 ad_disc_set_DomainController(ad_disc_t ctx, 1876 const idmap_ad_disc_ds_t *domainController) 1877 { 1878 idmap_ad_disc_ds_t *domain_controller = NULL; 1879 if (domainController != NULL) { 1880 domain_controller = ds_dup(domainController); 1881 if (domain_controller == NULL) 1882 return (-1); 1883 update_item(&ctx->domain_controller, domain_controller, 1884 AD_STATE_FIXED, 0); 1885 } else if (ctx->domain_controller.state == AD_STATE_FIXED) 1886 ctx->domain_controller.state = AD_STATE_INVALID; 1887 return (0); 1888 } 1889 1890 1891 int 1892 ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName) 1893 { 1894 char *site_name = NULL; 1895 if (siteName != NULL) { 1896 site_name = strdup(siteName); 1897 if (site_name == NULL) 1898 return (-1); 1899 update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0); 1900 } else if (ctx->site_name.state == AD_STATE_FIXED) 1901 ctx->site_name.state = AD_STATE_INVALID; 1902 return (0); 1903 } 1904 1905 int 1906 ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName) 1907 { 1908 char *forest_name = NULL; 1909 if (forestName != NULL) { 1910 forest_name = strdup(forestName); 1911 if (forest_name == NULL) 1912 return (-1); 1913 update_item(&ctx->forest_name, forest_name, 1914 AD_STATE_FIXED, 0); 1915 } else if (ctx->forest_name.state == AD_STATE_FIXED) 1916 ctx->forest_name.state = AD_STATE_INVALID; 1917 return (0); 1918 } 1919 1920 int 1921 ad_disc_set_GlobalCatalog(ad_disc_t ctx, 1922 const idmap_ad_disc_ds_t *globalCatalog) 1923 { 1924 idmap_ad_disc_ds_t *global_catalog = NULL; 1925 if (globalCatalog != NULL) { 1926 global_catalog = ds_dup(globalCatalog); 1927 if (global_catalog == NULL) 1928 return (-1); 1929 update_item(&ctx->global_catalog, global_catalog, 1930 AD_STATE_FIXED, 0); 1931 } else if (ctx->global_catalog.state == AD_STATE_FIXED) 1932 ctx->global_catalog.state = AD_STATE_INVALID; 1933 return (0); 1934 } 1935 1936 1937 int 1938 ad_disc_unset(ad_disc_t ctx) 1939 { 1940 if (ctx->domain_name.state == AD_STATE_FIXED) 1941 ctx->domain_name.state = AD_STATE_INVALID; 1942 1943 if (ctx->domain_controller.state == AD_STATE_FIXED) 1944 ctx->domain_controller.state = AD_STATE_INVALID; 1945 1946 if (ctx->site_name.state == AD_STATE_FIXED) 1947 ctx->site_name.state = AD_STATE_INVALID; 1948 1949 if (ctx->forest_name.state == AD_STATE_FIXED) 1950 ctx->forest_name.state = AD_STATE_INVALID; 1951 1952 if (ctx->global_catalog.state == AD_STATE_FIXED) 1953 ctx->global_catalog.state = AD_STATE_INVALID; 1954 1955 return (0); 1956 } 1957 1958 /* 1959 * ad_disc_get_TTL 1960 * 1961 * This routines the time to live for AD 1962 * auto discovered items. 1963 * 1964 * Returns: 1965 * -1 if there are no TTL items 1966 * 0 if there are expired items 1967 * else the number of seconds 1968 * 1969 * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it 1970 * is positive -- min() greater than zero. 1971 */ 1972 #define MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \ 1973 (-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x)))) 1974 int 1975 ad_disc_get_TTL(ad_disc_t ctx) 1976 { 1977 int ttl; 1978 1979 ttl = MIN_GT_ZERO(ctx->domain_controller.ttl, ctx->global_catalog.ttl); 1980 ttl = MIN_GT_ZERO(ttl, ctx->site_domain_controller.ttl); 1981 ttl = MIN_GT_ZERO(ttl, ctx->site_global_catalog.ttl); 1982 1983 if (ttl == -1) 1984 return (-1); 1985 ttl -= time(NULL); 1986 if (ttl < 0) 1987 return (0); 1988 return (ttl); 1989 } 1990 1991 boolean_t 1992 ad_disc_SubnetChanged(ad_disc_t ctx) 1993 { 1994 ad_subnet_t *subnets; 1995 1996 if (ctx->subnets_changed || ctx->subnets == NULL) 1997 return (B_TRUE); 1998 1999 if ((subnets = find_subnets()) != NULL) { 2000 if (cmpsubnets(subnets, ctx->subnets) != 0) 2001 ctx->subnets_changed = B_TRUE; 2002 free(subnets); 2003 } 2004 2005 return (ctx->subnets_changed); 2006 } 2007