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