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