xref: /illumos-gate/usr/src/lib/libadutils/common/addisc.c (revision 069e6b7e31ba5dcbc5441b98af272714d9a5455c)
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 }
2129