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