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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Active Directory Auto-Discovery. 28 * 29 * This [project private] API allows the caller to provide whatever 30 * details it knows a priori (i.e., provided via configuration so as to 31 * override auto-discovery) and in any order. Then the caller can ask 32 * for any of the auto-discoverable parameters in any order. 33 * 34 * But there is an actual order in which discovery must be done. Given 35 * the discovery mechanism implemented here, that order is: 36 * 37 * - the domain name joined must be discovered first 38 * - then the domain controllers 39 * - then the forest name and site name 40 * - then the global catalog servers, and site-specific domain 41 * controllers and global catalog servers. 42 * 43 * The API does not require it be called in the same order because there 44 * may be other discovery mechanisms in the future, and exposing 45 * ordering requirements of the current mechanism now can create trouble 46 * down the line. Also, this makes the API easier to use now, which 47 * means less work to do some day when we make this a public API. 48 * 49 * Domain discovery is done by res_nsearch() of the DNS SRV RR name for 50 * domain controllers. As long as the joined domain appears in the DNS 51 * resolver's search list then we'll find it. 52 * 53 * Domain controller discovery is a matter of formatting the DNS SRV RR 54 * FQDN for domain controllers and doing a lookup for them. Knowledge 55 * of the domain name is not fundamentally required, but we separate the 56 * two processes, which in practice can lead to one more DNS lookup than 57 * is strictly required. 58 * 59 * Forest and site name discovery require an LDAP search of the AD 60 * "configuration partition" at a domain controller for the joined 61 * domain. Forest and site name discovery depend on knowing the joined 62 * domain name and domain controllers for that domain. 63 * 64 * Global catalog server discovery requires knowledge of the forest 65 * name in order to format the DNS SRV RR FQDN to lookup. Site-specific 66 * domain controller discovery depends on knowing the site name (and, 67 * therefore, joined domain, ...). Site-specific global catalog server 68 * discovery depends on knowledge of the forest and site names, which 69 * depend on... 70 * 71 * All the work of discovering particular items is done by functions 72 * named validate_<item>(). Each such function calls validate_<item>() 73 * for any items that it depends on. 74 * 75 * This API is not thread-safe. 76 */ 77 78 79 #include <stdio.h> 80 #include <string.h> 81 #include <strings.h> 82 #include <unistd.h> 83 #include <assert.h> 84 #include <stdlib.h> 85 #include <net/if.h> 86 #include <net/if.h> 87 #include <sys/types.h> 88 #include <sys/socket.h> 89 #include <sys/sockio.h> 90 #include <netinet/in.h> 91 #include <netinet/in.h> 92 #include <arpa/inet.h> 93 #include <arpa/nameser.h> 94 #include <resolv.h> 95 #include <netdb.h> 96 #include <ctype.h> 97 #include <errno.h> 98 #include <ldap.h> 99 #include <sasl/sasl.h> 100 #include <sys/u8_textprep.h> 101 #include <syslog.h> 102 #include "adutils_impl.h" 103 #include "addisc.h" 104 105 /* 106 * These set some sanity policies for discovery. After a discovery 107 * cycle, we will consider the results (successful or unsuccessful) 108 * to be valid for at least MINIMUM_TTL seconds, and for at most 109 * MAXIMUM_TTL seconds. Note that the caller is free to request 110 * discovery cycles sooner than MINIMUM_TTL if it has reason to believe 111 * that the situation has changed. 112 */ 113 #define MINIMUM_TTL (5 * 60) 114 #define MAXIMUM_TTL (20 * 60) 115 116 enum ad_item_state { 117 AD_STATE_INVALID = 0, /* The value is not valid */ 118 AD_STATE_FIXED, /* The value was fixed by caller */ 119 AD_STATE_AUTO /* The value is auto discovered */ 120 }; 121 122 enum ad_data_type { 123 AD_STRING = 123, 124 AD_DIRECTORY, 125 AD_DOMAINS_IN_FOREST, 126 AD_TRUSTED_DOMAINS 127 }; 128 129 130 typedef struct ad_subnet { 131 char subnet[24]; 132 } ad_subnet_t; 133 134 135 typedef struct ad_item { 136 enum ad_item_state state; 137 enum ad_data_type type; 138 void *value; 139 time_t expires; 140 unsigned int version; /* Version is only changed */ 141 /* if the value changes */ 142 #define PARAM1 0 143 #define PARAM2 1 144 int param_version[2]; 145 /* These holds the version of */ 146 /* dependents so that a dependent */ 147 /* change can be detected */ 148 } ad_item_t; 149 150 typedef struct ad_disc { 151 struct __res_state res_state; 152 int res_ninitted; 153 ad_subnet_t *subnets; 154 boolean_t subnets_changed; 155 time_t subnets_last_check; 156 time_t expires_not_before; 157 time_t expires_not_after; 158 ad_item_t domain_name; /* DNS hostname string */ 159 ad_item_t domain_controller; /* Directory hostname and */ 160 /* port array */ 161 ad_item_t site_name; /* String */ 162 ad_item_t forest_name; /* DNS forestname string */ 163 ad_item_t global_catalog; /* Directory hostname and */ 164 /* port array */ 165 ad_item_t domains_in_forest; /* DNS domainname and SID */ 166 /* array */ 167 ad_item_t trusted_domains; /* DNS domainname and trust */ 168 /* direction array */ 169 /* Site specfic versions */ 170 ad_item_t site_domain_controller; /* Directory hostname and */ 171 /* port array */ 172 ad_item_t site_global_catalog; /* Directory hostname and */ 173 /* port array */ 174 } ad_disc; 175 176 177 #define DNS_MAX_NAME NS_MAXDNAME 178 179 180 /* SRV RR names for various queries */ 181 #define LDAP_SRV_HEAD "_ldap._tcp." 182 #define SITE_SRV_MIDDLE "%s._sites." 183 #define GC_SRV_TAIL "gc._msdcs" 184 #define DC_SRV_TAIL "dc._msdcs" 185 #define ALL_GC_SRV_TAIL "_gc._tcp" 186 #define PDC_SRV "_ldap._tcp.pdc._msdcs.%s" 187 188 /* A RR name for all GCs -- last resort this works */ 189 #define GC_ALL_A_NAME_FSTR "gc._msdcs.%s." 190 191 192 /* 193 * We try res_ninit() whenever we don't have one. res_ninit() fails if 194 * idmapd is running before the network is up! 195 */ 196 #define DO_RES_NINIT(ctx) if (!(ctx)->res_ninitted) \ 197 (ctx)->res_ninitted = (res_ninit(&ctx->res_state) != -1) 198 199 #define is_fixed(item) \ 200 ((item)->state == AD_STATE_FIXED) 201 202 #define is_changed(item, num, param) \ 203 ((item)->param_version[num] != (param)->version) 204 205 /*LINTLIBRARY*/ 206 207 /* 208 * Function definitions 209 */ 210 static ad_item_t * 211 validate_SiteName(ad_disc_t ctx); 212 213 214 215 static void 216 update_version(ad_item_t *item, int num, ad_item_t *param) 217 { 218 item->param_version[num] = param->version; 219 } 220 221 222 223 static boolean_t 224 is_valid(ad_item_t *item) 225 { 226 if (item->value != NULL) { 227 if (item->state == AD_STATE_FIXED) 228 return (B_TRUE); 229 if (item->state == AD_STATE_AUTO && 230 (item->expires == 0 || item->expires > time(NULL))) 231 return (B_TRUE); 232 } 233 return (B_FALSE); 234 } 235 236 237 static void 238 update_item(ad_item_t *item, void *value, enum ad_item_state state, 239 uint32_t ttl) 240 { 241 if (item->value != NULL && value != NULL) { 242 if ((item->type == AD_STRING && 243 strcmp(item->value, value) != 0) || 244 (item->type == AD_DIRECTORY && 245 ad_disc_compare_ds(item->value, value) != 0)|| 246 (item->type == AD_DOMAINS_IN_FOREST && 247 ad_disc_compare_domainsinforest(item->value, value) != 0) || 248 (item->type == AD_TRUSTED_DOMAINS && 249 ad_disc_compare_trusteddomains(item->value, value) != 0)) 250 item->version++; 251 } else if (item->value != value) 252 item->version++; 253 254 if (item->value != NULL) 255 free(item->value); 256 257 item->value = value; 258 item->state = state; 259 260 if (ttl == 0) 261 item->expires = 0; 262 else 263 item->expires = time(NULL) + ttl; 264 } 265 266 267 /* Compare DS lists */ 268 int 269 ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2) 270 { 271 int i, j; 272 int num_ds1; 273 int num_ds2; 274 boolean_t match; 275 276 for (i = 0; ds1[i].host[0] != '\0'; i++) 277 continue; 278 num_ds1 = i; 279 for (j = 0; ds2[j].host[0] != '\0'; j++) 280 continue; 281 num_ds2 = j; 282 if (num_ds1 != num_ds2) 283 return (1); 284 285 for (i = 0; i < num_ds1; i++) { 286 match = B_FALSE; 287 for (j = 0; j < num_ds2; j++) { 288 if (strcmp(ds1[i].host, ds2[j].host) == 0 && 289 ds1[i].port == ds2[j].port) { 290 match = B_TRUE; 291 break; 292 } 293 } 294 if (!match) 295 return (1); 296 } 297 return (0); 298 } 299 300 301 /* Copy a list of DSs */ 302 static idmap_ad_disc_ds_t * 303 ds_dup(const idmap_ad_disc_ds_t *srv) 304 { 305 int i; 306 int size; 307 idmap_ad_disc_ds_t *new = NULL; 308 309 for (i = 0; srv[i].host[0] != '\0'; i++) 310 continue; 311 312 size = (i + 1) * sizeof (idmap_ad_disc_ds_t); 313 new = malloc(size); 314 if (new != NULL) 315 memcpy(new, srv, size); 316 return (new); 317 } 318 319 320 int 321 ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1, 322 ad_disc_trusteddomains_t *td2) 323 { 324 int i, j; 325 int num_td1; 326 int num_td2; 327 boolean_t match; 328 329 for (i = 0; td1[i].domain[0] != '\0'; i++) 330 continue; 331 num_td1 = i; 332 333 for (j = 0; td2[j].domain[0] != '\0'; j++) 334 continue; 335 num_td2 = j; 336 337 if (num_td1 != num_td2) 338 return (1); 339 340 for (i = 0; i < num_td1; i++) { 341 match = B_FALSE; 342 for (j = 0; j < num_td2; j++) { 343 if (domain_eq(td1[i].domain, td2[j].domain)) { 344 match = B_TRUE; 345 break; 346 } 347 } 348 if (!match) 349 return (1); 350 } 351 return (0); 352 } 353 354 355 356 /* Copy a list of Trusted Domains */ 357 static ad_disc_trusteddomains_t * 358 td_dup(const ad_disc_trusteddomains_t *td) 359 { 360 int i; 361 int size; 362 ad_disc_trusteddomains_t *new = NULL; 363 364 for (i = 0; td[i].domain[0] != '\0'; i++) 365 continue; 366 367 size = (i + 1) * sizeof (ad_disc_trusteddomains_t); 368 new = malloc(size); 369 if (new != NULL) 370 memcpy(new, td, size); 371 return (new); 372 } 373 374 375 376 int 377 ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *df1, 378 ad_disc_domainsinforest_t *df2) 379 { 380 int i, j; 381 int num_df1; 382 int num_df2; 383 boolean_t match; 384 385 for (i = 0; df1[i].domain[0] != '\0'; i++) 386 continue; 387 num_df1 = i; 388 389 for (j = 0; df2[j].domain[0] != '\0'; j++) 390 continue; 391 num_df2 = j; 392 393 if (num_df1 != num_df2) 394 return (1); 395 396 for (i = 0; i < num_df1; i++) { 397 match = B_FALSE; 398 for (j = 0; j < num_df2; j++) { 399 if (domain_eq(df1[i].domain, df2[j].domain) && 400 strcmp(df1[i].sid, df2[j].sid) == 0) { 401 match = B_TRUE; 402 break; 403 } 404 } 405 if (!match) 406 return (1); 407 } 408 return (0); 409 } 410 411 412 413 /* Copy a list of Trusted Domains */ 414 static ad_disc_domainsinforest_t * 415 df_dup(const ad_disc_domainsinforest_t *df) 416 { 417 int i; 418 int size; 419 ad_disc_domainsinforest_t *new = NULL; 420 421 for (i = 0; df[i].domain[0] != '\0'; i++) 422 continue; 423 424 size = (i + 1) * sizeof (ad_disc_domainsinforest_t); 425 new = malloc(size); 426 if (new != NULL) 427 memcpy(new, df, size); 428 return (new); 429 } 430 431 432 433 434 435 /* 436 * Returns an array of IPv4 address/prefix length 437 * The last subnet is NULL 438 */ 439 static ad_subnet_t * 440 find_subnets() 441 { 442 int sock, n, i; 443 struct lifconf lifc; 444 struct lifreq lifr, *lifrp; 445 struct lifnum lifn; 446 uint32_t prefix_len; 447 char *s; 448 ad_subnet_t *results; 449 450 lifrp = &lifr; 451 452 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 453 logger(LOG_ERR, "Failed to open IPv4 socket for " 454 "listing network interfaces (%s)", strerror(errno)); 455 return (NULL); 456 } 457 458 lifn.lifn_family = AF_INET; 459 lifn.lifn_flags = 0; 460 if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) { 461 logger(LOG_ERR, 462 "Failed to find the number of network interfaces (%s)", 463 strerror(errno)); 464 close(sock); 465 return (NULL); 466 } 467 468 if (lifn.lifn_count < 1) { 469 logger(LOG_ERR, "No IPv4 network interfaces found"); 470 close(sock); 471 return (NULL); 472 } 473 474 lifc.lifc_family = AF_INET; 475 lifc.lifc_flags = 0; 476 lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq); 477 lifc.lifc_buf = malloc(lifc.lifc_len); 478 479 if (lifc.lifc_buf == NULL) { 480 logger(LOG_ERR, "Out of memory"); 481 close(sock); 482 return (NULL); 483 } 484 485 if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) { 486 logger(LOG_ERR, "Failed to list network interfaces (%s)", 487 strerror(errno)); 488 free(lifc.lifc_buf); 489 close(sock); 490 return (NULL); 491 } 492 493 n = lifc.lifc_len / (int)sizeof (struct lifreq); 494 495 if ((results = calloc(n + 1, sizeof (ad_subnet_t))) == NULL) { 496 free(lifc.lifc_buf); 497 close(sock); 498 return (NULL); 499 } 500 501 for (i = 0, lifrp = lifc.lifc_req; i < n; i++, lifrp++) { 502 if (ioctl(sock, SIOCGLIFFLAGS, lifrp) < 0) 503 continue; 504 505 if ((lifrp->lifr_flags & IFF_UP) == 0) 506 continue; 507 508 if (ioctl(sock, SIOCGLIFSUBNET, lifrp) < 0) 509 continue; 510 511 prefix_len = lifrp->lifr_addrlen; 512 513 s = inet_ntoa(((struct sockaddr_in *) 514 &lifrp->lifr_addr)->sin_addr); 515 516 (void) snprintf(results[i].subnet, sizeof (ad_subnet_t), 517 "%s/%d", s, prefix_len); 518 } 519 520 free(lifc.lifc_buf); 521 close(sock); 522 523 return (results); 524 } 525 526 static int 527 cmpsubnets(ad_subnet_t *subnets1, ad_subnet_t *subnets2) 528 { 529 int num_subnets1; 530 int num_subnets2; 531 boolean_t matched; 532 int i, j; 533 534 for (i = 0; subnets1[i].subnet[0] != '\0'; i++) 535 continue; 536 num_subnets1 = i; 537 538 for (i = 0; subnets2[i].subnet[0] != '\0'; i++) 539 continue; 540 num_subnets2 = i; 541 542 if (num_subnets1 != num_subnets2) 543 return (1); 544 545 for (i = 0; i < num_subnets1; i++) { 546 matched = B_FALSE; 547 for (j = 0; j < num_subnets2; j++) { 548 if (strcmp(subnets1[i].subnet, 549 subnets2[j].subnet) == 0) { 550 matched = B_TRUE; 551 break; 552 } 553 } 554 if (!matched) 555 return (1); 556 } 557 return (0); 558 } 559 560 561 562 563 /* Convert a DN's DC components into a DNS domainname */ 564 char * 565 DN_to_DNS(const char *dn_name) 566 { 567 char dns[DNS_MAX_NAME]; 568 char *dns_name; 569 int i, j; 570 int num = 0; 571 572 j = 0; 573 i = 0; 574 575 if (dn_name == NULL) 576 return (NULL); 577 /* 578 * Find all DC=<value> and form DNS name of the 579 * form <value1>.<value2>... 580 */ 581 while (dn_name[i] != '\0') { 582 if (strncasecmp(&dn_name[i], "DC=", 3) == 0) { 583 i += 3; 584 if (dn_name[i] != '\0' && num > 0) 585 dns[j++] = '.'; 586 while (dn_name[i] != '\0' && 587 dn_name[i] != ',' && dn_name[i] != '+') 588 dns[j++] = dn_name[i++]; 589 num++; 590 } else { 591 /* Skip attr=value as it is not DC= */ 592 while (dn_name[i] != '\0' && 593 dn_name[i] != ',' && dn_name[i] != '+') 594 i++; 595 } 596 /* Skip over separator ',' or '+' */ 597 if (dn_name[i] != '\0') i++; 598 } 599 dns[j] = '\0'; 600 dns_name = malloc(j + 1); 601 if (dns_name != NULL) 602 (void) strlcpy(dns_name, dns, j + 1); 603 return (dns_name); 604 } 605 606 607 /* Make a list of subnet object DNs from a list of subnets */ 608 static char ** 609 subnets_to_DNs(ad_subnet_t *subnets, const char *base_dn) 610 { 611 char **results; 612 int i, j; 613 614 for (i = 0; subnets[i].subnet[0] != '\0'; i++) 615 continue; 616 617 results = calloc(i + 1, sizeof (char *)); 618 if (results == NULL) 619 return (NULL); 620 621 for (i = 0; subnets[i].subnet[0] != '\0'; i++) { 622 (void) asprintf(&results[i], "CN=%s,CN=Subnets,CN=Sites,%s", 623 subnets[i].subnet, base_dn); 624 if (results[i] == NULL) { 625 for (j = 0; j < i; j++) 626 free(results[j]); 627 free(results); 628 return (NULL); 629 } 630 } 631 632 return (results); 633 } 634 635 636 /* Compare SRC RRs; used with qsort() */ 637 static int 638 srvcmp(idmap_ad_disc_ds_t *s1, idmap_ad_disc_ds_t *s2) 639 { 640 if (s1->priority < s2->priority) 641 return (1); 642 else if (s1->priority > s2->priority) 643 return (-1); 644 645 if (s1->weight < s2->weight) 646 return (1); 647 else if (s1->weight > s2->weight) 648 return (-1); 649 650 return (0); 651 } 652 653 654 /* 655 * Query or search the SRV RRs for a given name. 656 * 657 * If name == NULL then search (as in res_nsearch(3RESOLV), honoring any 658 * search list/option), else query (as in res_nquery(3RESOLV)). 659 * 660 * The output TTL will be the one of the SRV RR with the lowest TTL. 661 */ 662 idmap_ad_disc_ds_t * 663 srv_query(res_state state, const char *svc_name, const char *dname, 664 char **rrname, uint32_t *ttl) 665 { 666 idmap_ad_disc_ds_t *srv; 667 idmap_ad_disc_ds_t *srv_res = NULL; 668 union { 669 HEADER hdr; 670 uchar_t buf[NS_MAXMSG]; 671 } msg; 672 int len, cnt, qdcount, ancount; 673 uchar_t *ptr, *eom; 674 uchar_t *end; 675 uint16_t type; 676 /* LINTED E_FUNC_SET_NOT_USED */ 677 uint16_t class; 678 uint32_t rttl; 679 uint16_t size; 680 char namebuf[NS_MAXDNAME]; 681 682 if (state == NULL) 683 return (NULL); 684 685 /* Set negative result TTL */ 686 *ttl = 5 * 60; 687 688 /* 1. query necessary resource records */ 689 690 /* Search, querydomain or query */ 691 if (rrname != NULL) { 692 *rrname = NULL; 693 len = res_nsearch(state, svc_name, C_IN, T_SRV, 694 msg.buf, sizeof (msg.buf)); 695 logger(LOG_DEBUG, "Searching DNS for SRV RRs named '%s'", 696 svc_name); 697 if (len < 0) { 698 logger(LOG_DEBUG, "DNS search for '%s' failed (%s)", 699 svc_name, hstrerror(state->res_h_errno)); 700 return (NULL); 701 } 702 } else if (dname != NULL) { 703 logger(LOG_DEBUG, 704 "Querying DNS for SRV RRs named '%s' for '%s' ", 705 svc_name, dname); 706 707 len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV, 708 msg.buf, sizeof (msg.buf)); 709 710 if (len < 0) { 711 logger(LOG_DEBUG, 712 "DNS query for '%s' for '%s' failed (%s)", 713 svc_name, dname, hstrerror(state->res_h_errno)); 714 return (NULL); 715 } 716 } 717 718 if (len > sizeof (msg.buf)) { 719 logger(LOG_ERR, "DNS query %ib message doesn't fit" 720 " into %ib buffer", 721 len, sizeof (msg.buf)); 722 return (NULL); 723 } 724 725 /* 2. parse the reply, skip header and question sections */ 726 727 ptr = msg.buf + sizeof (msg.hdr); 728 eom = msg.buf + len; 729 qdcount = ntohs(msg.hdr.qdcount); 730 ancount = ntohs(msg.hdr.ancount); 731 732 for (cnt = qdcount; cnt > 0; --cnt) { 733 if ((len = dn_skipname(ptr, eom)) < 0) { 734 logger(LOG_ERR, "DNS query invalid message format"); 735 return (NULL); 736 } 737 ptr += len + QFIXEDSZ; 738 } 739 740 /* 3. walk through the answer section */ 741 742 srv_res = calloc(ancount + 1, sizeof (idmap_ad_disc_ds_t)); 743 if (srv_res == NULL) { 744 logger(LOG_ERR, "Out of memory"); 745 return (NULL); 746 } 747 748 *ttl = (uint32_t)-1; 749 750 for (srv = srv_res, cnt = ancount; 751 cnt > 0; --cnt, srv++) { 752 753 len = dn_expand(msg.buf, eom, ptr, namebuf, 754 sizeof (namebuf)); 755 if (len < 0) { 756 logger(LOG_ERR, "DNS query invalid message format"); 757 goto err; 758 } 759 if (rrname != NULL && *rrname == NULL) { 760 *rrname = strdup(namebuf); 761 if (*rrname == NULL) { 762 logger(LOG_ERR, "Out of memory"); 763 goto err; 764 } 765 } 766 ptr += len; 767 NS_GET16(type, ptr); 768 NS_GET16(class, ptr); 769 NS_GET32(rttl, ptr); 770 NS_GET16(size, ptr); 771 if ((end = ptr + size) > eom) { 772 logger(LOG_ERR, "DNS query invalid message format"); 773 goto err; 774 } 775 776 if (type != T_SRV) { 777 ptr = end; 778 continue; 779 } 780 781 NS_GET16(srv->priority, ptr); 782 NS_GET16(srv->weight, ptr); 783 NS_GET16(srv->port, ptr); 784 len = dn_expand(msg.buf, eom, ptr, srv->host, 785 sizeof (srv->host)); 786 if (len < 0) { 787 logger(LOG_ERR, "DNS query invalid SRV record"); 788 goto err; 789 } 790 791 if (rttl < *ttl) 792 *ttl = rttl; 793 794 logger(LOG_DEBUG, "Found %s %d IN SRV [%d][%d] %s:%d", 795 namebuf, rttl, srv->priority, srv->weight, srv->host, 796 srv->port); 797 798 /* 3. move ptr to the end of current record */ 799 800 ptr = end; 801 } 802 803 if (ancount > 1) 804 qsort(srv_res, ancount, sizeof (*srv_res), 805 (int (*)(const void *, const void *))srvcmp); 806 807 return (srv_res); 808 809 err: 810 free(srv_res); 811 if (rrname != NULL) { 812 free(*rrname); 813 *rrname = NULL; 814 } 815 return (NULL); 816 } 817 818 819 /* 820 * A utility function to bind to a Directory server 821 */ 822 823 static 824 LDAP * 825 ldap_lookup_init(idmap_ad_disc_ds_t *ds) 826 { 827 int i; 828 int rc, ldversion; 829 int zero = 0; 830 int timeoutms = 5 * 1000; 831 char *saslmech = "GSSAPI"; 832 uint32_t saslflags = LDAP_SASL_INTERACTIVE; 833 LDAP *ld = NULL; 834 835 for (i = 0; ds[i].host[0] != '\0'; i++) { 836 ld = ldap_init(ds[i].host, ds[i].port); 837 if (ld == NULL) { 838 logger(LOG_DEBUG, "Couldn't connect to " 839 "AD DC %s:%d (%s)", 840 ds[i].host, ds[i].port, 841 strerror(errno)); 842 continue; 843 } 844 845 ldversion = LDAP_VERSION3; 846 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, 847 &ldversion); 848 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, 849 LDAP_OPT_OFF); 850 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero); 851 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero); 852 /* setup TCP/IP connect timeout */ 853 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, 854 &timeoutms); 855 (void) ldap_set_option(ld, LDAP_OPT_RESTART, 856 LDAP_OPT_ON); 857 858 rc = adutils_set_thread_functions(ld); 859 if (rc != LDAP_SUCCESS) { 860 /* Error has already been logged */ 861 (void) ldap_unbind(ld); 862 ld = NULL; 863 continue; 864 } 865 866 rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */, 867 saslmech, NULL, NULL, saslflags, &saslcallback, 868 NULL /* defaults */); 869 if (rc == LDAP_SUCCESS) 870 break; 871 872 logger(LOG_INFO, "LDAP SASL bind to %s:%d failed (%s)", 873 ds[i].host, ds[i].port, ldap_err2string(rc)); 874 (void) ldap_unbind(ld); 875 ld = NULL; 876 } 877 return (ld); 878 } 879 880 881 882 /* 883 * A utility function to get the value of some attribute of one of one 884 * or more AD LDAP objects named by the dn_list; first found one wins. 885 */ 886 static char * 887 ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers, 888 char **dn_list, char *attr) 889 { 890 int i; 891 int rc; 892 int scope = LDAP_SCOPE_BASE; 893 char *attrs[2]; 894 LDAPMessage *results = NULL; 895 LDAPMessage *entry; 896 char **values = NULL; 897 char *val = NULL; 898 899 attrs[0] = attr; 900 attrs[1] = NULL; 901 902 if (*ld == NULL) 903 *ld = ldap_lookup_init(domainControllers); 904 905 if (*ld == NULL) 906 return (NULL); 907 908 for (i = 0; dn_list[i] != NULL; i++) { 909 rc = ldap_search_s(*ld, dn_list[i], scope, 910 "(objectclass=*)", attrs, 0, &results); 911 if (rc == LDAP_SUCCESS) { 912 for (entry = ldap_first_entry(*ld, results); 913 entry != NULL && values == NULL; 914 entry = ldap_next_entry(*ld, entry)) { 915 values = ldap_get_values( 916 *ld, entry, attr); 917 } 918 919 if (values != NULL) { 920 (void) ldap_msgfree(results); 921 val = strdup(values[0]); 922 ldap_value_free(values); 923 return (val); 924 } 925 } 926 if (results != NULL) { 927 (void) ldap_msgfree(results); 928 results = NULL; 929 } 930 } 931 932 return (NULL); 933 } 934 935 936 /* 937 * Lookup the trusted domains in the global catalog. 938 * 939 * Returns: 940 * array of trusted domains which is terminated by 941 * an empty trusted domain. 942 * NULL an error occured 943 */ 944 ad_disc_trusteddomains_t * 945 ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog, 946 char *base_dn) 947 { 948 int scope = LDAP_SCOPE_SUBTREE; 949 char *attrs[3]; 950 int rc; 951 LDAPMessage *results = NULL; 952 LDAPMessage *entry; 953 char *filter; 954 char **partner = NULL; 955 char **direction = NULL; 956 int num = 0; 957 ad_disc_trusteddomains_t *trusted_domains = NULL; 958 959 960 if (*ld == NULL) 961 *ld = ldap_lookup_init(globalCatalog); 962 963 if (*ld == NULL) 964 return (NULL); 965 966 attrs[0] = "trustPartner"; 967 attrs[1] = "trustDirection"; 968 attrs[2] = NULL; 969 970 /* trustDirection values - inbound = 1 and bidirectional = 3 */ 971 filter = "(&(objectclass=trustedDomain)" 972 "(|(trustDirection=3)(trustDirection=1)))"; 973 974 rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results); 975 if (rc == LDAP_SUCCESS) { 976 for (entry = ldap_first_entry(*ld, results); 977 entry != NULL; entry = ldap_next_entry(*ld, entry)) { 978 partner = ldap_get_values(*ld, entry, "trustPartner"); 979 direction = ldap_get_values( 980 *ld, entry, "trustDirection"); 981 982 if (partner != NULL && direction != NULL) { 983 num++; 984 void *tmp = realloc(trusted_domains, 985 (num + 1) * 986 sizeof (ad_disc_trusteddomains_t)); 987 if (tmp == NULL) { 988 free(trusted_domains); 989 ldap_value_free(partner); 990 ldap_value_free(direction); 991 ldap_msgfree(results); 992 return (NULL); 993 } 994 trusted_domains = tmp; 995 /* Last element should be zero */ 996 memset(&trusted_domains[num], 0, 997 sizeof (ad_disc_trusteddomains_t)); 998 strcpy(trusted_domains[num - 1].domain, 999 partner[0]); 1000 trusted_domains[num - 1].direction = 1001 atoi(direction[0]); 1002 } 1003 if (partner != NULL) 1004 ldap_value_free(partner); 1005 if (direction != NULL) 1006 ldap_value_free(direction); 1007 } 1008 } else if (rc == LDAP_NO_RESULTS_RETURNED) { 1009 /* This is not an error - return empty trusted domain */ 1010 trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t)); 1011 } 1012 if (results != NULL) 1013 ldap_msgfree(results); 1014 1015 return (trusted_domains); 1016 } 1017 1018 1019 /* 1020 * This functions finds all the domains in a forest. 1021 */ 1022 ad_disc_domainsinforest_t * 1023 ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs) 1024 { 1025 static char *attrs[] = { 1026 "objectSid", 1027 NULL, 1028 }; 1029 int rc; 1030 LDAPMessage *result = NULL; 1031 LDAPMessage *entry; 1032 int ndomains = 0; 1033 int nresults; 1034 ad_disc_domainsinforest_t *domains = NULL; 1035 1036 if (*ld == NULL) 1037 *ld = ldap_lookup_init(globalCatalogs); 1038 1039 if (*ld == NULL) 1040 return (NULL); 1041 1042 logger(LOG_DEBUG, "Looking for domains in forest..."); 1043 /* Find domains */ 1044 rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE, 1045 "(objectClass=Domain)", attrs, 0, &result); 1046 if (rc != LDAP_SUCCESS) 1047 goto err; 1048 1049 nresults = ldap_count_entries(*ld, result); 1050 domains = calloc(nresults + 1, sizeof (*domains)); 1051 if (domains == NULL) 1052 goto err; 1053 1054 for (entry = ldap_first_entry(*ld, result); 1055 entry != NULL; 1056 entry = ldap_next_entry(*ld, entry)) { 1057 struct berval **sid_ber; 1058 adutils_sid_t sid; 1059 char *sid_str; 1060 char *name; 1061 char *dn; 1062 1063 sid_ber = ldap_get_values_len(*ld, entry, 1064 "objectSid"); 1065 if (sid_ber == NULL) 1066 continue; 1067 1068 rc = adutils_getsid(sid_ber[0], &sid); 1069 ldap_value_free_len(sid_ber); 1070 if (rc < 0) 1071 goto err; 1072 1073 if ((sid_str = adutils_sid2txt(&sid)) == NULL) 1074 goto err; 1075 1076 strcpy(domains[ndomains].sid, sid_str); 1077 free(sid_str); 1078 1079 dn = ldap_get_dn(*ld, entry); 1080 name = DN_to_DNS(dn); 1081 free(dn); 1082 if (name == NULL) 1083 goto err; 1084 1085 strcpy(domains[ndomains].domain, name); 1086 free(name); 1087 1088 logger(LOG_DEBUG, " found %s", domains[ndomains].domain); 1089 1090 ndomains++; 1091 } 1092 1093 if (ndomains == 0) 1094 goto err; 1095 1096 if (ndomains < nresults) { 1097 ad_disc_domainsinforest_t *tmp; 1098 tmp = realloc(domains, (ndomains + 1) * sizeof (*domains)); 1099 if (tmp == NULL) 1100 goto err; 1101 domains = tmp; 1102 } 1103 1104 if (result != NULL) 1105 ldap_msgfree(result); 1106 1107 return (domains); 1108 1109 err: 1110 free(domains); 1111 if (result != NULL) 1112 ldap_msgfree(result); 1113 return (NULL); 1114 } 1115 1116 1117 ad_disc_t 1118 ad_disc_init(void) 1119 { 1120 struct ad_disc *ctx; 1121 ctx = calloc(1, sizeof (struct ad_disc)); 1122 if (ctx != NULL) 1123 DO_RES_NINIT(ctx); 1124 1125 ctx->domain_name.type = AD_STRING; 1126 ctx->domain_controller.type = AD_DIRECTORY; 1127 ctx->site_name.type = AD_STRING; 1128 ctx->forest_name.type = AD_STRING; 1129 ctx->global_catalog.type = AD_DIRECTORY; 1130 ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST; 1131 ctx->trusted_domains.type = AD_TRUSTED_DOMAINS; 1132 /* Site specific versions */ 1133 ctx->site_domain_controller.type = AD_DIRECTORY; 1134 ctx->site_global_catalog.type = AD_DIRECTORY; 1135 return (ctx); 1136 } 1137 1138 1139 void 1140 ad_disc_fini(ad_disc_t ctx) 1141 { 1142 if (ctx == NULL) 1143 return; 1144 1145 if (ctx->res_ninitted) 1146 res_ndestroy(&ctx->res_state); 1147 1148 if (ctx->subnets != NULL) 1149 free(ctx->subnets); 1150 1151 if (ctx->domain_name.value != NULL) 1152 free(ctx->domain_name.value); 1153 1154 if (ctx->domain_controller.value != NULL) 1155 free(ctx->domain_controller.value); 1156 1157 if (ctx->site_name.value != NULL) 1158 free(ctx->site_name.value); 1159 1160 if (ctx->forest_name.value != NULL) 1161 free(ctx->forest_name.value); 1162 1163 if (ctx->global_catalog.value != NULL) 1164 free(ctx->global_catalog.value); 1165 1166 if (ctx->domains_in_forest.value != NULL) 1167 free(ctx->domains_in_forest.value); 1168 1169 if (ctx->trusted_domains.value != NULL) 1170 free(ctx->trusted_domains.value); 1171 1172 /* Site specific versions */ 1173 if (ctx->site_domain_controller.value != NULL) 1174 free(ctx->site_domain_controller.value); 1175 1176 if (ctx->site_global_catalog.value != NULL) 1177 free(ctx->site_global_catalog.value); 1178 1179 free(ctx); 1180 } 1181 1182 void 1183 ad_disc_refresh(ad_disc_t ctx) 1184 { 1185 if (ctx->res_ninitted) 1186 res_ndestroy(&ctx->res_state); 1187 (void) memset(&ctx->res_state, 0, sizeof (ctx->res_state)); 1188 ctx->res_ninitted = res_ninit(&ctx->res_state) != -1; 1189 1190 if (ctx->domain_name.state == AD_STATE_AUTO) 1191 ctx->domain_name.state = AD_STATE_INVALID; 1192 1193 if (ctx->domain_controller.state == AD_STATE_AUTO) 1194 ctx->domain_controller.state = AD_STATE_INVALID; 1195 1196 if (ctx->site_name.state == AD_STATE_AUTO) 1197 ctx->site_name.state = AD_STATE_INVALID; 1198 1199 if (ctx->forest_name.state == AD_STATE_AUTO) 1200 ctx->forest_name.state = AD_STATE_INVALID; 1201 1202 if (ctx->global_catalog.state == AD_STATE_AUTO) 1203 ctx->global_catalog.state = AD_STATE_INVALID; 1204 1205 if (ctx->domains_in_forest.state == AD_STATE_AUTO) 1206 ctx->domains_in_forest.state = AD_STATE_INVALID; 1207 1208 if (ctx->trusted_domains.state == AD_STATE_AUTO) 1209 ctx->trusted_domains.state = AD_STATE_INVALID; 1210 1211 if (ctx->site_domain_controller.state == AD_STATE_AUTO) 1212 ctx->site_domain_controller.state = AD_STATE_INVALID; 1213 1214 if (ctx->site_global_catalog.state == AD_STATE_AUTO) 1215 ctx->site_global_catalog.state = AD_STATE_INVALID; 1216 } 1217 1218 1219 /* 1220 * Called when the discovery cycle is done. Sets a master TTL 1221 * that will avoid doing new time-based discoveries too soon after 1222 * the last discovery cycle. Most interesting when the discovery 1223 * cycle failed, because then the TTLs on the individual items will 1224 * not be updated and may go stale. 1225 */ 1226 void 1227 ad_disc_done(ad_disc_t ctx) 1228 { 1229 time_t now = time(NULL); 1230 1231 ctx->expires_not_before = now + MINIMUM_TTL; 1232 ctx->expires_not_after = now + MAXIMUM_TTL; 1233 } 1234 1235 1236 /* Discover joined Active Directory domainName */ 1237 static ad_item_t * 1238 validate_DomainName(ad_disc_t ctx) 1239 { 1240 idmap_ad_disc_ds_t *domain_controller = NULL; 1241 char *dname, *srvname; 1242 uint32_t ttl = 0; 1243 int len; 1244 1245 if (is_valid(&ctx->domain_name)) 1246 return (&ctx->domain_name); 1247 1248 1249 /* Try to find our domain by searching for DCs for it */ 1250 DO_RES_NINIT(ctx); 1251 domain_controller = srv_query(&ctx->res_state, LDAP_SRV_HEAD 1252 DC_SRV_TAIL, ctx->domain_name.value, &srvname, &ttl); 1253 1254 /* 1255 * If we can't find DCs by via res_nsearch() then there's no 1256 * point in trying anything else to discover the AD domain name. 1257 */ 1258 if (domain_controller == NULL) 1259 return (NULL); 1260 1261 free(domain_controller); 1262 /* 1263 * We have the FQDN of the SRV RR name, so now we extract the 1264 * domainname suffix from it. 1265 */ 1266 dname = strdup(srvname + strlen(LDAP_SRV_HEAD DC_SRV_TAIL) + 1267 1 /* for the dot between RR name and domainname */); 1268 1269 free(srvname); 1270 1271 if (dname == NULL) { 1272 logger(LOG_ERR, "Out of memory"); 1273 return (NULL); 1274 } 1275 1276 /* Eat any trailing dot */ 1277 len = strlen(dname); 1278 if (len > 0 && dname[len - 1] == '.') 1279 dname[len - 1] = '\0'; 1280 1281 update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl); 1282 1283 return (&ctx->domain_name); 1284 } 1285 1286 1287 char * 1288 ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered) 1289 { 1290 char *domain_name = NULL; 1291 ad_item_t *domain_name_item; 1292 1293 domain_name_item = validate_DomainName(ctx); 1294 1295 if (domain_name_item) { 1296 domain_name = strdup(domain_name_item->value); 1297 if (auto_discovered != NULL) 1298 *auto_discovered = 1299 (domain_name_item->state == AD_STATE_AUTO); 1300 } else if (auto_discovered != NULL) 1301 *auto_discovered = B_FALSE; 1302 1303 return (domain_name); 1304 } 1305 1306 1307 /* Discover domain controllers */ 1308 static ad_item_t * 1309 validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) 1310 { 1311 uint32_t ttl = 0; 1312 idmap_ad_disc_ds_t *domain_controller = NULL; 1313 boolean_t validate_global = B_FALSE; 1314 boolean_t validate_site = B_FALSE; 1315 ad_item_t *domain_name_item; 1316 ad_item_t *site_name_item = NULL; 1317 1318 /* If the values is fixed there will not be a site specific version */ 1319 if (is_fixed(&ctx->domain_controller)) 1320 return (&ctx->domain_controller); 1321 1322 domain_name_item = validate_DomainName(ctx); 1323 if (domain_name_item == NULL) 1324 return (NULL); 1325 1326 if (req == AD_DISC_GLOBAL) 1327 validate_global = B_TRUE; 1328 else { 1329 site_name_item = validate_SiteName(ctx); 1330 if (site_name_item != NULL) 1331 validate_site = B_TRUE; 1332 else if (req == AD_DISC_PREFER_SITE) 1333 validate_global = B_TRUE; 1334 } 1335 1336 if (validate_global) { 1337 if (!is_valid(&ctx->domain_controller) || 1338 is_changed(&ctx->domain_controller, PARAM1, 1339 domain_name_item)) { 1340 /* 1341 * Lookup DNS SRV RR named 1342 * _ldap._tcp.dc._msdcs.<DomainName> 1343 */ 1344 DO_RES_NINIT(ctx); 1345 domain_controller = srv_query(&ctx->res_state, 1346 LDAP_SRV_HEAD DC_SRV_TAIL, 1347 domain_name_item->value, NULL, &ttl); 1348 1349 if (domain_controller == NULL) 1350 return (NULL); 1351 1352 update_item(&ctx->domain_controller, domain_controller, 1353 AD_STATE_AUTO, ttl); 1354 update_version(&ctx->domain_controller, PARAM1, 1355 domain_name_item); 1356 } 1357 return (&ctx->domain_controller); 1358 } 1359 1360 if (validate_site) { 1361 if (!is_valid(&ctx->site_domain_controller) || 1362 is_changed(&ctx->site_domain_controller, PARAM1, 1363 domain_name_item) || 1364 is_changed(&ctx->site_domain_controller, PARAM2, 1365 site_name_item)) { 1366 char rr_name[DNS_MAX_NAME]; 1367 /* 1368 * Lookup DNS SRV RR named 1369 * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName> 1370 */ 1371 (void) snprintf(rr_name, sizeof (rr_name), 1372 LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL, 1373 site_name_item->value); 1374 DO_RES_NINIT(ctx); 1375 domain_controller = srv_query(&ctx->res_state, rr_name, 1376 domain_name_item->value, NULL, &ttl); 1377 if (domain_controller == NULL) 1378 return (NULL); 1379 1380 update_item(&ctx->site_domain_controller, 1381 domain_controller, AD_STATE_AUTO, ttl); 1382 update_version(&ctx->site_domain_controller, PARAM1, 1383 domain_name_item); 1384 update_version(&ctx->site_domain_controller, PARAM2, 1385 site_name_item); 1386 } 1387 return (&ctx->site_domain_controller); 1388 } 1389 return (NULL); 1390 } 1391 1392 idmap_ad_disc_ds_t * 1393 ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req, 1394 boolean_t *auto_discovered) 1395 { 1396 ad_item_t *domain_controller_item; 1397 idmap_ad_disc_ds_t *domain_controller = NULL; 1398 1399 domain_controller_item = validate_DomainController(ctx, req); 1400 1401 if (domain_controller_item != NULL) { 1402 domain_controller = ds_dup(domain_controller_item->value); 1403 if (auto_discovered != NULL) 1404 *auto_discovered = 1405 (domain_controller_item->state == AD_STATE_AUTO); 1406 } else if (auto_discovered != NULL) 1407 *auto_discovered = B_FALSE; 1408 1409 return (domain_controller); 1410 } 1411 1412 1413 /* Discover site name (for multi-homed systems the first one found wins) */ 1414 static ad_item_t * 1415 validate_SiteName(ad_disc_t ctx) 1416 { 1417 LDAP *ld = NULL; 1418 ad_subnet_t *subnets = NULL; 1419 char **dn_subnets = NULL; 1420 char *dn_root[2]; 1421 char *config_naming_context = NULL; 1422 char *site_object = NULL; 1423 char *site_name = NULL; 1424 char *forest_name; 1425 int len; 1426 int i; 1427 boolean_t update_required = B_FALSE; 1428 ad_item_t *domain_controller_item; 1429 1430 if (is_fixed(&ctx->site_name)) 1431 return (&ctx->site_name); 1432 1433 /* Can't rely on site-specific DCs */ 1434 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 1435 if (domain_controller_item == NULL) 1436 return (NULL); 1437 1438 if (!is_valid(&ctx->site_name) || 1439 is_changed(&ctx->site_name, PARAM1, domain_controller_item) || 1440 ctx->subnets == NULL || ctx->subnets_changed) { 1441 subnets = find_subnets(); 1442 ctx->subnets_last_check = time(NULL); 1443 update_required = B_TRUE; 1444 } else if (ctx->subnets_last_check + 60 < time(NULL)) { 1445 /* NEEDSWORK magic constant 60 above */ 1446 subnets = find_subnets(); 1447 ctx->subnets_last_check = time(NULL); 1448 if (cmpsubnets(ctx->subnets, subnets) != 0) 1449 update_required = B_TRUE; 1450 } 1451 1452 if (!update_required) { 1453 free(subnets); 1454 return (&ctx->site_name); 1455 } 1456 1457 if (subnets == NULL) 1458 return (NULL); 1459 1460 dn_root[0] = ""; 1461 dn_root[1] = NULL; 1462 1463 config_naming_context = ldap_lookup_entry_attr( 1464 &ld, ctx->domain_controller.value, 1465 dn_root, "configurationNamingContext"); 1466 if (config_naming_context == NULL) 1467 goto out; 1468 /* 1469 * configurationNamingContext also provides the Forest 1470 * Name. 1471 */ 1472 if (!is_fixed(&ctx->forest_name)) { 1473 /* 1474 * The configurationNamingContext should be of 1475 * form: 1476 * CN=Configuration,<DNforestName> 1477 * Remove the first part and convert to DNS form 1478 * (replace ",DC=" with ".") 1479 */ 1480 char *str = "CN=Configuration,"; 1481 int len = strlen(str); 1482 if (strncasecmp(config_naming_context, str, len) == 0) { 1483 forest_name = DN_to_DNS(config_naming_context + len); 1484 update_item(&ctx->forest_name, forest_name, 1485 AD_STATE_AUTO, 0); 1486 update_version(&ctx->forest_name, PARAM1, 1487 domain_controller_item); 1488 } 1489 } 1490 1491 dn_subnets = subnets_to_DNs(subnets, config_naming_context); 1492 if (dn_subnets == NULL) 1493 goto out; 1494 1495 site_object = ldap_lookup_entry_attr( 1496 &ld, domain_controller_item->value, 1497 dn_subnets, "siteobject"); 1498 if (site_object != NULL) { 1499 /* 1500 * The site object should be of the form 1501 * CN=<site>,CN=Sites,CN=Configuration, 1502 * <DN Domain> 1503 */ 1504 if (strncasecmp(site_object, "CN=", 3) == 0) { 1505 for (len = 0; site_object[len + 3] != ','; len++) 1506 ; 1507 site_name = malloc(len + 1); 1508 (void) strncpy(site_name, &site_object[3], len); 1509 site_name[len] = '\0'; 1510 update_item(&ctx->site_name, site_name, 1511 AD_STATE_AUTO, 0); 1512 update_version(&ctx->site_name, PARAM1, 1513 domain_controller_item); 1514 } 1515 } 1516 1517 if (ctx->subnets != NULL) { 1518 free(ctx->subnets); 1519 ctx->subnets = NULL; 1520 } 1521 ctx->subnets = subnets; 1522 subnets = NULL; 1523 ctx->subnets_changed = B_FALSE; 1524 1525 out: 1526 if (ld != NULL) 1527 (void) ldap_unbind(ld); 1528 1529 if (dn_subnets != NULL) { 1530 for (i = 0; dn_subnets[i] != NULL; i++) 1531 free(dn_subnets[i]); 1532 free(dn_subnets); 1533 } 1534 if (config_naming_context != NULL) 1535 free(config_naming_context); 1536 if (site_object != NULL) 1537 free(site_object); 1538 1539 free(subnets); 1540 if (site_name == NULL) 1541 return (NULL); 1542 return (&ctx->site_name); 1543 1544 } 1545 1546 1547 char * 1548 ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered) 1549 { 1550 ad_item_t *site_name_item; 1551 char *site_name = NULL; 1552 1553 site_name_item = validate_SiteName(ctx); 1554 if (site_name_item != NULL) { 1555 site_name = strdup(site_name_item->value); 1556 if (auto_discovered != NULL) 1557 *auto_discovered = 1558 (site_name_item->state == AD_STATE_AUTO); 1559 } else if (auto_discovered != NULL) 1560 *auto_discovered = B_FALSE; 1561 1562 return (site_name); 1563 } 1564 1565 1566 1567 /* Discover forest name */ 1568 static ad_item_t * 1569 validate_ForestName(ad_disc_t ctx) 1570 { 1571 LDAP *ld = NULL; 1572 char *config_naming_context; 1573 char *forest_name = NULL; 1574 char *dn_list[2]; 1575 ad_item_t *domain_controller_item; 1576 1577 if (is_fixed(&ctx->forest_name)) 1578 return (&ctx->forest_name); 1579 /* 1580 * We may not have a site name yet, so we won't rely on 1581 * site-specific DCs. (But maybe we could replace 1582 * validate_ForestName() with validate_siteName()?) 1583 */ 1584 domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); 1585 if (domain_controller_item == NULL) 1586 return (NULL); 1587 1588 if (!is_valid(&ctx->forest_name) || 1589 is_changed(&ctx->forest_name, PARAM1, domain_controller_item)) { 1590 1591 dn_list[0] = ""; 1592 dn_list[1] = NULL; 1593 config_naming_context = ldap_lookup_entry_attr( 1594 &ld, ctx->domain_controller.value, 1595 dn_list, "configurationNamingContext"); 1596 if (config_naming_context != NULL) { 1597 /* 1598 * The configurationNamingContext should be of 1599 * form: 1600 * CN=Configuration,<DNforestName> 1601 * Remove the first part and convert to DNS form 1602 * (replace ",DC=" with ".") 1603 */ 1604 char *str = "CN=Configuration,"; 1605 int len = strlen(str); 1606 if (strncasecmp(config_naming_context, str, len) == 0) { 1607 forest_name = DN_to_DNS( 1608 config_naming_context + len); 1609 } 1610 free(config_naming_context); 1611 } 1612 if (ld != NULL) 1613 (void) ldap_unbind(ld); 1614 1615 if (forest_name == NULL) 1616 return (NULL); 1617 1618 update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0); 1619 update_version(&ctx->forest_name, PARAM1, 1620 domain_controller_item); 1621 } 1622 return (&ctx->forest_name); 1623 } 1624 1625 1626 char * 1627 ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered) 1628 { 1629 ad_item_t *forest_name_item; 1630 char *forest_name = NULL; 1631 1632 forest_name_item = validate_ForestName(ctx); 1633 1634 if (forest_name_item != NULL) { 1635 forest_name = strdup(forest_name_item->value); 1636 if (auto_discovered != NULL) 1637 *auto_discovered = 1638 (forest_name_item->state == AD_STATE_AUTO); 1639 } else if (auto_discovered != NULL) 1640 *auto_discovered = B_FALSE; 1641 1642 return (forest_name); 1643 } 1644 1645 1646 /* Discover global catalog servers */ 1647 static ad_item_t * 1648 validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) 1649 { 1650 idmap_ad_disc_ds_t *global_catalog = NULL; 1651 uint32_t ttl = 0; 1652 boolean_t validate_global = B_FALSE; 1653 boolean_t validate_site = B_FALSE; 1654 ad_item_t *forest_name_item; 1655 ad_item_t *site_name_item; 1656 1657 /* If the values is fixed there will not be a site specific version */ 1658 if (is_fixed(&ctx->global_catalog)) 1659 return (&ctx->global_catalog); 1660 1661 forest_name_item = validate_ForestName(ctx); 1662 if (forest_name_item == NULL) 1663 return (NULL); 1664 1665 if (req == AD_DISC_GLOBAL) 1666 validate_global = B_TRUE; 1667 else { 1668 site_name_item = validate_SiteName(ctx); 1669 if (site_name_item != NULL) 1670 validate_site = B_TRUE; 1671 else if (req == AD_DISC_PREFER_SITE) 1672 validate_global = B_TRUE; 1673 } 1674 1675 if (validate_global) { 1676 if (!is_valid(&ctx->global_catalog) || 1677 is_changed(&ctx->global_catalog, PARAM1, 1678 forest_name_item)) { 1679 /* 1680 * Lookup DNS SRV RR named 1681 * _ldap._tcp.gc._msdcs.<ForestName> 1682 */ 1683 DO_RES_NINIT(ctx); 1684 global_catalog = 1685 srv_query(&ctx->res_state, 1686 LDAP_SRV_HEAD GC_SRV_TAIL, 1687 ctx->forest_name.value, NULL, &ttl); 1688 1689 if (global_catalog == NULL) 1690 return (NULL); 1691 1692 update_item(&ctx->global_catalog, global_catalog, 1693 AD_STATE_AUTO, ttl); 1694 update_version(&ctx->global_catalog, PARAM1, 1695 forest_name_item); 1696 } 1697 return (&ctx->global_catalog); 1698 } 1699 1700 if (validate_site) { 1701 if (!is_valid(&ctx->site_global_catalog) || 1702 is_changed(&ctx->site_global_catalog, PARAM1, 1703 forest_name_item) || 1704 is_changed(&ctx->site_global_catalog, PARAM2, 1705 site_name_item)) { 1706 char rr_name[DNS_MAX_NAME]; 1707 1708 /* 1709 * Lookup DNS SRV RR named: 1710 * _ldap._tcp.<siteName>._sites.gc. 1711 * _msdcs.<ForestName> 1712 */ 1713 (void) snprintf(rr_name, 1714 sizeof (rr_name), 1715 LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL, 1716 ctx->site_name.value); 1717 DO_RES_NINIT(ctx); 1718 global_catalog = srv_query(&ctx->res_state, rr_name, 1719 ctx->forest_name.value, NULL, &ttl); 1720 1721 if (global_catalog == NULL) 1722 return (NULL); 1723 update_item(&ctx->site_global_catalog, global_catalog, 1724 AD_STATE_AUTO, ttl); 1725 update_version(&ctx->site_global_catalog, PARAM1, 1726 forest_name_item); 1727 update_version(&ctx->site_global_catalog, PARAM2, 1728 site_name_item); 1729 } 1730 return (&ctx->site_global_catalog); 1731 } 1732 return (NULL); 1733 } 1734 1735 1736 idmap_ad_disc_ds_t * 1737 ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req, 1738 boolean_t *auto_discovered) 1739 { 1740 idmap_ad_disc_ds_t *global_catalog = NULL; 1741 ad_item_t *global_catalog_item; 1742 1743 global_catalog_item = validate_GlobalCatalog(ctx, req); 1744 1745 if (global_catalog_item != NULL) { 1746 global_catalog = ds_dup(global_catalog_item->value); 1747 if (auto_discovered != NULL) 1748 *auto_discovered = 1749 (global_catalog_item->state == AD_STATE_AUTO); 1750 } else if (auto_discovered != NULL) 1751 *auto_discovered = B_FALSE; 1752 1753 return (global_catalog); 1754 } 1755 1756 1757 static ad_item_t * 1758 validate_TrustedDomains(ad_disc_t ctx) 1759 { 1760 LDAP *ld = NULL; 1761 ad_item_t *global_catalog_item; 1762 ad_item_t *forest_name_item; 1763 ad_disc_trusteddomains_t *trusted_domains; 1764 char *dn = NULL; 1765 char *forest_name_dn; 1766 int len; 1767 int num_parts; 1768 1769 if (is_fixed(&ctx->trusted_domains)) 1770 return (&ctx->trusted_domains); 1771 1772 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 1773 if (global_catalog_item == NULL) 1774 return (NULL); 1775 1776 forest_name_item = validate_ForestName(ctx); 1777 if (forest_name_item == NULL) 1778 return (NULL); 1779 1780 if (!is_valid(&ctx->trusted_domains) || 1781 is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) || 1782 is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) { 1783 1784 forest_name_dn = ldap_dns_to_dn(forest_name_item->value, 1785 &num_parts); 1786 if (forest_name_dn == NULL) 1787 return (NULL); 1788 1789 len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1; 1790 dn = malloc(len); 1791 if (dn == NULL) { 1792 free(forest_name_dn); 1793 return (NULL); 1794 } 1795 (void) snprintf(dn, len, "CN=System,%s", forest_name_dn); 1796 free(forest_name_dn); 1797 1798 trusted_domains = ldap_lookup_trusted_domains( 1799 &ld, global_catalog_item->value, dn); 1800 1801 if (ld != NULL) 1802 (void) ldap_unbind(ld); 1803 free(dn); 1804 1805 if (trusted_domains == NULL) 1806 return (NULL); 1807 1808 update_item(&ctx->trusted_domains, trusted_domains, 1809 AD_STATE_AUTO, 0); 1810 update_version(&ctx->trusted_domains, PARAM1, 1811 global_catalog_item); 1812 update_version(&ctx->trusted_domains, PARAM2, 1813 forest_name_item); 1814 } 1815 1816 return (&ctx->trusted_domains); 1817 } 1818 1819 1820 ad_disc_trusteddomains_t * 1821 ad_disc_get_TrustedDomains(ad_disc_t ctx, boolean_t *auto_discovered) 1822 { 1823 ad_disc_trusteddomains_t *trusted_domains = NULL; 1824 ad_item_t *trusted_domains_item; 1825 1826 trusted_domains_item = validate_TrustedDomains(ctx); 1827 1828 if (trusted_domains_item != NULL) { 1829 trusted_domains = td_dup(trusted_domains_item->value); 1830 if (auto_discovered != NULL) 1831 *auto_discovered = 1832 (trusted_domains_item->state == AD_STATE_AUTO); 1833 } else if (auto_discovered != NULL) 1834 *auto_discovered = B_FALSE; 1835 1836 return (trusted_domains); 1837 } 1838 1839 1840 static ad_item_t * 1841 validate_DomainsInForest(ad_disc_t ctx) 1842 { 1843 ad_item_t *global_catalog_item; 1844 LDAP *ld = NULL; 1845 ad_disc_domainsinforest_t *domains_in_forest; 1846 1847 if (is_fixed(&ctx->domains_in_forest)) 1848 return (&ctx->domains_in_forest); 1849 1850 global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL); 1851 if (global_catalog_item == NULL) 1852 return (NULL); 1853 1854 if (!is_valid(&ctx->domains_in_forest) || 1855 is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) { 1856 1857 domains_in_forest = ldap_lookup_domains_in_forest( 1858 &ld, global_catalog_item->value); 1859 1860 if (ld != NULL) 1861 (void) ldap_unbind(ld); 1862 1863 if (domains_in_forest == NULL) 1864 return (NULL); 1865 1866 update_item(&ctx->domains_in_forest, domains_in_forest, 1867 AD_STATE_AUTO, 0); 1868 update_version(&ctx->domains_in_forest, PARAM1, 1869 global_catalog_item); 1870 } 1871 return (&ctx->domains_in_forest); 1872 } 1873 1874 1875 ad_disc_domainsinforest_t * 1876 ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered) 1877 { 1878 ad_disc_domainsinforest_t *domains_in_forest = NULL; 1879 ad_item_t *domains_in_forest_item; 1880 1881 domains_in_forest_item = validate_DomainsInForest(ctx); 1882 1883 if (domains_in_forest_item != NULL) { 1884 domains_in_forest = df_dup(domains_in_forest_item->value); 1885 if (auto_discovered != NULL) 1886 *auto_discovered = 1887 (domains_in_forest_item->state == AD_STATE_AUTO); 1888 } else if (auto_discovered != NULL) 1889 *auto_discovered = B_FALSE; 1890 1891 return (domains_in_forest); 1892 } 1893 1894 1895 1896 1897 int 1898 ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName) 1899 { 1900 char *domain_name = NULL; 1901 if (domainName != NULL) { 1902 domain_name = strdup(domainName); 1903 if (domain_name == NULL) 1904 return (-1); 1905 update_item(&ctx->domain_name, domain_name, 1906 AD_STATE_FIXED, 0); 1907 } else if (ctx->domain_name.state == AD_STATE_FIXED) 1908 ctx->domain_name.state = AD_STATE_INVALID; 1909 return (0); 1910 } 1911 1912 1913 int 1914 ad_disc_set_DomainController(ad_disc_t ctx, 1915 const idmap_ad_disc_ds_t *domainController) 1916 { 1917 idmap_ad_disc_ds_t *domain_controller = NULL; 1918 if (domainController != NULL) { 1919 domain_controller = ds_dup(domainController); 1920 if (domain_controller == NULL) 1921 return (-1); 1922 update_item(&ctx->domain_controller, domain_controller, 1923 AD_STATE_FIXED, 0); 1924 } else if (ctx->domain_controller.state == AD_STATE_FIXED) 1925 ctx->domain_controller.state = AD_STATE_INVALID; 1926 return (0); 1927 } 1928 1929 1930 int 1931 ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName) 1932 { 1933 char *site_name = NULL; 1934 if (siteName != NULL) { 1935 site_name = strdup(siteName); 1936 if (site_name == NULL) 1937 return (-1); 1938 update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0); 1939 } else if (ctx->site_name.state == AD_STATE_FIXED) 1940 ctx->site_name.state = AD_STATE_INVALID; 1941 return (0); 1942 } 1943 1944 int 1945 ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName) 1946 { 1947 char *forest_name = NULL; 1948 if (forestName != NULL) { 1949 forest_name = strdup(forestName); 1950 if (forest_name == NULL) 1951 return (-1); 1952 update_item(&ctx->forest_name, forest_name, 1953 AD_STATE_FIXED, 0); 1954 } else if (ctx->forest_name.state == AD_STATE_FIXED) 1955 ctx->forest_name.state = AD_STATE_INVALID; 1956 return (0); 1957 } 1958 1959 int 1960 ad_disc_set_GlobalCatalog(ad_disc_t ctx, 1961 const idmap_ad_disc_ds_t *globalCatalog) 1962 { 1963 idmap_ad_disc_ds_t *global_catalog = NULL; 1964 if (globalCatalog != NULL) { 1965 global_catalog = ds_dup(globalCatalog); 1966 if (global_catalog == NULL) 1967 return (-1); 1968 update_item(&ctx->global_catalog, global_catalog, 1969 AD_STATE_FIXED, 0); 1970 } else if (ctx->global_catalog.state == AD_STATE_FIXED) 1971 ctx->global_catalog.state = AD_STATE_INVALID; 1972 return (0); 1973 } 1974 1975 1976 int 1977 ad_disc_unset(ad_disc_t ctx) 1978 { 1979 if (ctx->domain_name.state == AD_STATE_FIXED) 1980 ctx->domain_name.state = AD_STATE_INVALID; 1981 1982 if (ctx->domain_controller.state == AD_STATE_FIXED) 1983 ctx->domain_controller.state = AD_STATE_INVALID; 1984 1985 if (ctx->site_name.state == AD_STATE_FIXED) 1986 ctx->site_name.state = AD_STATE_INVALID; 1987 1988 if (ctx->forest_name.state == AD_STATE_FIXED) 1989 ctx->forest_name.state = AD_STATE_INVALID; 1990 1991 if (ctx->global_catalog.state == AD_STATE_FIXED) 1992 ctx->global_catalog.state = AD_STATE_INVALID; 1993 1994 return (0); 1995 } 1996 1997 /* 1998 * ad_disc_get_TTL 1999 * 2000 * This routines the time to live for AD 2001 * auto discovered items. 2002 * 2003 * Returns: 2004 * -1 if there are no TTL items 2005 * 0 if there are expired items 2006 * else the number of seconds 2007 * 2008 * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it 2009 * is positive -- min() greater than zero. 2010 */ 2011 #define MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \ 2012 (-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x)))) 2013 int 2014 ad_disc_get_TTL(ad_disc_t ctx) 2015 { 2016 time_t expires; 2017 int ttl; 2018 2019 expires = MIN_GT_ZERO(ctx->domain_controller.expires, 2020 ctx->global_catalog.expires); 2021 expires = MIN_GT_ZERO(expires, ctx->site_domain_controller.expires); 2022 expires = MIN_GT_ZERO(expires, ctx->site_global_catalog.expires); 2023 2024 if (expires == -1) { 2025 return (-1); 2026 } 2027 2028 if (ctx->expires_not_before != 0 && 2029 expires < ctx->expires_not_before) { 2030 expires = ctx->expires_not_before; 2031 } 2032 2033 if (ctx->expires_not_after != 0 && 2034 expires > ctx->expires_not_after) { 2035 expires = ctx->expires_not_after; 2036 } 2037 2038 ttl = expires - time(NULL); 2039 2040 if (ttl < 0) { 2041 return (0); 2042 } 2043 return (ttl); 2044 } 2045 2046 boolean_t 2047 ad_disc_SubnetChanged(ad_disc_t ctx) 2048 { 2049 ad_subnet_t *subnets; 2050 2051 if (ctx->subnets_changed || ctx->subnets == NULL) 2052 return (B_TRUE); 2053 2054 if ((subnets = find_subnets()) != NULL) { 2055 if (cmpsubnets(subnets, ctx->subnets) != 0) 2056 ctx->subnets_changed = B_TRUE; 2057 free(subnets); 2058 } 2059 2060 return (ctx->subnets_changed); 2061 } 2062