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