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