xref: /illumos-gate/usr/src/lib/libadutils/common/addisc.c (revision d0bed8f264c913bf83285b0beed22bd3a9f7eb08)
1c8e26105Sjp /*
2c8e26105Sjp  * CDDL HEADER START
3c8e26105Sjp  *
4c8e26105Sjp  * The contents of this file are subject to the terms of the
5c8e26105Sjp  * Common Development and Distribution License (the "License").
6c8e26105Sjp  * You may not use this file except in compliance with the License.
7c8e26105Sjp  *
8c8e26105Sjp  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9c8e26105Sjp  * or http://www.opensolaris.org/os/licensing.
10c8e26105Sjp  * See the License for the specific language governing permissions
11c8e26105Sjp  * and limitations under the License.
12c8e26105Sjp  *
13c8e26105Sjp  * When distributing Covered Code, include this CDDL HEADER in each
14c8e26105Sjp  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15c8e26105Sjp  * If applicable, add the following below this CDDL HEADER, with the
16c8e26105Sjp  * fields enclosed by brackets "[]" replaced with your own identifying
17c8e26105Sjp  * information: Portions Copyright [yyyy] [name of copyright owner]
18c8e26105Sjp  *
19c8e26105Sjp  * CDDL HEADER END
20c8e26105Sjp  */
21c8e26105Sjp 
22c8e26105Sjp /*
23148c5f43SAlan Wright  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24*d0bed8f2SGordon Ross  * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
25c8e26105Sjp  */
26c8e26105Sjp 
270dcc7149Snw /*
280dcc7149Snw  * Active Directory Auto-Discovery.
290dcc7149Snw  *
300dcc7149Snw  * This [project private] API allows the caller to provide whatever
310dcc7149Snw  * details it knows a priori (i.e., provided via configuration so as to
320dcc7149Snw  * override auto-discovery) and in any order.  Then the caller can ask
330dcc7149Snw  * for any of the auto-discoverable parameters in any order.
340dcc7149Snw  *
350dcc7149Snw  * But there is an actual order in which discovery must be done.  Given
360dcc7149Snw  * the discovery mechanism implemented here, that order is:
370dcc7149Snw  *
380dcc7149Snw  *  - the domain name joined must be discovered first
390dcc7149Snw  *  - then the domain controllers
400dcc7149Snw  *  - then the forest name and site name
410dcc7149Snw  *  - then the global catalog servers, and site-specific domain
420dcc7149Snw  *    controllers and global catalog servers.
430dcc7149Snw  *
440dcc7149Snw  * The API does not require it be called in the same order because there
450dcc7149Snw  * may be other discovery mechanisms in the future, and exposing
460dcc7149Snw  * ordering requirements of the current mechanism now can create trouble
470dcc7149Snw  * down the line.  Also, this makes the API easier to use now, which
480dcc7149Snw  * means less work to do some day when we make this a public API.
490dcc7149Snw  *
500dcc7149Snw  * Domain discovery is done by res_nsearch() of the DNS SRV RR name for
510dcc7149Snw  * domain controllers.  As long as the joined domain appears in the DNS
520dcc7149Snw  * resolver's search list then we'll find it.
530dcc7149Snw  *
540dcc7149Snw  * Domain controller discovery is a matter of formatting the DNS SRV RR
550dcc7149Snw  * FQDN for domain controllers and doing a lookup for them.  Knowledge
560dcc7149Snw  * of the domain name is not fundamentally required, but we separate the
570dcc7149Snw  * two processes, which in practice can lead to one more DNS lookup than
580dcc7149Snw  * is strictly required.
590dcc7149Snw  *
600dcc7149Snw  * Forest and site name discovery require an LDAP search of the AD
610dcc7149Snw  * "configuration partition" at a domain controller for the joined
620dcc7149Snw  * domain.  Forest and site name discovery depend on knowing the joined
630dcc7149Snw  * domain name and domain controllers for that domain.
640dcc7149Snw  *
650dcc7149Snw  * Global catalog server discovery requires knowledge of the forest
660dcc7149Snw  * name in order to format the DNS SRV RR FQDN to lookup.  Site-specific
670dcc7149Snw  * domain controller discovery depends on knowing the site name (and,
680dcc7149Snw  * therefore, joined domain, ...).  Site-specific global catalog server
690dcc7149Snw  * discovery depends on knowledge of the forest and site names, which
700dcc7149Snw  * depend on...
710dcc7149Snw  *
720dcc7149Snw  * All the work of discovering particular items is done by functions
730dcc7149Snw  * named validate_<item>().  Each such function calls validate_<item>()
740dcc7149Snw  * for any items that it depends on.
750dcc7149Snw  *
760dcc7149Snw  * This API is not thread-safe.
770dcc7149Snw  */
780dcc7149Snw 
79c8e26105Sjp 
80c8e26105Sjp #include <stdio.h>
81c8e26105Sjp #include <string.h>
82c8e26105Sjp #include <strings.h>
83c8e26105Sjp #include <unistd.h>
844d61c878SJulian Pullen #include <assert.h>
85c8e26105Sjp #include <stdlib.h>
86c8e26105Sjp #include <net/if.h>
87c8e26105Sjp #include <sys/types.h>
88c8e26105Sjp #include <sys/socket.h>
89c8e26105Sjp #include <sys/sockio.h>
90c8e26105Sjp #include <netinet/in.h>
91c8e26105Sjp #include <arpa/inet.h>
92c8e26105Sjp #include <arpa/nameser.h>
93c8e26105Sjp #include <resolv.h>
94c8e26105Sjp #include <netdb.h>
95c8e26105Sjp #include <ctype.h>
96c8e26105Sjp #include <errno.h>
97c8e26105Sjp #include <ldap.h>
98b3700b07SGordon Ross #include <note.h>
99c8e26105Sjp #include <sasl/sasl.h>
1004d61c878SJulian Pullen #include <sys/u8_textprep.h>
1017a8a68f5SJulian Pullen #include <syslog.h>
102b3700b07SGordon Ross #include <uuid/uuid.h>
103b3700b07SGordon Ross #include <ads/dsgetdc.h>
1047a8a68f5SJulian Pullen #include "adutils_impl.h"
105b3700b07SGordon Ross #include "addisc_impl.h"
106c8e26105Sjp 
107c5866007SKeyur Desai /*
108c5866007SKeyur Desai  * These set some sanity policies for discovery.  After a discovery
109c5866007SKeyur Desai  * cycle, we will consider the results (successful or unsuccessful)
110c5866007SKeyur Desai  * to be valid for at least MINIMUM_TTL seconds, and for at most
111c5866007SKeyur Desai  * MAXIMUM_TTL seconds.  Note that the caller is free to request
112c5866007SKeyur Desai  * discovery cycles sooner than MINIMUM_TTL if it has reason to believe
113c5866007SKeyur Desai  * that the situation has changed.
114c5866007SKeyur Desai  */
115c5866007SKeyur Desai #define	MINIMUM_TTL	(5 * 60)
116c5866007SKeyur Desai #define	MAXIMUM_TTL	(20 * 60)
117c8e26105Sjp 
118c8e26105Sjp 
119c8e26105Sjp #define	DNS_MAX_NAME	NS_MAXDNAME
120c8e26105Sjp 
121b3700b07SGordon Ross #define	GC_PORT		3268
122c8e26105Sjp 
123c8e26105Sjp /* SRV RR names for various queries */
124c8e26105Sjp #define	LDAP_SRV_HEAD		"_ldap._tcp."
125c8e26105Sjp #define	SITE_SRV_MIDDLE		"%s._sites."
126c8e26105Sjp #define	GC_SRV_TAIL		"gc._msdcs"
127c8e26105Sjp #define	DC_SRV_TAIL		"dc._msdcs"
128c8e26105Sjp #define	ALL_GC_SRV_TAIL		"_gc._tcp"
129c8e26105Sjp #define	PDC_SRV			 "_ldap._tcp.pdc._msdcs.%s"
130c8e26105Sjp 
131c8e26105Sjp /* A RR name for all GCs -- last resort this works */
132c8e26105Sjp #define	GC_ALL_A_NAME_FSTR "gc._msdcs.%s."
133c8e26105Sjp 
134c8e26105Sjp 
1350dcc7149Snw /*
1360dcc7149Snw  * We try res_ninit() whenever we don't have one.  res_ninit() fails if
1370dcc7149Snw  * idmapd is running before the network is up!
1380dcc7149Snw  */
139b3700b07SGordon Ross #define	DO_RES_NINIT(ctx)				\
140b3700b07SGordon Ross 	if (!(ctx)->res_ninitted)			\
141b3700b07SGordon Ross 		(void) do_res_ninit(ctx)
142b3700b07SGordon Ross 
143b3700b07SGordon Ross #define	DO_GETNAMEINFO(b, l, s)				\
144b3700b07SGordon Ross 	if (ad_disc_getnameinfo(b, l, s) != 0)		\
145b3700b07SGordon Ross 		(void) strlcpy(b, "?", l)
146b3700b07SGordon Ross 
147b3700b07SGordon Ross #define	DEBUG1STATUS(ctx, ...) do { \
148b3700b07SGordon Ross 	if (DBG(DISC, 1)) \
149b3700b07SGordon Ross 		logger(LOG_DEBUG, __VA_ARGS__); \
150b3700b07SGordon Ross 	if (ctx->status_fp) { \
151b3700b07SGordon Ross 		(void) fprintf(ctx->status_fp, __VA_ARGS__); \
152b3700b07SGordon Ross 		(void) fprintf(ctx->status_fp, "\n"); \
153b3700b07SGordon Ross 	} \
154b3700b07SGordon Ross 	_NOTE(CONSTCOND) \
155b3700b07SGordon Ross } while (0)
156c8e26105Sjp 
157c8e26105Sjp #define	is_fixed(item)					\
1584d61c878SJulian Pullen 	((item)->state == AD_STATE_FIXED)
159c8e26105Sjp 
160*d0bed8f2SGordon Ross #define	is_changed(item, num, param)			\
161c8e26105Sjp 	((item)->param_version[num] != (param)->version)
162c8e26105Sjp 
163b3700b07SGordon Ross void * uuid_dup(void *);
164b3700b07SGordon Ross 
165b3700b07SGordon Ross static ad_item_t *validate_SiteName(ad_disc_t ctx);
166b3700b07SGordon Ross static ad_item_t *validate_PreferredDC(ad_disc_t ctx);
167b3700b07SGordon Ross 
168c8e26105Sjp /*
169c8e26105Sjp  * Function definitions
170c8e26105Sjp  */
171c8e26105Sjp 
172c8e26105Sjp 
173b3700b07SGordon Ross static int
174b3700b07SGordon Ross do_res_ninit(ad_disc_t ctx)
175b3700b07SGordon Ross {
176b3700b07SGordon Ross 	int rc;
177b3700b07SGordon Ross 
178b3700b07SGordon Ross 	rc = res_ninit(&ctx->res_state);
179b3700b07SGordon Ross 	if (rc != 0)
180b3700b07SGordon Ross 		return (rc);
181b3700b07SGordon Ross 	ctx->res_ninitted = 1;
182b3700b07SGordon Ross 	/*
183b3700b07SGordon Ross 	 * The SRV records returnd by AD can be larger than 512 bytes,
184b3700b07SGordon Ross 	 * so we'd like to use TCP for those searches.  Unfortunately,
185b3700b07SGordon Ross 	 * the TCP connect timeout seen by the resolver is very long
186b3700b07SGordon Ross 	 * (more than a couple minutes) and we can't wait that long.
187b3700b07SGordon Ross 	 * Don't do use TCP until we can override the timeout.
188b3700b07SGordon Ross 	 *
189b3700b07SGordon Ross 	 * Note that some queries will try TCP anyway.
190b3700b07SGordon Ross 	 */
191b3700b07SGordon Ross #if 0
192b3700b07SGordon Ross 	ctx->res_state.options |= RES_USEVC;
193b3700b07SGordon Ross #endif
194b3700b07SGordon Ross 	return (0);
195b3700b07SGordon Ross }
196b3700b07SGordon Ross 
197b3700b07SGordon Ross /*
198b3700b07SGordon Ross  * Private getnameinfo(3socket) variant tailored to our needs.
199b3700b07SGordon Ross  */
200b3700b07SGordon Ross int
201b3700b07SGordon Ross ad_disc_getnameinfo(char *obuf, int olen, struct sockaddr_storage *ss)
202b3700b07SGordon Ross {
203b3700b07SGordon Ross 	struct sockaddr *sa;
204b3700b07SGordon Ross 	int eai, slen;
205b3700b07SGordon Ross 
206b3700b07SGordon Ross 	sa = (void *)ss;
207b3700b07SGordon Ross 	switch (sa->sa_family) {
208b3700b07SGordon Ross 	case AF_INET:
209b3700b07SGordon Ross 		slen = sizeof (struct sockaddr_in);
210b3700b07SGordon Ross 		break;
211b3700b07SGordon Ross 	case AF_INET6:
212b3700b07SGordon Ross 		slen = sizeof (struct sockaddr_in6);
213b3700b07SGordon Ross 		break;
214b3700b07SGordon Ross 	default:
215b3700b07SGordon Ross 		return (EAI_FAMILY);
216b3700b07SGordon Ross 	}
217b3700b07SGordon Ross 
218b3700b07SGordon Ross 	eai = getnameinfo(sa, slen, obuf, olen, NULL, 0, NI_NUMERICHOST);
219b3700b07SGordon Ross 
220b3700b07SGordon Ross 	return (eai);
221b3700b07SGordon Ross }
222c8e26105Sjp 
223c8e26105Sjp static void
224c8e26105Sjp update_version(ad_item_t *item, int  num, ad_item_t *param)
225c8e26105Sjp {
226c8e26105Sjp 	item->param_version[num] = param->version;
227c8e26105Sjp }
228c8e26105Sjp 
229c8e26105Sjp 
2304d61c878SJulian Pullen 
2317a8a68f5SJulian Pullen static boolean_t
2324d61c878SJulian Pullen is_valid(ad_item_t *item)
233c8e26105Sjp {
2344d61c878SJulian Pullen 	if (item->value != NULL) {
2354d61c878SJulian Pullen 		if (item->state == AD_STATE_FIXED)
2367a8a68f5SJulian Pullen 			return (B_TRUE);
2374d61c878SJulian Pullen 		if (item->state == AD_STATE_AUTO &&
238c5866007SKeyur Desai 		    (item->expires == 0 || item->expires > time(NULL)))
2397a8a68f5SJulian Pullen 			return (B_TRUE);
2404d61c878SJulian Pullen 	}
2417a8a68f5SJulian Pullen 	return (B_FALSE);
242c8e26105Sjp }
243c8e26105Sjp 
244c8e26105Sjp 
245c8e26105Sjp static void
2464d61c878SJulian Pullen update_item(ad_item_t *item, void *value, enum ad_item_state state,
247c8e26105Sjp 		uint32_t ttl)
248c8e26105Sjp {
2494d61c878SJulian Pullen 	if (item->value != NULL && value != NULL) {
2504d61c878SJulian Pullen 		if ((item->type == AD_STRING &&
2514d61c878SJulian Pullen 		    strcmp(item->value, value) != 0) ||
252b3700b07SGordon Ross 		    (item->type == AD_UUID &&
253b3700b07SGordon Ross 		    ad_disc_compare_uuid(item->value, value) != 0)||
2544d61c878SJulian Pullen 		    (item->type == AD_DIRECTORY &&
2554d61c878SJulian Pullen 		    ad_disc_compare_ds(item->value, value) != 0)||
2564d61c878SJulian Pullen 		    (item->type == AD_DOMAINS_IN_FOREST &&
2574d61c878SJulian Pullen 		    ad_disc_compare_domainsinforest(item->value, value) != 0) ||
2584d61c878SJulian Pullen 		    (item->type == AD_TRUSTED_DOMAINS &&
2594d61c878SJulian Pullen 		    ad_disc_compare_trusteddomains(item->value, value) != 0))
260c8e26105Sjp 			item->version++;
2614d61c878SJulian Pullen 	} else if (item->value != value)
262c8e26105Sjp 		item->version++;
263c8e26105Sjp 
2644d61c878SJulian Pullen 	if (item->value != NULL)
2654d61c878SJulian Pullen 		free(item->value);
266c8e26105Sjp 
2674d61c878SJulian Pullen 	item->value = value;
2684d61c878SJulian Pullen 	item->state = state;
269c8e26105Sjp 
270c8e26105Sjp 	if (ttl == 0)
271c5866007SKeyur Desai 		item->expires = 0;
272c8e26105Sjp 	else
273c5866007SKeyur Desai 		item->expires = time(NULL) + ttl;
274c8e26105Sjp }
275c8e26105Sjp 
276b3700b07SGordon Ross /* Compare UUIDs */
277b3700b07SGordon Ross int
278b3700b07SGordon Ross ad_disc_compare_uuid(uuid_t *u1, uuid_t *u2)
279b3700b07SGordon Ross {
280b3700b07SGordon Ross 	int rc;
281b3700b07SGordon Ross 
282b3700b07SGordon Ross 	rc = memcmp(u1, u2, UUID_LEN);
283b3700b07SGordon Ross 	return (rc);
284b3700b07SGordon Ross }
285b3700b07SGordon Ross 
286b3700b07SGordon Ross void *
287b3700b07SGordon Ross uuid_dup(void *src)
288b3700b07SGordon Ross {
289b3700b07SGordon Ross 	void *dst;
290b3700b07SGordon Ross 	dst = malloc(UUID_LEN);
291b3700b07SGordon Ross 	if (dst != NULL)
292b3700b07SGordon Ross 		(void) memcpy(dst, src, UUID_LEN);
293b3700b07SGordon Ross 	return (dst);
294b3700b07SGordon Ross }
295c8e26105Sjp 
2964d61c878SJulian Pullen /* Compare DS lists */
2974d61c878SJulian Pullen int
298b3700b07SGordon Ross ad_disc_compare_ds(ad_disc_ds_t *ds1, ad_disc_ds_t *ds2)
299c8e26105Sjp {
3007a8a68f5SJulian Pullen 	int		i, j;
3017a8a68f5SJulian Pullen 	int		num_ds1;
3027a8a68f5SJulian Pullen 	int		num_ds2;
3037a8a68f5SJulian Pullen 	boolean_t	match;
304c8e26105Sjp 
3054d61c878SJulian Pullen 	for (i = 0; ds1[i].host[0] != '\0'; i++)
3064d61c878SJulian Pullen 		continue;
3074d61c878SJulian Pullen 	num_ds1 = i;
3084d61c878SJulian Pullen 	for (j = 0; ds2[j].host[0] != '\0'; j++)
3094d61c878SJulian Pullen 		continue;
3104d61c878SJulian Pullen 	num_ds2 = j;
3114d61c878SJulian Pullen 	if (num_ds1 != num_ds2)
3124d61c878SJulian Pullen 		return (1);
313c8e26105Sjp 
3144d61c878SJulian Pullen 	for (i = 0; i < num_ds1; i++) {
3157a8a68f5SJulian Pullen 		match = B_FALSE;
3164d61c878SJulian Pullen 		for (j = 0; j < num_ds2; j++) {
317928e1f97SJordan Brown 			if (strcmp(ds1[i].host, ds2[j].host) == 0 &&
318928e1f97SJordan Brown 			    ds1[i].port == ds2[j].port) {
3197a8a68f5SJulian Pullen 				match = B_TRUE;
3204d61c878SJulian Pullen 				break;
3214d61c878SJulian Pullen 			}
3224d61c878SJulian Pullen 		}
3234d61c878SJulian Pullen 		if (!match)
3244d61c878SJulian Pullen 			return (1);
3254d61c878SJulian Pullen 	}
3264d61c878SJulian Pullen 	return (0);
3274d61c878SJulian Pullen }
328c8e26105Sjp 
3294d61c878SJulian Pullen 
3304d61c878SJulian Pullen /* Copy a list of DSs */
331b3700b07SGordon Ross static ad_disc_ds_t *
332b3700b07SGordon Ross ds_dup(const ad_disc_ds_t *srv)
3334d61c878SJulian Pullen {
3344d61c878SJulian Pullen 	int	i;
3354d61c878SJulian Pullen 	int	size;
336b3700b07SGordon Ross 	ad_disc_ds_t *new = NULL;
3374d61c878SJulian Pullen 
3384d61c878SJulian Pullen 	for (i = 0; srv[i].host[0] != '\0'; i++)
3394d61c878SJulian Pullen 		continue;
3404d61c878SJulian Pullen 
341b3700b07SGordon Ross 	size = (i + 1) * sizeof (ad_disc_ds_t);
3424d61c878SJulian Pullen 	new = malloc(size);
3434d61c878SJulian Pullen 	if (new != NULL)
344148c5f43SAlan Wright 		(void) memcpy(new, srv, size);
3454d61c878SJulian Pullen 	return (new);
346c8e26105Sjp }
347c8e26105Sjp 
348c8e26105Sjp 
3494d61c878SJulian Pullen int
3504d61c878SJulian Pullen ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1,
3514d61c878SJulian Pullen 			ad_disc_trusteddomains_t *td2)
352c8e26105Sjp {
3537a8a68f5SJulian Pullen 	int		i, j;
3547a8a68f5SJulian Pullen 	int		num_td1;
3557a8a68f5SJulian Pullen 	int		num_td2;
3567a8a68f5SJulian Pullen 	boolean_t	match;
357c8e26105Sjp 
3584d61c878SJulian Pullen 	for (i = 0; td1[i].domain[0] != '\0'; i++)
3594d61c878SJulian Pullen 		continue;
3604d61c878SJulian Pullen 	num_td1 = i;
361c8e26105Sjp 
3624d61c878SJulian Pullen 	for (j = 0; td2[j].domain[0] != '\0'; j++)
3634d61c878SJulian Pullen 		continue;
3644d61c878SJulian Pullen 	num_td2 = j;
3654d61c878SJulian Pullen 
3664d61c878SJulian Pullen 	if (num_td1 != num_td2)
3674d61c878SJulian Pullen 		return (1);
3684d61c878SJulian Pullen 
3694d61c878SJulian Pullen 	for (i = 0; i < num_td1; i++) {
3707a8a68f5SJulian Pullen 		match = B_FALSE;
3714d61c878SJulian Pullen 		for (j = 0; j < num_td2; j++) {
3721fcced4cSJordan Brown 			if (domain_eq(td1[i].domain, td2[j].domain)) {
3737a8a68f5SJulian Pullen 				match = B_TRUE;
3744d61c878SJulian Pullen 				break;
3754d61c878SJulian Pullen 			}
3764d61c878SJulian Pullen 		}
3774d61c878SJulian Pullen 		if (!match)
3784d61c878SJulian Pullen 			return (1);
3794d61c878SJulian Pullen 	}
3804d61c878SJulian Pullen 	return (0);
3814d61c878SJulian Pullen }
3824d61c878SJulian Pullen 
3834d61c878SJulian Pullen 
3844d61c878SJulian Pullen 
3854d61c878SJulian Pullen /* Copy a list of Trusted Domains */
3864d61c878SJulian Pullen static ad_disc_trusteddomains_t *
3874d61c878SJulian Pullen td_dup(const ad_disc_trusteddomains_t *td)
3884d61c878SJulian Pullen {
3894d61c878SJulian Pullen 	int	i;
3904d61c878SJulian Pullen 	int	size;
3914d61c878SJulian Pullen 	ad_disc_trusteddomains_t *new = NULL;
3924d61c878SJulian Pullen 
3934d61c878SJulian Pullen 	for (i = 0; td[i].domain[0] != '\0'; i++)
3944d61c878SJulian Pullen 		continue;
3954d61c878SJulian Pullen 
3964d61c878SJulian Pullen 	size = (i + 1) * sizeof (ad_disc_trusteddomains_t);
3974d61c878SJulian Pullen 	new = malloc(size);
3984d61c878SJulian Pullen 	if (new != NULL)
399148c5f43SAlan Wright 		(void) memcpy(new, td, size);
4004d61c878SJulian Pullen 	return (new);
4014d61c878SJulian Pullen }
4024d61c878SJulian Pullen 
4034d61c878SJulian Pullen 
4044d61c878SJulian Pullen 
4054d61c878SJulian Pullen int
4064d61c878SJulian Pullen ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *df1,
4074d61c878SJulian Pullen 			ad_disc_domainsinforest_t *df2)
4084d61c878SJulian Pullen {
4097a8a68f5SJulian Pullen 	int		i, j;
4107a8a68f5SJulian Pullen 	int		num_df1;
4117a8a68f5SJulian Pullen 	int		num_df2;
4127a8a68f5SJulian Pullen 	boolean_t	match;
4134d61c878SJulian Pullen 
4144d61c878SJulian Pullen 	for (i = 0; df1[i].domain[0] != '\0'; i++)
4154d61c878SJulian Pullen 		continue;
4164d61c878SJulian Pullen 	num_df1 = i;
4174d61c878SJulian Pullen 
4184d61c878SJulian Pullen 	for (j = 0; df2[j].domain[0] != '\0'; j++)
4194d61c878SJulian Pullen 		continue;
4204d61c878SJulian Pullen 	num_df2 = j;
4214d61c878SJulian Pullen 
4224d61c878SJulian Pullen 	if (num_df1 != num_df2)
4234d61c878SJulian Pullen 		return (1);
4244d61c878SJulian Pullen 
4254d61c878SJulian Pullen 	for (i = 0; i < num_df1; i++) {
4267a8a68f5SJulian Pullen 		match = B_FALSE;
4274d61c878SJulian Pullen 		for (j = 0; j < num_df2; j++) {
4281fcced4cSJordan Brown 			if (domain_eq(df1[i].domain, df2[j].domain) &&
429928e1f97SJordan Brown 			    strcmp(df1[i].sid, df2[j].sid) == 0) {
4307a8a68f5SJulian Pullen 				match = B_TRUE;
4314d61c878SJulian Pullen 				break;
4324d61c878SJulian Pullen 			}
4334d61c878SJulian Pullen 		}
4344d61c878SJulian Pullen 		if (!match)
4354d61c878SJulian Pullen 			return (1);
4364d61c878SJulian Pullen 	}
4374d61c878SJulian Pullen 	return (0);
4384d61c878SJulian Pullen }
439c8e26105Sjp 
4404d61c878SJulian Pullen 
4414d61c878SJulian Pullen 
4424d61c878SJulian Pullen /* Copy a list of Trusted Domains */
4434d61c878SJulian Pullen static ad_disc_domainsinforest_t *
4444d61c878SJulian Pullen df_dup(const ad_disc_domainsinforest_t *df)
4454d61c878SJulian Pullen {
4464d61c878SJulian Pullen 	int	i;
4474d61c878SJulian Pullen 	int	size;
4484d61c878SJulian Pullen 	ad_disc_domainsinforest_t *new = NULL;
4494d61c878SJulian Pullen 
4504d61c878SJulian Pullen 	for (i = 0; df[i].domain[0] != '\0'; i++)
4514d61c878SJulian Pullen 		continue;
4524d61c878SJulian Pullen 
4534d61c878SJulian Pullen 	size = (i + 1) * sizeof (ad_disc_domainsinforest_t);
4544d61c878SJulian Pullen 	new = malloc(size);
4554d61c878SJulian Pullen 	if (new != NULL)
456148c5f43SAlan Wright 		(void) memcpy(new, df, size);
4574d61c878SJulian Pullen 	return (new);
458c8e26105Sjp }
459c8e26105Sjp 
460c8e26105Sjp 
461c8e26105Sjp 
4624d61c878SJulian Pullen 
4634d61c878SJulian Pullen 
464c8e26105Sjp /*
465c8e26105Sjp  * Returns an array of IPv4 address/prefix length
466c8e26105Sjp  * The last subnet is NULL
467c8e26105Sjp  */
468c8e26105Sjp static ad_subnet_t *
469c8e26105Sjp find_subnets()
470c8e26105Sjp {
471c8e26105Sjp 	int		sock, n, i;
472c8e26105Sjp 	struct lifconf	lifc;
473c8e26105Sjp 	struct lifreq	lifr, *lifrp;
474c8e26105Sjp 	struct lifnum	lifn;
475c8e26105Sjp 	uint32_t	prefix_len;
476c8e26105Sjp 	char		*s;
477c8e26105Sjp 	ad_subnet_t	*results;
478c8e26105Sjp 
479c8e26105Sjp 	lifrp = &lifr;
480c8e26105Sjp 
481c8e26105Sjp 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
4827a8a68f5SJulian Pullen 		logger(LOG_ERR, "Failed to open IPv4 socket for "
48371590c90Snw 		    "listing network interfaces (%s)", strerror(errno));
484c8e26105Sjp 		return (NULL);
485c8e26105Sjp 	}
486c8e26105Sjp 
487c8e26105Sjp 	lifn.lifn_family = AF_INET;
488c8e26105Sjp 	lifn.lifn_flags = 0;
489c8e26105Sjp 	if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
4907a8a68f5SJulian Pullen 		logger(LOG_ERR,
49171590c90Snw 		    "Failed to find the number of network interfaces (%s)",
49271590c90Snw 		    strerror(errno));
493148c5f43SAlan Wright 		(void) close(sock);
494c8e26105Sjp 		return (NULL);
495c8e26105Sjp 	}
496c8e26105Sjp 
497c8e26105Sjp 	if (lifn.lifn_count < 1) {
4987a8a68f5SJulian Pullen 		logger(LOG_ERR, "No IPv4 network interfaces found");
499148c5f43SAlan Wright 		(void) close(sock);
500c8e26105Sjp 		return (NULL);
501c8e26105Sjp 	}
502c8e26105Sjp 
503c8e26105Sjp 	lifc.lifc_family = AF_INET;
504c8e26105Sjp 	lifc.lifc_flags = 0;
505c8e26105Sjp 	lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
506c8e26105Sjp 	lifc.lifc_buf = malloc(lifc.lifc_len);
507c8e26105Sjp 
508c8e26105Sjp 	if (lifc.lifc_buf == NULL) {
5097a8a68f5SJulian Pullen 		logger(LOG_ERR, "Out of memory");
510148c5f43SAlan Wright 		(void) close(sock);
511c8e26105Sjp 		return (NULL);
512c8e26105Sjp 	}
513c8e26105Sjp 
514c8e26105Sjp 	if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) {
5157a8a68f5SJulian Pullen 		logger(LOG_ERR, "Failed to list network interfaces (%s)",
51671590c90Snw 		    strerror(errno));
517c8e26105Sjp 		free(lifc.lifc_buf);
518148c5f43SAlan Wright 		(void) close(sock);
519c8e26105Sjp 		return (NULL);
520c8e26105Sjp 	}
521c8e26105Sjp 
522c8e26105Sjp 	n = lifc.lifc_len / (int)sizeof (struct lifreq);
523c8e26105Sjp 
524c8e26105Sjp 	if ((results = calloc(n + 1, sizeof (ad_subnet_t))) == NULL) {
525c8e26105Sjp 		free(lifc.lifc_buf);
526148c5f43SAlan Wright 		(void) close(sock);
527c8e26105Sjp 		return (NULL);
528c8e26105Sjp 	}
529c8e26105Sjp 
530c8e26105Sjp 	for (i = 0, lifrp = lifc.lifc_req; i < n; i++, lifrp++) {
531c8e26105Sjp 		if (ioctl(sock, SIOCGLIFFLAGS, lifrp) < 0)
532c8e26105Sjp 			continue;
533c8e26105Sjp 
534c8e26105Sjp 		if ((lifrp->lifr_flags & IFF_UP) == 0)
535c8e26105Sjp 			continue;
536c8e26105Sjp 
537c8e26105Sjp 		if (ioctl(sock, SIOCGLIFSUBNET, lifrp) < 0)
538c8e26105Sjp 			continue;
539c8e26105Sjp 
540c8e26105Sjp 		prefix_len = lifrp->lifr_addrlen;
541c8e26105Sjp 
542c8e26105Sjp 		s = inet_ntoa(((struct sockaddr_in *)
543c8e26105Sjp 		    &lifrp->lifr_addr)->sin_addr);
544c8e26105Sjp 
545c8e26105Sjp 		(void) snprintf(results[i].subnet, sizeof (ad_subnet_t),
546c8e26105Sjp 		    "%s/%d", s, prefix_len);
547c8e26105Sjp 	}
548c8e26105Sjp 
549c8e26105Sjp 	free(lifc.lifc_buf);
550148c5f43SAlan Wright 	(void) close(sock);
551c8e26105Sjp 
552c8e26105Sjp 	return (results);
553c8e26105Sjp }
554c8e26105Sjp 
555c8e26105Sjp static int
556c8e26105Sjp cmpsubnets(ad_subnet_t *subnets1, ad_subnet_t *subnets2)
557c8e26105Sjp {
558c8e26105Sjp 	int num_subnets1;
559c8e26105Sjp 	int num_subnets2;
5607a8a68f5SJulian Pullen 	boolean_t matched;
561c8e26105Sjp 	int i, j;
562c8e26105Sjp 
563c8e26105Sjp 	for (i = 0; subnets1[i].subnet[0] != '\0'; i++)
5644d61c878SJulian Pullen 		continue;
565c8e26105Sjp 	num_subnets1 = i;
566c8e26105Sjp 
567c8e26105Sjp 	for (i = 0; subnets2[i].subnet[0] != '\0'; i++)
5684d61c878SJulian Pullen 		continue;
569c8e26105Sjp 	num_subnets2 = i;
570c8e26105Sjp 
571c8e26105Sjp 	if (num_subnets1 != num_subnets2)
572c8e26105Sjp 		return (1);
573c8e26105Sjp 
574c8e26105Sjp 	for (i = 0;  i < num_subnets1; i++) {
5757a8a68f5SJulian Pullen 		matched = B_FALSE;
576c8e26105Sjp 		for (j = 0; j < num_subnets2; j++) {
577c8e26105Sjp 			if (strcmp(subnets1[i].subnet,
578c8e26105Sjp 			    subnets2[j].subnet) == 0) {
5797a8a68f5SJulian Pullen 				matched = B_TRUE;
580c8e26105Sjp 				break;
581c8e26105Sjp 			}
582c8e26105Sjp 		}
583c8e26105Sjp 		if (!matched)
584c8e26105Sjp 			return (1);
585c8e26105Sjp 	}
586c8e26105Sjp 	return (0);
587c8e26105Sjp }
588c8e26105Sjp 
589c8e26105Sjp 
590c8e26105Sjp 
591c8e26105Sjp 
592c8e26105Sjp /* Convert a DN's DC components into a DNS domainname */
5937a8a68f5SJulian Pullen char *
594c8e26105Sjp DN_to_DNS(const char *dn_name)
595c8e26105Sjp {
596c8e26105Sjp 	char	dns[DNS_MAX_NAME];
597c8e26105Sjp 	char	*dns_name;
598c8e26105Sjp 	int	i, j;
599c8e26105Sjp 	int	num = 0;
600c8e26105Sjp 
601c8e26105Sjp 	j = 0;
602c8e26105Sjp 	i = 0;
603c8e26105Sjp 
6044d61c878SJulian Pullen 	if (dn_name == NULL)
6054d61c878SJulian Pullen 		return (NULL);
606c8e26105Sjp 	/*
607c8e26105Sjp 	 * Find all DC=<value> and form DNS name of the
608c8e26105Sjp 	 * form <value1>.<value2>...
609c8e26105Sjp 	 */
6104d61c878SJulian Pullen 	while (dn_name[i] != '\0') {
611c8e26105Sjp 		if (strncasecmp(&dn_name[i], "DC=", 3) == 0) {
612c8e26105Sjp 			i += 3;
6134d61c878SJulian Pullen 			if (dn_name[i] != '\0' && num > 0)
614c8e26105Sjp 				dns[j++] = '.';
6154d61c878SJulian Pullen 			while (dn_name[i] != '\0' &&
616c8e26105Sjp 			    dn_name[i] != ',' && dn_name[i] != '+')
617c8e26105Sjp 				dns[j++] = dn_name[i++];
618c8e26105Sjp 			num++;
619c8e26105Sjp 		} else {
620c8e26105Sjp 			/* Skip attr=value as it is not DC= */
6214d61c878SJulian Pullen 			while (dn_name[i] != '\0' &&
622c8e26105Sjp 			    dn_name[i] != ',' && dn_name[i] != '+')
623c8e26105Sjp 				i++;
624c8e26105Sjp 		}
625c8e26105Sjp 		/* Skip over separator ','  or '+' */
6264d61c878SJulian Pullen 		if (dn_name[i] != '\0') i++;
627c8e26105Sjp 	}
628c8e26105Sjp 	dns[j] = '\0';
629c8e26105Sjp 	dns_name = malloc(j + 1);
630c8e26105Sjp 	if (dns_name != NULL)
631c8e26105Sjp 		(void) strlcpy(dns_name, dns, j + 1);
632c8e26105Sjp 	return (dns_name);
633c8e26105Sjp }
634c8e26105Sjp 
635c8e26105Sjp 
6364d61c878SJulian Pullen /*
6374d61c878SJulian Pullen  * A utility function to bind to a Directory server
6384d61c878SJulian Pullen  */
6394d61c878SJulian Pullen 
640c5866007SKeyur Desai static
641c5866007SKeyur Desai LDAP *
642b3700b07SGordon Ross ldap_lookup_init(ad_disc_ds_t *ds)
6434d61c878SJulian Pullen {
644*d0bed8f2SGordon Ross 	int	i;
6454d61c878SJulian Pullen 	int	rc, ldversion;
6464d61c878SJulian Pullen 	int	zero = 0;
647*d0bed8f2SGordon Ross 	int	timeoutms = 5 * 1000;
648*d0bed8f2SGordon Ross 	char	*saslmech = "GSSAPI";
6494d61c878SJulian Pullen 	uint32_t saslflags = LDAP_SASL_INTERACTIVE;
650*d0bed8f2SGordon Ross 	LDAP	*ld = NULL;
6514d61c878SJulian Pullen 
6524d61c878SJulian Pullen 	for (i = 0; ds[i].host[0] != '\0'; i++) {
653b3700b07SGordon Ross 		if (DBG(LDAP, 2)) {
654b3700b07SGordon Ross 			logger(LOG_DEBUG, "adutils: ldap_lookup_init, host %s",
655b3700b07SGordon Ross 			    ds[i].host);
656b3700b07SGordon Ross 		}
657b3700b07SGordon Ross 
6584d61c878SJulian Pullen 		ld = ldap_init(ds[i].host, ds[i].port);
6594d61c878SJulian Pullen 		if (ld == NULL) {
660148c5f43SAlan Wright 			if (DBG(LDAP, 1)) {
661148c5f43SAlan Wright 				logger(LOG_DEBUG,
662148c5f43SAlan Wright 				    "Couldn't connect to AD DC %s:%d (%s)",
663148c5f43SAlan Wright 				    ds[i].host, ds[i].port,
664148c5f43SAlan Wright 				    strerror(errno));
665148c5f43SAlan Wright 			}
6664d61c878SJulian Pullen 			continue;
6674d61c878SJulian Pullen 		}
6684d61c878SJulian Pullen 
6694d61c878SJulian Pullen 		ldversion = LDAP_VERSION3;
6704d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
6714d61c878SJulian Pullen 		    &ldversion);
6724d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_REFERRALS,
6734d61c878SJulian Pullen 		    LDAP_OPT_OFF);
6744d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
6754d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
6764d61c878SJulian Pullen 		/* setup TCP/IP connect timeout */
6774d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
6784d61c878SJulian Pullen 		    &timeoutms);
6794d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_RESTART,
6804d61c878SJulian Pullen 		    LDAP_OPT_ON);
6814d61c878SJulian Pullen 
682bd428526SJulian Pullen 		rc = adutils_set_thread_functions(ld);
683bd428526SJulian Pullen 		if (rc != LDAP_SUCCESS) {
684bd428526SJulian Pullen 			/* Error has already been logged */
685bd428526SJulian Pullen 			(void) ldap_unbind(ld);
686bd428526SJulian Pullen 			ld = NULL;
687bd428526SJulian Pullen 			continue;
688bd428526SJulian Pullen 		}
689bd428526SJulian Pullen 
6904d61c878SJulian Pullen 		rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */,
6914d61c878SJulian Pullen 		    saslmech, NULL, NULL, saslflags, &saslcallback,
6924d61c878SJulian Pullen 		    NULL /* defaults */);
6934d61c878SJulian Pullen 		if (rc == LDAP_SUCCESS)
6944d61c878SJulian Pullen 			break;
6954d61c878SJulian Pullen 
696148c5f43SAlan Wright 		if (DBG(LDAP, 0)) {
697148c5f43SAlan Wright 			logger(LOG_INFO, "LDAP: %s:%d: %s",
698148c5f43SAlan Wright 			    ds[i].host, ds[i].port, ldap_err2string(rc));
699148c5f43SAlan Wright 			ldap_perror(ld, ds[i].host);
700148c5f43SAlan Wright 		}
7014d61c878SJulian Pullen 		(void) ldap_unbind(ld);
7024d61c878SJulian Pullen 		ld = NULL;
7034d61c878SJulian Pullen 	}
7044d61c878SJulian Pullen 	return (ld);
7054d61c878SJulian Pullen }
7064d61c878SJulian Pullen 
707c8e26105Sjp 
7087a8a68f5SJulian Pullen 
7094d61c878SJulian Pullen /*
7104d61c878SJulian Pullen  * Lookup the trusted domains in the global catalog.
7114d61c878SJulian Pullen  *
7124d61c878SJulian Pullen  * Returns:
7134d61c878SJulian Pullen  *	array of trusted domains which is terminated by
7144d61c878SJulian Pullen  *		an empty trusted domain.
7154d61c878SJulian Pullen  *	NULL an error occured
7164d61c878SJulian Pullen  */
7174d61c878SJulian Pullen ad_disc_trusteddomains_t *
718b3700b07SGordon Ross ldap_lookup_trusted_domains(LDAP **ld, ad_disc_ds_t *globalCatalog,
7194d61c878SJulian Pullen 			char *base_dn)
7204d61c878SJulian Pullen {
7214d61c878SJulian Pullen 	int		scope = LDAP_SCOPE_SUBTREE;
7224d61c878SJulian Pullen 	char		*attrs[3];
7234d61c878SJulian Pullen 	int		rc;
7244d61c878SJulian Pullen 	LDAPMessage	*results = NULL;
7254d61c878SJulian Pullen 	LDAPMessage	*entry;
7264d61c878SJulian Pullen 	char		*filter;
7274d61c878SJulian Pullen 	char		**partner = NULL;
7284d61c878SJulian Pullen 	char		**direction = NULL;
7294d61c878SJulian Pullen 	int		num = 0;
7304d61c878SJulian Pullen 	ad_disc_trusteddomains_t *trusted_domains = NULL;
7314d61c878SJulian Pullen 
732148c5f43SAlan Wright 	if (DBG(DISC, 1))
733148c5f43SAlan Wright 		logger(LOG_DEBUG, "Looking for trusted domains...");
7344d61c878SJulian Pullen 
7354d61c878SJulian Pullen 	if (*ld == NULL)
7364d61c878SJulian Pullen 		*ld = ldap_lookup_init(globalCatalog);
7374d61c878SJulian Pullen 
738b3700b07SGordon Ross 	if (*ld == NULL) {
739b3700b07SGordon Ross 		logger(LOG_ERR, "adutils: ldap_lookup_init failed");
7404d61c878SJulian Pullen 		return (NULL);
741b3700b07SGordon Ross 	}
7424d61c878SJulian Pullen 
7434d61c878SJulian Pullen 	attrs[0] = "trustPartner";
7444d61c878SJulian Pullen 	attrs[1] = "trustDirection";
7454d61c878SJulian Pullen 	attrs[2] = NULL;
7464d61c878SJulian Pullen 
747148c5f43SAlan Wright 	/*
748148c5f43SAlan Wright 	 * Trust direction values:
749148c5f43SAlan Wright 	 * 1 - inbound (they trust us)
750148c5f43SAlan Wright 	 * 2 - outbound (we trust them)
751148c5f43SAlan Wright 	 * 3 - bidirectional (we trust each other)
752148c5f43SAlan Wright 	 */
7534d61c878SJulian Pullen 	filter = "(&(objectclass=trustedDomain)"
754148c5f43SAlan Wright 	    "(|(trustDirection=3)(trustDirection=2)))";
7554d61c878SJulian Pullen 
7564d61c878SJulian Pullen 	rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results);
757148c5f43SAlan Wright 	if (DBG(DISC, 1))
758148c5f43SAlan Wright 		logger(LOG_DEBUG, "Trusted domains:");
7594d61c878SJulian Pullen 	if (rc == LDAP_SUCCESS) {
7604d61c878SJulian Pullen 		for (entry = ldap_first_entry(*ld, results);
7614d61c878SJulian Pullen 		    entry != NULL; entry = ldap_next_entry(*ld, entry)) {
7624d61c878SJulian Pullen 			partner = ldap_get_values(*ld, entry, "trustPartner");
7634d61c878SJulian Pullen 			direction = ldap_get_values(
7644d61c878SJulian Pullen 			    *ld, entry, "trustDirection");
7654d61c878SJulian Pullen 
7664d61c878SJulian Pullen 			if (partner != NULL && direction != NULL) {
767148c5f43SAlan Wright 				if (DBG(DISC, 1)) {
768148c5f43SAlan Wright 					logger(LOG_DEBUG, "    %s (%s)",
769148c5f43SAlan Wright 					    partner[0], direction[0]);
770148c5f43SAlan Wright 				}
7714d61c878SJulian Pullen 				num++;
772c5866007SKeyur Desai 				void *tmp = realloc(trusted_domains,
7734d61c878SJulian Pullen 				    (num + 1) *
7744d61c878SJulian Pullen 				    sizeof (ad_disc_trusteddomains_t));
775c5866007SKeyur Desai 				if (tmp == NULL) {
776c5866007SKeyur Desai 					free(trusted_domains);
7774d61c878SJulian Pullen 					ldap_value_free(partner);
7784d61c878SJulian Pullen 					ldap_value_free(direction);
779148c5f43SAlan Wright 					(void) ldap_msgfree(results);
7804d61c878SJulian Pullen 					return (NULL);
7814d61c878SJulian Pullen 				}
782c5866007SKeyur Desai 				trusted_domains = tmp;
7834d61c878SJulian Pullen 				/* Last element should be zero */
784148c5f43SAlan Wright 				(void) memset(&trusted_domains[num], 0,
7854d61c878SJulian Pullen 				    sizeof (ad_disc_trusteddomains_t));
786148c5f43SAlan Wright 				(void) strcpy(trusted_domains[num - 1].domain,
7874d61c878SJulian Pullen 				    partner[0]);
7884d61c878SJulian Pullen 				trusted_domains[num - 1].direction =
7894d61c878SJulian Pullen 				    atoi(direction[0]);
7904d61c878SJulian Pullen 			}
7914d61c878SJulian Pullen 			if (partner != NULL)
7924d61c878SJulian Pullen 				ldap_value_free(partner);
7934d61c878SJulian Pullen 			if (direction != NULL)
7944d61c878SJulian Pullen 				ldap_value_free(direction);
7954d61c878SJulian Pullen 		}
7964d61c878SJulian Pullen 	} else if (rc == LDAP_NO_RESULTS_RETURNED) {
7974d61c878SJulian Pullen 		/* This is not an error - return empty trusted domain */
7984d61c878SJulian Pullen 		trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t));
799148c5f43SAlan Wright 		if (DBG(DISC, 1))
800148c5f43SAlan Wright 			logger(LOG_DEBUG, "    not found");
801b3700b07SGordon Ross 	} else {
802b3700b07SGordon Ross 		if (DBG(DISC, 1))
803b3700b07SGordon Ross 			logger(LOG_DEBUG, "    rc=%d", rc);
8044d61c878SJulian Pullen 	}
8054d61c878SJulian Pullen 	if (results != NULL)
806148c5f43SAlan Wright 		(void) ldap_msgfree(results);
8074d61c878SJulian Pullen 
8084d61c878SJulian Pullen 	return (trusted_domains);
8094d61c878SJulian Pullen }
8104d61c878SJulian Pullen 
8114d61c878SJulian Pullen 
8124d61c878SJulian Pullen /*
8134d61c878SJulian Pullen  * This functions finds all the domains in a forest.
8144d61c878SJulian Pullen  */
8154d61c878SJulian Pullen ad_disc_domainsinforest_t *
816b3700b07SGordon Ross ldap_lookup_domains_in_forest(LDAP **ld, ad_disc_ds_t *globalCatalogs)
8174d61c878SJulian Pullen {
818928e1f97SJordan Brown 	static char	*attrs[] = {
819928e1f97SJordan Brown 		"objectSid",
820928e1f97SJordan Brown 		NULL,
821928e1f97SJordan Brown 	};
8224d61c878SJulian Pullen 	int		rc;
8234d61c878SJulian Pullen 	LDAPMessage	*result = NULL;
8244d61c878SJulian Pullen 	LDAPMessage	*entry;
825928e1f97SJordan Brown 	int		ndomains = 0;
826928e1f97SJordan Brown 	int		nresults;
8274d61c878SJulian Pullen 	ad_disc_domainsinforest_t *domains = NULL;
8284d61c878SJulian Pullen 
829b3700b07SGordon Ross 	if (DBG(DISC, 1))
830148c5f43SAlan Wright 		logger(LOG_DEBUG, "Looking for domains in forest...");
831148c5f43SAlan Wright 
8324d61c878SJulian Pullen 	if (*ld == NULL)
8334d61c878SJulian Pullen 		*ld = ldap_lookup_init(globalCatalogs);
8344d61c878SJulian Pullen 
835b3700b07SGordon Ross 	if (*ld == NULL) {
836b3700b07SGordon Ross 		logger(LOG_ERR, "adutils: ldap_lookup_init failed");
8374d61c878SJulian Pullen 		return (NULL);
838b3700b07SGordon Ross 	}
8394d61c878SJulian Pullen 
840928e1f97SJordan Brown 	/* Find domains */
841928e1f97SJordan Brown 	rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE,
842928e1f97SJordan Brown 	    "(objectClass=Domain)", attrs, 0, &result);
843b3700b07SGordon Ross 	if (rc != LDAP_SUCCESS) {
844b3700b07SGordon Ross 		logger(LOG_ERR, "adutils: ldap_search, rc=%d", rc);
845b3700b07SGordon Ross 		goto err;
846b3700b07SGordon Ross 	}
847148c5f43SAlan Wright 	if (DBG(DISC, 1))
848148c5f43SAlan Wright 		logger(LOG_DEBUG, "Domains in forest:");
849928e1f97SJordan Brown 
850928e1f97SJordan Brown 	nresults = ldap_count_entries(*ld, result);
851928e1f97SJordan Brown 	domains = calloc(nresults + 1, sizeof (*domains));
852b3700b07SGordon Ross 	if (domains == NULL) {
853b3700b07SGordon Ross 		if (DBG(DISC, 1))
854b3700b07SGordon Ross 			logger(LOG_DEBUG, "    (nomem)");
855928e1f97SJordan Brown 		goto err;
856b3700b07SGordon Ross 	}
857928e1f97SJordan Brown 
858928e1f97SJordan Brown 	for (entry = ldap_first_entry(*ld, result);
859928e1f97SJordan Brown 	    entry != NULL;
860928e1f97SJordan Brown 	    entry = ldap_next_entry(*ld, entry)) {
861928e1f97SJordan Brown 		struct berval	**sid_ber;
862928e1f97SJordan Brown 		adutils_sid_t	sid;
863928e1f97SJordan Brown 		char		*sid_str;
864*d0bed8f2SGordon Ross 		char		*name;
865bbf6f00cSJordan Brown 		char		*dn;
866928e1f97SJordan Brown 
867928e1f97SJordan Brown 		sid_ber = ldap_get_values_len(*ld, entry,
868928e1f97SJordan Brown 		    "objectSid");
869928e1f97SJordan Brown 		if (sid_ber == NULL)
870928e1f97SJordan Brown 			continue;
8714d61c878SJulian Pullen 
872928e1f97SJordan Brown 		rc = adutils_getsid(sid_ber[0], &sid);
873928e1f97SJordan Brown 		ldap_value_free_len(sid_ber);
874928e1f97SJordan Brown 		if (rc < 0)
875928e1f97SJordan Brown 			goto err;
8764d61c878SJulian Pullen 
877928e1f97SJordan Brown 		if ((sid_str = adutils_sid2txt(&sid)) == NULL)
878928e1f97SJordan Brown 			goto err;
8794d61c878SJulian Pullen 
880148c5f43SAlan Wright 		(void) strcpy(domains[ndomains].sid, sid_str);
881928e1f97SJordan Brown 		free(sid_str);
882928e1f97SJordan Brown 
883bbf6f00cSJordan Brown 		dn = ldap_get_dn(*ld, entry);
884bbf6f00cSJordan Brown 		name = DN_to_DNS(dn);
885bbf6f00cSJordan Brown 		free(dn);
886928e1f97SJordan Brown 		if (name == NULL)
887928e1f97SJordan Brown 			goto err;
888928e1f97SJordan Brown 
889148c5f43SAlan Wright 		(void) strcpy(domains[ndomains].domain, name);
890928e1f97SJordan Brown 		free(name);
891928e1f97SJordan Brown 
892148c5f43SAlan Wright 		if (DBG(DISC, 1))
893148c5f43SAlan Wright 			logger(LOG_DEBUG, "    %s", domains[ndomains].domain);
894928e1f97SJordan Brown 
895928e1f97SJordan Brown 		ndomains++;
8964d61c878SJulian Pullen 	}
8974d61c878SJulian Pullen 
898148c5f43SAlan Wright 	if (ndomains == 0) {
899148c5f43SAlan Wright 		if (DBG(DISC, 1))
900148c5f43SAlan Wright 			logger(LOG_DEBUG, "    not found");
901928e1f97SJordan Brown 		goto err;
902148c5f43SAlan Wright 	}
903928e1f97SJordan Brown 
904928e1f97SJordan Brown 	if (ndomains < nresults) {
905928e1f97SJordan Brown 		ad_disc_domainsinforest_t *tmp;
9061fcced4cSJordan Brown 		tmp = realloc(domains, (ndomains + 1) * sizeof (*domains));
907928e1f97SJordan Brown 		if (tmp == NULL)
908928e1f97SJordan Brown 			goto err;
909928e1f97SJordan Brown 		domains = tmp;
9104d61c878SJulian Pullen 	}
911928e1f97SJordan Brown 
912928e1f97SJordan Brown 	if (result != NULL)
913148c5f43SAlan Wright 		(void) ldap_msgfree(result);
9144d61c878SJulian Pullen 
9154d61c878SJulian Pullen 	return (domains);
916928e1f97SJordan Brown 
917928e1f97SJordan Brown err:
918928e1f97SJordan Brown 	free(domains);
919928e1f97SJordan Brown 	if (result != NULL)
920148c5f43SAlan Wright 		(void) ldap_msgfree(result);
921928e1f97SJordan Brown 	return (NULL);
9224d61c878SJulian Pullen }
9234d61c878SJulian Pullen 
924c8e26105Sjp 
925c8e26105Sjp ad_disc_t
926c8e26105Sjp ad_disc_init(void)
927c8e26105Sjp {
928c8e26105Sjp 	struct ad_disc *ctx;
929c8e26105Sjp 	ctx = calloc(1, sizeof (struct ad_disc));
9300dcc7149Snw 	if (ctx != NULL)
9310dcc7149Snw 		DO_RES_NINIT(ctx);
9324d61c878SJulian Pullen 
9334d61c878SJulian Pullen 	ctx->domain_name.type = AD_STRING;
934b3700b07SGordon Ross 	ctx->domain_guid.type = AD_UUID;
9354d61c878SJulian Pullen 	ctx->domain_controller.type = AD_DIRECTORY;
936b3700b07SGordon Ross 	ctx->preferred_dc.type = AD_DIRECTORY;
9374d61c878SJulian Pullen 	ctx->site_name.type = AD_STRING;
9384d61c878SJulian Pullen 	ctx->forest_name.type = AD_STRING;
9394d61c878SJulian Pullen 	ctx->global_catalog.type = AD_DIRECTORY;
9404d61c878SJulian Pullen 	ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST;
9414d61c878SJulian Pullen 	ctx->trusted_domains.type = AD_TRUSTED_DOMAINS;
9424d61c878SJulian Pullen 	/* Site specific versions */
9434d61c878SJulian Pullen 	ctx->site_domain_controller.type = AD_DIRECTORY;
9444d61c878SJulian Pullen 	ctx->site_global_catalog.type = AD_DIRECTORY;
945c8e26105Sjp 	return (ctx);
946c8e26105Sjp }
947c8e26105Sjp 
948c8e26105Sjp void
949c8e26105Sjp ad_disc_fini(ad_disc_t ctx)
950c8e26105Sjp {
951cd37da74Snw 	if (ctx == NULL)
952cd37da74Snw 		return;
953cd37da74Snw 
9540dcc7149Snw 	if (ctx->res_ninitted)
9554d61c878SJulian Pullen 		res_ndestroy(&ctx->res_state);
956c8e26105Sjp 
957c8e26105Sjp 	if (ctx->subnets != NULL)
958c8e26105Sjp 		free(ctx->subnets);
959c8e26105Sjp 
9604d61c878SJulian Pullen 	if (ctx->domain_name.value != NULL)
9614d61c878SJulian Pullen 		free(ctx->domain_name.value);
9624d61c878SJulian Pullen 
963b3700b07SGordon Ross 	if (ctx->domain_guid.value != NULL)
964b3700b07SGordon Ross 		free(ctx->domain_guid.value);
965b3700b07SGordon Ross 
9664d61c878SJulian Pullen 	if (ctx->domain_controller.value != NULL)
9674d61c878SJulian Pullen 		free(ctx->domain_controller.value);
9684d61c878SJulian Pullen 
969b3700b07SGordon Ross 	if (ctx->preferred_dc.value != NULL)
970b3700b07SGordon Ross 		free(ctx->preferred_dc.value);
971b3700b07SGordon Ross 
9724d61c878SJulian Pullen 	if (ctx->site_name.value != NULL)
9734d61c878SJulian Pullen 		free(ctx->site_name.value);
974c8e26105Sjp 
9754d61c878SJulian Pullen 	if (ctx->forest_name.value != NULL)
9764d61c878SJulian Pullen 		free(ctx->forest_name.value);
977c8e26105Sjp 
9784d61c878SJulian Pullen 	if (ctx->global_catalog.value != NULL)
9794d61c878SJulian Pullen 		free(ctx->global_catalog.value);
980c8e26105Sjp 
9814d61c878SJulian Pullen 	if (ctx->domains_in_forest.value != NULL)
9824d61c878SJulian Pullen 		free(ctx->domains_in_forest.value);
983c8e26105Sjp 
9844d61c878SJulian Pullen 	if (ctx->trusted_domains.value != NULL)
9854d61c878SJulian Pullen 		free(ctx->trusted_domains.value);
986c8e26105Sjp 
9874d61c878SJulian Pullen 	/* Site specific versions */
9884d61c878SJulian Pullen 	if (ctx->site_domain_controller.value != NULL)
9894d61c878SJulian Pullen 		free(ctx->site_domain_controller.value);
990c8e26105Sjp 
9914d61c878SJulian Pullen 	if (ctx->site_global_catalog.value != NULL)
9924d61c878SJulian Pullen 		free(ctx->site_global_catalog.value);
993c8e26105Sjp 
994c8e26105Sjp 	free(ctx);
995c8e26105Sjp }
996c8e26105Sjp 
997c8e26105Sjp void
998c8e26105Sjp ad_disc_refresh(ad_disc_t ctx)
999c8e26105Sjp {
1000b3700b07SGordon Ross 	if (ctx->res_ninitted) {
10014d61c878SJulian Pullen 		res_ndestroy(&ctx->res_state);
1002b3700b07SGordon Ross 		ctx->res_ninitted = 0;
1003b3700b07SGordon Ross 	}
10044d61c878SJulian Pullen 	(void) memset(&ctx->res_state, 0, sizeof (ctx->res_state));
1005b3700b07SGordon Ross 	DO_RES_NINIT(ctx);
1006c8e26105Sjp 
10074d61c878SJulian Pullen 	if (ctx->domain_name.state == AD_STATE_AUTO)
10084d61c878SJulian Pullen 		ctx->domain_name.state = AD_STATE_INVALID;
1009c8e26105Sjp 
1010b3700b07SGordon Ross 	if (ctx->domain_guid.state == AD_STATE_AUTO)
1011b3700b07SGordon Ross 		ctx->domain_guid.state = AD_STATE_INVALID;
1012b3700b07SGordon Ross 
10134d61c878SJulian Pullen 	if (ctx->domain_controller.state == AD_STATE_AUTO)
10144d61c878SJulian Pullen 		ctx->domain_controller.state  = AD_STATE_INVALID;
1015c8e26105Sjp 
1016b3700b07SGordon Ross 	if (ctx->preferred_dc.state == AD_STATE_AUTO)
1017b3700b07SGordon Ross 		ctx->preferred_dc.state  = AD_STATE_INVALID;
1018b3700b07SGordon Ross 
10194d61c878SJulian Pullen 	if (ctx->site_name.state == AD_STATE_AUTO)
10204d61c878SJulian Pullen 		ctx->site_name.state = AD_STATE_INVALID;
1021c8e26105Sjp 
10224d61c878SJulian Pullen 	if (ctx->forest_name.state == AD_STATE_AUTO)
10234d61c878SJulian Pullen 		ctx->forest_name.state = AD_STATE_INVALID;
1024c8e26105Sjp 
10254d61c878SJulian Pullen 	if (ctx->global_catalog.state == AD_STATE_AUTO)
10264d61c878SJulian Pullen 		ctx->global_catalog.state = AD_STATE_INVALID;
1027c8e26105Sjp 
10284d61c878SJulian Pullen 	if (ctx->domains_in_forest.state == AD_STATE_AUTO)
10294d61c878SJulian Pullen 		ctx->domains_in_forest.state  = AD_STATE_INVALID;
1030c8e26105Sjp 
10314d61c878SJulian Pullen 	if (ctx->trusted_domains.state == AD_STATE_AUTO)
10324d61c878SJulian Pullen 		ctx->trusted_domains.state  = AD_STATE_INVALID;
10334d61c878SJulian Pullen 
10344d61c878SJulian Pullen 	if (ctx->site_domain_controller.state == AD_STATE_AUTO)
10354d61c878SJulian Pullen 		ctx->site_domain_controller.state  = AD_STATE_INVALID;
10364d61c878SJulian Pullen 
10374d61c878SJulian Pullen 	if (ctx->site_global_catalog.state == AD_STATE_AUTO)
10384d61c878SJulian Pullen 		ctx->site_global_catalog.state = AD_STATE_INVALID;
1039c8e26105Sjp }
1040c8e26105Sjp 
1041c8e26105Sjp 
1042c5866007SKeyur Desai /*
1043c5866007SKeyur Desai  * Called when the discovery cycle is done.  Sets a master TTL
1044c5866007SKeyur Desai  * that will avoid doing new time-based discoveries too soon after
1045c5866007SKeyur Desai  * the last discovery cycle.  Most interesting when the discovery
1046c5866007SKeyur Desai  * cycle failed, because then the TTLs on the individual items will
1047c5866007SKeyur Desai  * not be updated and may go stale.
1048c5866007SKeyur Desai  */
1049c5866007SKeyur Desai void
1050c5866007SKeyur Desai ad_disc_done(ad_disc_t ctx)
1051c5866007SKeyur Desai {
1052c5866007SKeyur Desai 	time_t now = time(NULL);
1053c5866007SKeyur Desai 
1054c5866007SKeyur Desai 	ctx->expires_not_before = now + MINIMUM_TTL;
1055c5866007SKeyur Desai 	ctx->expires_not_after = now + MAXIMUM_TTL;
1056c5866007SKeyur Desai }
1057c5866007SKeyur Desai 
1058b3700b07SGordon Ross static void
1059b3700b07SGordon Ross log_cds(ad_disc_t ctx, ad_disc_cds_t *cds)
1060b3700b07SGordon Ross {
1061b3700b07SGordon Ross 	char buf[INET6_ADDRSTRLEN];
1062b3700b07SGordon Ross 	struct addrinfo *ai;
1063b3700b07SGordon Ross 
1064b3700b07SGordon Ross 	if (!DBG(DISC, 1) && ctx->status_fp == NULL)
1065b3700b07SGordon Ross 		return;
1066b3700b07SGordon Ross 
1067b3700b07SGordon Ross 	DEBUG1STATUS(ctx, "Candidate servers:");
1068b3700b07SGordon Ross 	if (cds->cds_ds.host[0] == '\0') {
1069b3700b07SGordon Ross 		DEBUG1STATUS(ctx, "  (empty list)");
1070b3700b07SGordon Ross 		return;
1071b3700b07SGordon Ross 	}
1072b3700b07SGordon Ross 
1073b3700b07SGordon Ross 	while (cds->cds_ds.host[0] != '\0') {
1074b3700b07SGordon Ross 
1075b3700b07SGordon Ross 		DEBUG1STATUS(ctx, "  %s  p=%d w=%d",
1076b3700b07SGordon Ross 		    cds->cds_ds.host,
1077b3700b07SGordon Ross 		    cds->cds_ds.priority,
1078b3700b07SGordon Ross 		    cds->cds_ds.weight);
1079b3700b07SGordon Ross 
1080b3700b07SGordon Ross 		ai = cds->cds_ai;
1081b3700b07SGordon Ross 		if (ai == NULL) {
1082b3700b07SGordon Ross 			DEBUG1STATUS(ctx, "    (no address)");
1083b3700b07SGordon Ross 		}
1084b3700b07SGordon Ross 		while (ai != NULL) {
1085b3700b07SGordon Ross 			int eai;
1086b3700b07SGordon Ross 
1087b3700b07SGordon Ross 			eai = getnameinfo(ai->ai_addr, ai->ai_addrlen,
1088b3700b07SGordon Ross 			    buf, sizeof (buf), NULL, 0, NI_NUMERICHOST);
1089b3700b07SGordon Ross 			if (eai != 0)
1090b3700b07SGordon Ross 				(void) strlcpy(buf, "?", sizeof (buf));
1091b3700b07SGordon Ross 
1092b3700b07SGordon Ross 			DEBUG1STATUS(ctx, "    %s", buf);
1093b3700b07SGordon Ross 			ai = ai->ai_next;
1094b3700b07SGordon Ross 		}
1095b3700b07SGordon Ross 		cds++;
1096b3700b07SGordon Ross 	}
1097b3700b07SGordon Ross }
1098b3700b07SGordon Ross 
1099b3700b07SGordon Ross static void
1100b3700b07SGordon Ross log_ds(ad_disc_t ctx, ad_disc_ds_t *ds)
1101b3700b07SGordon Ross {
1102b3700b07SGordon Ross 	char buf[INET6_ADDRSTRLEN];
1103b3700b07SGordon Ross 
1104b3700b07SGordon Ross 	if (!DBG(DISC, 1) && ctx->status_fp == NULL)
1105b3700b07SGordon Ross 		return;
1106b3700b07SGordon Ross 
1107b3700b07SGordon Ross 	DEBUG1STATUS(ctx, "Responding servers:");
1108b3700b07SGordon Ross 	if (ds->host[0] == '\0') {
1109b3700b07SGordon Ross 		DEBUG1STATUS(ctx, "  (empty list)");
1110b3700b07SGordon Ross 		return;
1111b3700b07SGordon Ross 	}
1112b3700b07SGordon Ross 
1113b3700b07SGordon Ross 	while (ds->host[0] != '\0') {
1114b3700b07SGordon Ross 
1115b3700b07SGordon Ross 		DEBUG1STATUS(ctx, "  %s", ds->host);
1116b3700b07SGordon Ross 		DO_GETNAMEINFO(buf, sizeof (buf), &ds->addr);
1117b3700b07SGordon Ross 		DEBUG1STATUS(ctx, "    %s", buf);
1118b3700b07SGordon Ross 
1119b3700b07SGordon Ross 		ds++;
1120b3700b07SGordon Ross 	}
1121b3700b07SGordon Ross }
1122c8e26105Sjp 
11230dcc7149Snw /* Discover joined Active Directory domainName */
11244d61c878SJulian Pullen static ad_item_t *
1125c8e26105Sjp validate_DomainName(ad_disc_t ctx)
1126c8e26105Sjp {
1127c8e26105Sjp 	char *dname, *srvname;
1128b3700b07SGordon Ross 	int len, rc;
1129c8e26105Sjp 
11304d61c878SJulian Pullen 	if (is_valid(&ctx->domain_name))
11314d61c878SJulian Pullen 		return (&ctx->domain_name);
1132c8e26105Sjp 
1133c8e26105Sjp 
1134c8e26105Sjp 	/* Try to find our domain by searching for DCs for it */
11350dcc7149Snw 	DO_RES_NINIT(ctx);
1136b3700b07SGordon Ross 	if (DBG(DISC, 1))
1137148c5f43SAlan Wright 		logger(LOG_DEBUG, "Looking for our AD domain name...");
1138b3700b07SGordon Ross 	rc = srv_getdom(&ctx->res_state,
1139b3700b07SGordon Ross 	    LDAP_SRV_HEAD DC_SRV_TAIL, &srvname);
1140c8e26105Sjp 
1141c8e26105Sjp 	/*
1142c8e26105Sjp 	 * If we can't find DCs by via res_nsearch() then there's no
1143c8e26105Sjp 	 * point in trying anything else to discover the AD domain name.
1144c8e26105Sjp 	 */
1145b3700b07SGordon Ross 	if (rc < 0) {
1146148c5f43SAlan Wright 		if (DBG(DISC, 1))
1147148c5f43SAlan Wright 			logger(LOG_DEBUG, "Can't find our domain name.");
11484d61c878SJulian Pullen 		return (NULL);
1149148c5f43SAlan Wright 	}
1150c8e26105Sjp 
1151c8e26105Sjp 	/*
1152c8e26105Sjp 	 * We have the FQDN of the SRV RR name, so now we extract the
1153c8e26105Sjp 	 * domainname suffix from it.
1154c8e26105Sjp 	 */
1155c8e26105Sjp 	dname = strdup(srvname + strlen(LDAP_SRV_HEAD DC_SRV_TAIL) +
1156c8e26105Sjp 	    1 /* for the dot between RR name and domainname */);
1157c8e26105Sjp 
1158c8e26105Sjp 	free(srvname);
1159c8e26105Sjp 
1160c8e26105Sjp 	if (dname == NULL) {
11617a8a68f5SJulian Pullen 		logger(LOG_ERR, "Out of memory");
11624d61c878SJulian Pullen 		return (NULL);
1163c8e26105Sjp 	}
1164c8e26105Sjp 
1165c8e26105Sjp 	/* Eat any trailing dot */
1166928e1f97SJordan Brown 	len = strlen(dname);
11671fcced4cSJordan Brown 	if (len > 0 && dname[len - 1] == '.')
11681fcced4cSJordan Brown 		dname[len - 1] = '\0';
1169c8e26105Sjp 
1170148c5f43SAlan Wright 	if (DBG(DISC, 1))
1171148c5f43SAlan Wright 		logger(LOG_DEBUG, "Our domain name:  %s", dname);
1172b3700b07SGordon Ross 
1173b3700b07SGordon Ross 	/*
1174b3700b07SGordon Ross 	 * There is no "time to live" on the discovered domain,
1175b3700b07SGordon Ross 	 * so passing zero as TTL here, making it non-expiring.
1176b3700b07SGordon Ross 	 * Note that current consumers do not auto-discover the
1177b3700b07SGordon Ross 	 * domain name, though a future installer could.
1178b3700b07SGordon Ross 	 */
1179b3700b07SGordon Ross 	update_item(&ctx->domain_name, dname, AD_STATE_AUTO, 0);
11804d61c878SJulian Pullen 
11814d61c878SJulian Pullen 	return (&ctx->domain_name);
1182c8e26105Sjp }
1183c8e26105Sjp 
1184c8e26105Sjp 
1185c8e26105Sjp char *
11867a8a68f5SJulian Pullen ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered)
1187c8e26105Sjp {
1188c8e26105Sjp 	char *domain_name = NULL;
11894d61c878SJulian Pullen 	ad_item_t *domain_name_item;
1190c8e26105Sjp 
11914d61c878SJulian Pullen 	domain_name_item = validate_DomainName(ctx);
11924d61c878SJulian Pullen 
11934d61c878SJulian Pullen 	if (domain_name_item) {
11944d61c878SJulian Pullen 		domain_name = strdup(domain_name_item->value);
11954d61c878SJulian Pullen 		if (auto_discovered != NULL)
11964d61c878SJulian Pullen 			*auto_discovered =
11974d61c878SJulian Pullen 			    (domain_name_item->state == AD_STATE_AUTO);
11984d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
11997a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
1200c8e26105Sjp 
1201c8e26105Sjp 	return (domain_name);
1202c8e26105Sjp }
1203c8e26105Sjp 
1204c8e26105Sjp 
12050dcc7149Snw /* Discover domain controllers */
12064d61c878SJulian Pullen static ad_item_t *
1207c8e26105Sjp validate_DomainController(ad_disc_t ctx, enum ad_disc_req req)
1208c8e26105Sjp {
1209b3700b07SGordon Ross 	ad_disc_ds_t *dc = NULL;
1210b3700b07SGordon Ross 	ad_disc_cds_t *cdc = NULL;
12117a8a68f5SJulian Pullen 	boolean_t validate_global = B_FALSE;
12127a8a68f5SJulian Pullen 	boolean_t validate_site = B_FALSE;
12134d61c878SJulian Pullen 	ad_item_t *domain_name_item;
1214b3700b07SGordon Ross 	char *domain_name;
12154d61c878SJulian Pullen 	ad_item_t *site_name_item = NULL;
1216b3700b07SGordon Ross 	char *site_name;
1217b3700b07SGordon Ross 	ad_item_t *prefer_dc_item;
1218b3700b07SGordon Ross 	ad_disc_ds_t *prefer_dc = NULL;
1219c8e26105Sjp 
12204d61c878SJulian Pullen 	/* If the values is fixed there will not be a site specific version */
1221c8e26105Sjp 	if (is_fixed(&ctx->domain_controller))
12224d61c878SJulian Pullen 		return (&ctx->domain_controller);
12234d61c878SJulian Pullen 
12244d61c878SJulian Pullen 	domain_name_item = validate_DomainName(ctx);
1225*d0bed8f2SGordon Ross 	if (domain_name_item == NULL) {
1226*d0bed8f2SGordon Ross 		DEBUG1STATUS(ctx, "(no domain name)");
12274d61c878SJulian Pullen 		return (NULL);
1228*d0bed8f2SGordon Ross 	}
1229b3700b07SGordon Ross 	domain_name = (char *)domain_name_item->value;
1230b3700b07SGordon Ross 
1231b3700b07SGordon Ross 	/* Get (optional) preferred DC. */
1232b3700b07SGordon Ross 	prefer_dc_item = validate_PreferredDC(ctx);
1233b3700b07SGordon Ross 	if (prefer_dc_item != NULL)
1234b3700b07SGordon Ross 		prefer_dc = prefer_dc_item->value;
1235c8e26105Sjp 
1236c8e26105Sjp 	if (req == AD_DISC_GLOBAL)
12377a8a68f5SJulian Pullen 		validate_global = B_TRUE;
1238c8e26105Sjp 	else {
1239b3700b07SGordon Ross 		if (is_fixed(&ctx->site_name))
12407a8a68f5SJulian Pullen 			validate_site = B_TRUE;
1241*d0bed8f2SGordon Ross 		if (req == AD_DISC_PREFER_SITE)
12427a8a68f5SJulian Pullen 			validate_global = B_TRUE;
1243c8e26105Sjp 	}
1244c8e26105Sjp 
1245*d0bed8f2SGordon Ross 	/*
1246*d0bed8f2SGordon Ross 	 * If we're trying both site-specific and global,
1247*d0bed8f2SGordon Ross 	 * try the site-specific first, then fall-back.
1248*d0bed8f2SGordon Ross 	 */
1249*d0bed8f2SGordon Ross 	if (validate_site) {
1250*d0bed8f2SGordon Ross 		site_name_item = &ctx->site_name;
1251*d0bed8f2SGordon Ross 		site_name = (char *)site_name_item->value;
1252*d0bed8f2SGordon Ross 
1253*d0bed8f2SGordon Ross 		if (!is_valid(&ctx->site_domain_controller) ||
1254*d0bed8f2SGordon Ross 		    is_changed(&ctx->site_domain_controller, PARAM1,
1255*d0bed8f2SGordon Ross 		    domain_name_item) ||
1256*d0bed8f2SGordon Ross 		    is_changed(&ctx->site_domain_controller, PARAM2,
1257*d0bed8f2SGordon Ross 		    site_name_item)) {
1258*d0bed8f2SGordon Ross 			char rr_name[DNS_MAX_NAME];
1259b3700b07SGordon Ross 
1260c8e26105Sjp 			/*
12610dcc7149Snw 			 * Lookup DNS SRV RR named
1262*d0bed8f2SGordon Ross 			 * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName>
1263c8e26105Sjp 			 */
1264*d0bed8f2SGordon Ross 			DEBUG1STATUS(ctx, "DNS SRV query, dom=%s, site=%s",
1265*d0bed8f2SGordon Ross 			    domain_name, site_name);
1266*d0bed8f2SGordon Ross 			(void) snprintf(rr_name, sizeof (rr_name),
1267*d0bed8f2SGordon Ross 			    LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL,
1268*d0bed8f2SGordon Ross 			    site_name);
12690dcc7149Snw 			DO_RES_NINIT(ctx);
1270*d0bed8f2SGordon Ross 			cdc = srv_query(&ctx->res_state, rr_name,
1271b3700b07SGordon Ross 			    domain_name, prefer_dc);
1272c8e26105Sjp 
1273b3700b07SGordon Ross 			if (cdc == NULL) {
1274b3700b07SGordon Ross 				DEBUG1STATUS(ctx, "(no DNS response)");
1275*d0bed8f2SGordon Ross 				goto try_global;
1276148c5f43SAlan Wright 			}
1277b3700b07SGordon Ross 			log_cds(ctx, cdc);
1278148c5f43SAlan Wright 
1279b3700b07SGordon Ross 			/*
1280b3700b07SGordon Ross 			 * Filter out unresponsive servers, and
1281b3700b07SGordon Ross 			 * save the domain info we get back.
1282b3700b07SGordon Ross 			 */
1283b3700b07SGordon Ross 			dc = ldap_ping(
1284b3700b07SGordon Ross 			    ctx,
1285b3700b07SGordon Ross 			    cdc,
1286b3700b07SGordon Ross 			    domain_name,
1287b3700b07SGordon Ross 			    DS_DS_FLAG);
1288b3700b07SGordon Ross 			srv_free(cdc);
1289b3700b07SGordon Ross 			cdc = NULL;
1290b3700b07SGordon Ross 
1291b3700b07SGordon Ross 			if (dc == NULL) {
1292b3700b07SGordon Ross 				DEBUG1STATUS(ctx, "(no LDAP response)");
1293*d0bed8f2SGordon Ross 				goto try_global;
1294148c5f43SAlan Wright 			}
1295b3700b07SGordon Ross 			log_ds(ctx, dc);
1296c8e26105Sjp 
1297*d0bed8f2SGordon Ross 			update_item(&ctx->site_domain_controller, dc,
1298b3700b07SGordon Ross 			    AD_STATE_AUTO, dc->ttl);
1299*d0bed8f2SGordon Ross 			update_version(&ctx->site_domain_controller, PARAM1,
13004d61c878SJulian Pullen 			    domain_name_item);
1301*d0bed8f2SGordon Ross 			update_version(&ctx->site_domain_controller, PARAM2,
1302*d0bed8f2SGordon Ross 			    site_name_item);
13034d61c878SJulian Pullen 		}
1304*d0bed8f2SGordon Ross 		return (&ctx->site_domain_controller);
13054d61c878SJulian Pullen 	}
1306c8e26105Sjp 
1307*d0bed8f2SGordon Ross try_global:
1308b3700b07SGordon Ross 
1309*d0bed8f2SGordon Ross 	if (validate_global) {
1310*d0bed8f2SGordon Ross 		if (!is_valid(&ctx->domain_controller) ||
1311*d0bed8f2SGordon Ross 		    is_changed(&ctx->domain_controller, PARAM1,
1312*d0bed8f2SGordon Ross 		    domain_name_item)) {
1313b3700b07SGordon Ross 
1314c8e26105Sjp 			/*
13150dcc7149Snw 			 * Lookup DNS SRV RR named
1316*d0bed8f2SGordon Ross 			 * _ldap._tcp.dc._msdcs.<DomainName>
1317c8e26105Sjp 			 */
1318*d0bed8f2SGordon Ross 			DEBUG1STATUS(ctx, "DNS SRV query, dom=%s",
1319*d0bed8f2SGordon Ross 			    domain_name);
13200dcc7149Snw 			DO_RES_NINIT(ctx);
1321*d0bed8f2SGordon Ross 			cdc = srv_query(&ctx->res_state,
1322*d0bed8f2SGordon Ross 			    LDAP_SRV_HEAD DC_SRV_TAIL,
1323b3700b07SGordon Ross 			    domain_name, prefer_dc);
1324b3700b07SGordon Ross 
1325b3700b07SGordon Ross 			if (cdc == NULL) {
1326b3700b07SGordon Ross 				DEBUG1STATUS(ctx, "(no DNS response)");
13274d61c878SJulian Pullen 				return (NULL);
1328148c5f43SAlan Wright 			}
1329b3700b07SGordon Ross 			log_cds(ctx, cdc);
1330148c5f43SAlan Wright 
1331b3700b07SGordon Ross 			/*
1332b3700b07SGordon Ross 			 * Filter out unresponsive servers, and
1333b3700b07SGordon Ross 			 * save the domain info we get back.
1334b3700b07SGordon Ross 			 */
1335b3700b07SGordon Ross 			dc = ldap_ping(
1336b3700b07SGordon Ross 			    ctx,
1337b3700b07SGordon Ross 			    cdc,
1338b3700b07SGordon Ross 			    domain_name,
1339b3700b07SGordon Ross 			    DS_DS_FLAG);
1340b3700b07SGordon Ross 			srv_free(cdc);
1341b3700b07SGordon Ross 			cdc = NULL;
1342b3700b07SGordon Ross 
1343b3700b07SGordon Ross 			if (dc == NULL) {
1344b3700b07SGordon Ross 				DEBUG1STATUS(ctx, "(no LDAP response)");
1345b3700b07SGordon Ross 				return (NULL);
1346148c5f43SAlan Wright 			}
1347b3700b07SGordon Ross 			log_ds(ctx, dc);
13484d61c878SJulian Pullen 
1349*d0bed8f2SGordon Ross 			update_item(&ctx->domain_controller, dc,
1350b3700b07SGordon Ross 			    AD_STATE_AUTO, dc->ttl);
1351*d0bed8f2SGordon Ross 			update_version(&ctx->domain_controller, PARAM1,
13524d61c878SJulian Pullen 			    domain_name_item);
1353c8e26105Sjp 		}
1354*d0bed8f2SGordon Ross 		return (&ctx->domain_controller);
1355c8e26105Sjp 	}
1356*d0bed8f2SGordon Ross 
13574d61c878SJulian Pullen 	return (NULL);
1358c8e26105Sjp }
1359c8e26105Sjp 
1360b3700b07SGordon Ross ad_disc_ds_t *
13614d61c878SJulian Pullen ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req,
1362b3700b07SGordon Ross     boolean_t *auto_discovered)
1363c8e26105Sjp {
13644d61c878SJulian Pullen 	ad_item_t *domain_controller_item;
1365b3700b07SGordon Ross 	ad_disc_ds_t *domain_controller = NULL;
1366c8e26105Sjp 
13674d61c878SJulian Pullen 	domain_controller_item = validate_DomainController(ctx, req);
13684d61c878SJulian Pullen 
13694d61c878SJulian Pullen 	if (domain_controller_item != NULL) {
13704d61c878SJulian Pullen 		domain_controller = ds_dup(domain_controller_item->value);
13714d61c878SJulian Pullen 		if (auto_discovered != NULL)
13724d61c878SJulian Pullen 			*auto_discovered =
13734d61c878SJulian Pullen 			    (domain_controller_item->state == AD_STATE_AUTO);
13744d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
13757a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
13764d61c878SJulian Pullen 
1377c8e26105Sjp 	return (domain_controller);
1378c8e26105Sjp }
1379c8e26105Sjp 
1380c8e26105Sjp 
1381b3700b07SGordon Ross /*
1382b3700b07SGordon Ross  * Discover the Domain GUID
1383b3700b07SGordon Ross  * This info comes from validate_DomainController()
1384b3700b07SGordon Ross  */
13854d61c878SJulian Pullen static ad_item_t *
1386b3700b07SGordon Ross validate_DomainGUID(ad_disc_t ctx)
1387c8e26105Sjp {
13884d61c878SJulian Pullen 	ad_item_t *domain_controller_item;
1389c8e26105Sjp 
1390b3700b07SGordon Ross 	if (is_fixed(&ctx->domain_guid))
1391b3700b07SGordon Ross 		return (&ctx->domain_guid);
1392c8e26105Sjp 
13934d61c878SJulian Pullen 	domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
13944d61c878SJulian Pullen 	if (domain_controller_item == NULL)
13954d61c878SJulian Pullen 		return (NULL);
1396c8e26105Sjp 
1397b3700b07SGordon Ross 	if (!is_valid(&ctx->domain_guid))
13984d61c878SJulian Pullen 		return (NULL);
13990dcc7149Snw 
1400b3700b07SGordon Ross 	return (&ctx->domain_guid);
1401b3700b07SGordon Ross }
14020dcc7149Snw 
1403148c5f43SAlan Wright 
1404b3700b07SGordon Ross uchar_t *
1405b3700b07SGordon Ross ad_disc_get_DomainGUID(ad_disc_t ctx, boolean_t *auto_discovered)
1406b3700b07SGordon Ross {
1407b3700b07SGordon Ross 	ad_item_t *domain_guid_item;
1408b3700b07SGordon Ross 	uchar_t	*domain_guid = NULL;
14094d61c878SJulian Pullen 
1410b3700b07SGordon Ross 	domain_guid_item = validate_DomainGUID(ctx);
1411b3700b07SGordon Ross 	if (domain_guid_item != NULL) {
1412b3700b07SGordon Ross 		domain_guid = uuid_dup(domain_guid_item->value);
1413b3700b07SGordon Ross 		if (auto_discovered != NULL)
1414b3700b07SGordon Ross 			*auto_discovered =
1415b3700b07SGordon Ross 			    (domain_guid_item->state == AD_STATE_AUTO);
1416b3700b07SGordon Ross 	} else if (auto_discovered != NULL)
1417b3700b07SGordon Ross 		*auto_discovered = B_FALSE;
1418148c5f43SAlan Wright 
1419b3700b07SGordon Ross 	return (domain_guid);
1420b3700b07SGordon Ross }
1421148c5f43SAlan Wright 
1422c8e26105Sjp 
1423b3700b07SGordon Ross /*
1424b3700b07SGordon Ross  * Discover site name (for multi-homed systems the first one found wins)
1425b3700b07SGordon Ross  * This info comes from validate_DomainController()
1426b3700b07SGordon Ross  */
1427b3700b07SGordon Ross static ad_item_t *
1428b3700b07SGordon Ross validate_SiteName(ad_disc_t ctx)
1429b3700b07SGordon Ross {
1430b3700b07SGordon Ross 	ad_item_t *domain_controller_item;
14314d61c878SJulian Pullen 
1432b3700b07SGordon Ross 	if (is_fixed(&ctx->site_name))
1433b3700b07SGordon Ross 		return (&ctx->site_name);
1434c8e26105Sjp 
1435b3700b07SGordon Ross 	domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
1436b3700b07SGordon Ross 	if (domain_controller_item == NULL)
1437b3700b07SGordon Ross 		return (NULL);
14380dcc7149Snw 
1439b3700b07SGordon Ross 	if (!is_valid(&ctx->site_name))
14404d61c878SJulian Pullen 		return (NULL);
1441c8e26105Sjp 
1442b3700b07SGordon Ross 	return (&ctx->site_name);
1443c8e26105Sjp }
1444c8e26105Sjp 
1445c8e26105Sjp 
1446c8e26105Sjp char *
14477a8a68f5SJulian Pullen ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered)
1448c8e26105Sjp {
14494d61c878SJulian Pullen 	ad_item_t *site_name_item;
1450c8e26105Sjp 	char	*site_name = NULL;
1451c8e26105Sjp 
14524d61c878SJulian Pullen 	site_name_item = validate_SiteName(ctx);
14534d61c878SJulian Pullen 	if (site_name_item != NULL) {
14544d61c878SJulian Pullen 		site_name = strdup(site_name_item->value);
14554d61c878SJulian Pullen 		if (auto_discovered != NULL)
14564d61c878SJulian Pullen 			*auto_discovered =
14574d61c878SJulian Pullen 			    (site_name_item->state == AD_STATE_AUTO);
14584d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
14597a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
14604d61c878SJulian Pullen 
1461c8e26105Sjp 	return (site_name);
1462c8e26105Sjp }
1463c8e26105Sjp 
1464c8e26105Sjp 
1465c8e26105Sjp 
1466b3700b07SGordon Ross /*
1467b3700b07SGordon Ross  * Discover forest name
1468b3700b07SGordon Ross  * This info comes from validate_DomainController()
1469b3700b07SGordon Ross  */
14704d61c878SJulian Pullen static ad_item_t *
1471c8e26105Sjp validate_ForestName(ad_disc_t ctx)
1472c8e26105Sjp {
14734d61c878SJulian Pullen 	ad_item_t *domain_controller_item;
1474c8e26105Sjp 
1475c8e26105Sjp 	if (is_fixed(&ctx->forest_name))
14764d61c878SJulian Pullen 		return (&ctx->forest_name);
1477b3700b07SGordon Ross 
14784d61c878SJulian Pullen 	domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
14794d61c878SJulian Pullen 	if (domain_controller_item == NULL)
14804d61c878SJulian Pullen 		return (NULL);
1481c8e26105Sjp 
1482b3700b07SGordon Ross 	if (!is_valid(&ctx->forest_name))
1483b3700b07SGordon Ross 		return (NULL);
14844d61c878SJulian Pullen 
14854d61c878SJulian Pullen 	return (&ctx->forest_name);
1486c8e26105Sjp }
1487c8e26105Sjp 
1488c8e26105Sjp 
1489c8e26105Sjp char *
14907a8a68f5SJulian Pullen ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered)
1491c8e26105Sjp {
14924d61c878SJulian Pullen 	ad_item_t *forest_name_item;
1493c8e26105Sjp 	char	*forest_name = NULL;
1494c8e26105Sjp 
14954d61c878SJulian Pullen 	forest_name_item = validate_ForestName(ctx);
14964d61c878SJulian Pullen 
14974d61c878SJulian Pullen 	if (forest_name_item != NULL) {
14984d61c878SJulian Pullen 		forest_name = strdup(forest_name_item->value);
14994d61c878SJulian Pullen 		if (auto_discovered != NULL)
15004d61c878SJulian Pullen 			*auto_discovered =
15014d61c878SJulian Pullen 			    (forest_name_item->state == AD_STATE_AUTO);
15024d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
15037a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
1504c8e26105Sjp 
1505c8e26105Sjp 	return (forest_name);
1506c8e26105Sjp }
1507c8e26105Sjp 
1508c8e26105Sjp 
15090dcc7149Snw /* Discover global catalog servers */
15104d61c878SJulian Pullen static ad_item_t *
1511c8e26105Sjp validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req)
1512c8e26105Sjp {
1513b3700b07SGordon Ross 	ad_disc_ds_t *gc = NULL;
1514b3700b07SGordon Ross 	ad_disc_cds_t *cgc = NULL;
15157a8a68f5SJulian Pullen 	boolean_t validate_global = B_FALSE;
15167a8a68f5SJulian Pullen 	boolean_t validate_site = B_FALSE;
1517b3700b07SGordon Ross 	ad_item_t *dc_item;
15184d61c878SJulian Pullen 	ad_item_t *forest_name_item;
15194d61c878SJulian Pullen 	ad_item_t *site_name_item;
1520b3700b07SGordon Ross 	char *forest_name;
1521b3700b07SGordon Ross 	char *site_name;
1522c8e26105Sjp 
15234d61c878SJulian Pullen 	/* If the values is fixed there will not be a site specific version */
1524c8e26105Sjp 	if (is_fixed(&ctx->global_catalog))
15254d61c878SJulian Pullen 		return (&ctx->global_catalog);
15264d61c878SJulian Pullen 
15274d61c878SJulian Pullen 	forest_name_item = validate_ForestName(ctx);
1528*d0bed8f2SGordon Ross 	if (forest_name_item == NULL) {
1529*d0bed8f2SGordon Ross 		DEBUG1STATUS(ctx, "(no forrest name)");
15304d61c878SJulian Pullen 		return (NULL);
1531*d0bed8f2SGordon Ross 	}
1532b3700b07SGordon Ross 	forest_name = (char *)forest_name_item->value;
1533c8e26105Sjp 
1534c8e26105Sjp 	if (req == AD_DISC_GLOBAL)
15357a8a68f5SJulian Pullen 		validate_global = B_TRUE;
1536c8e26105Sjp 	else {
1537b3700b07SGordon Ross 		if (is_fixed(&ctx->site_name))
15387a8a68f5SJulian Pullen 			validate_site = B_TRUE;
1539*d0bed8f2SGordon Ross 		if (req == AD_DISC_PREFER_SITE)
15407a8a68f5SJulian Pullen 			validate_global = B_TRUE;
1541c8e26105Sjp 	}
1542c8e26105Sjp 
1543*d0bed8f2SGordon Ross 	/*
1544*d0bed8f2SGordon Ross 	 * If we're trying both site-specific and global,
1545*d0bed8f2SGordon Ross 	 * try the site-specific first, then fall-back.
1546*d0bed8f2SGordon Ross 	 */
1547*d0bed8f2SGordon Ross 	if (validate_site) {
1548*d0bed8f2SGordon Ross 		site_name_item = &ctx->site_name;
1549*d0bed8f2SGordon Ross 		site_name = (char *)site_name_item->value;
1550*d0bed8f2SGordon Ross 
1551*d0bed8f2SGordon Ross 		if (!is_valid(&ctx->site_global_catalog) ||
1552*d0bed8f2SGordon Ross 		    is_changed(&ctx->site_global_catalog, PARAM1,
1553*d0bed8f2SGordon Ross 		    forest_name_item) ||
1554*d0bed8f2SGordon Ross 		    is_changed(&ctx->site_global_catalog, PARAM2,
1555*d0bed8f2SGordon Ross 		    site_name_item)) {
1556*d0bed8f2SGordon Ross 			char rr_name[DNS_MAX_NAME];
1557b3700b07SGordon Ross 
1558c8e26105Sjp 			/*
1559b3700b07SGordon Ross 			 * See if our DC is also a GC.
1560b3700b07SGordon Ross 			 */
1561b3700b07SGordon Ross 			dc_item = validate_DomainController(ctx, req);
1562b3700b07SGordon Ross 			if (dc_item != NULL) {
1563b3700b07SGordon Ross 				ad_disc_ds_t *ds = dc_item->value;
1564b3700b07SGordon Ross 				if ((ds->flags & DS_GC_FLAG) != 0) {
1565b3700b07SGordon Ross 					DEBUG1STATUS(ctx,
1566*d0bed8f2SGordon Ross 					    "DC is also a GC for %s in %s",
1567*d0bed8f2SGordon Ross 					    forest_name, site_name);
1568b3700b07SGordon Ross 					gc = ds_dup(ds);
1569b3700b07SGordon Ross 					if (gc != NULL) {
1570b3700b07SGordon Ross 						gc->port = GC_PORT;
1571*d0bed8f2SGordon Ross 						goto update_site;
1572b3700b07SGordon Ross 					}
1573b3700b07SGordon Ross 				}
1574b3700b07SGordon Ross 			}
1575b3700b07SGordon Ross 
1576b3700b07SGordon Ross 			/*
1577b3700b07SGordon Ross 			 * Lookup DNS SRV RR named:
1578*d0bed8f2SGordon Ross 			 * _ldap._tcp.<siteName>._sites.gc.
1579*d0bed8f2SGordon Ross 			 *	_msdcs.<ForestName>
1580c8e26105Sjp 			 */
1581*d0bed8f2SGordon Ross 			DEBUG1STATUS(ctx, "DNS SRV query, forest=%s, site=%s",
1582*d0bed8f2SGordon Ross 			    forest_name, site_name);
1583*d0bed8f2SGordon Ross 			(void) snprintf(rr_name, sizeof (rr_name),
1584*d0bed8f2SGordon Ross 			    LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL,
1585*d0bed8f2SGordon Ross 			    site_name);
15860dcc7149Snw 			DO_RES_NINIT(ctx);
1587*d0bed8f2SGordon Ross 			cgc = srv_query(&ctx->res_state, rr_name,
1588b3700b07SGordon Ross 			    forest_name, NULL);
1589c8e26105Sjp 
1590b3700b07SGordon Ross 			if (cgc == NULL) {
1591b3700b07SGordon Ross 				DEBUG1STATUS(ctx, "(no DNS response)");
1592*d0bed8f2SGordon Ross 				goto try_global;
1593148c5f43SAlan Wright 			}
1594b3700b07SGordon Ross 			log_cds(ctx, cgc);
1595148c5f43SAlan Wright 
1596b3700b07SGordon Ross 			/*
1597b3700b07SGordon Ross 			 * Filter out unresponsive servers, and
1598b3700b07SGordon Ross 			 * save the domain info we get back.
1599b3700b07SGordon Ross 			 */
1600b3700b07SGordon Ross 			gc = ldap_ping(
1601b3700b07SGordon Ross 			    NULL,
1602b3700b07SGordon Ross 			    cgc,
1603b3700b07SGordon Ross 			    forest_name,
1604b3700b07SGordon Ross 			    DS_GC_FLAG);
1605b3700b07SGordon Ross 			srv_free(cgc);
1606b3700b07SGordon Ross 			cgc = NULL;
1607b3700b07SGordon Ross 
1608b3700b07SGordon Ross 			if (gc == NULL) {
1609b3700b07SGordon Ross 				DEBUG1STATUS(ctx, "(no LDAP response)");
1610*d0bed8f2SGordon Ross 				goto try_global;
1611148c5f43SAlan Wright 			}
1612b3700b07SGordon Ross 			log_ds(ctx, gc);
1613c8e26105Sjp 
1614*d0bed8f2SGordon Ross 		update_site:
1615*d0bed8f2SGordon Ross 			update_item(&ctx->site_global_catalog, gc,
1616b3700b07SGordon Ross 			    AD_STATE_AUTO, gc->ttl);
1617*d0bed8f2SGordon Ross 			update_version(&ctx->site_global_catalog, PARAM1,
16184d61c878SJulian Pullen 			    forest_name_item);
1619*d0bed8f2SGordon Ross 			update_version(&ctx->site_global_catalog, PARAM2,
1620*d0bed8f2SGordon Ross 			    site_name_item);
16214d61c878SJulian Pullen 		}
1622*d0bed8f2SGordon Ross 		return (&ctx->site_global_catalog);
16234d61c878SJulian Pullen 	}
1624c8e26105Sjp 
1625*d0bed8f2SGordon Ross try_global:
1626b3700b07SGordon Ross 
1627*d0bed8f2SGordon Ross 	if (validate_global) {
1628*d0bed8f2SGordon Ross 		if (!is_valid(&ctx->global_catalog) ||
1629*d0bed8f2SGordon Ross 		    is_changed(&ctx->global_catalog, PARAM1,
1630*d0bed8f2SGordon Ross 		    forest_name_item)) {
1631b3700b07SGordon Ross 
1632b3700b07SGordon Ross 			/*
1633b3700b07SGordon Ross 			 * See if our DC is also a GC.
1634b3700b07SGordon Ross 			 */
1635b3700b07SGordon Ross 			dc_item = validate_DomainController(ctx, req);
1636b3700b07SGordon Ross 			if (dc_item != NULL) {
1637b3700b07SGordon Ross 				ad_disc_ds_t *ds = dc_item->value;
1638b3700b07SGordon Ross 				if ((ds->flags & DS_GC_FLAG) != 0) {
1639b3700b07SGordon Ross 					DEBUG1STATUS(ctx,
1640*d0bed8f2SGordon Ross 					    "DC is also a GC for %s",
1641*d0bed8f2SGordon Ross 					    forest_name);
1642b3700b07SGordon Ross 					gc = ds_dup(ds);
1643b3700b07SGordon Ross 					if (gc != NULL) {
1644b3700b07SGordon Ross 						gc->port = GC_PORT;
1645*d0bed8f2SGordon Ross 						goto update_global;
1646b3700b07SGordon Ross 					}
1647b3700b07SGordon Ross 				}
1648b3700b07SGordon Ross 			}
16494d61c878SJulian Pullen 
1650c8e26105Sjp 			/*
16510dcc7149Snw 			 * Lookup DNS SRV RR named:
1652*d0bed8f2SGordon Ross 			 * _ldap._tcp.gc._msdcs.<ForestName>
1653c8e26105Sjp 			 */
1654*d0bed8f2SGordon Ross 			DEBUG1STATUS(ctx, "DNS SRV query, forest=%s",
1655*d0bed8f2SGordon Ross 			    forest_name);
16560dcc7149Snw 			DO_RES_NINIT(ctx);
1657*d0bed8f2SGordon Ross 			cgc = srv_query(&ctx->res_state,
1658*d0bed8f2SGordon Ross 			    LDAP_SRV_HEAD GC_SRV_TAIL,
1659b3700b07SGordon Ross 			    forest_name, NULL);
16604d61c878SJulian Pullen 
1661b3700b07SGordon Ross 			if (cgc == NULL) {
1662b3700b07SGordon Ross 				DEBUG1STATUS(ctx, "(no DNS response)");
16634d61c878SJulian Pullen 				return (NULL);
1664148c5f43SAlan Wright 			}
1665b3700b07SGordon Ross 			log_cds(ctx, cgc);
1666148c5f43SAlan Wright 
1667b3700b07SGordon Ross 			/*
1668b3700b07SGordon Ross 			 * Filter out unresponsive servers, and
1669b3700b07SGordon Ross 			 * save the domain info we get back.
1670b3700b07SGordon Ross 			 */
1671b3700b07SGordon Ross 			gc = ldap_ping(
1672b3700b07SGordon Ross 			    NULL,
1673b3700b07SGordon Ross 			    cgc,
1674b3700b07SGordon Ross 			    forest_name,
1675b3700b07SGordon Ross 			    DS_GC_FLAG);
1676b3700b07SGordon Ross 			srv_free(cgc);
1677b3700b07SGordon Ross 			cgc = NULL;
1678b3700b07SGordon Ross 
1679b3700b07SGordon Ross 			if (gc == NULL) {
1680b3700b07SGordon Ross 				DEBUG1STATUS(ctx, "(no LDAP response)");
1681b3700b07SGordon Ross 				return (NULL);
1682148c5f43SAlan Wright 			}
1683b3700b07SGordon Ross 			log_ds(ctx, gc);
1684148c5f43SAlan Wright 
1685*d0bed8f2SGordon Ross 		update_global:
1686*d0bed8f2SGordon Ross 			update_item(&ctx->global_catalog, gc,
1687b3700b07SGordon Ross 			    AD_STATE_AUTO, gc->ttl);
1688*d0bed8f2SGordon Ross 			update_version(&ctx->global_catalog, PARAM1,
16894d61c878SJulian Pullen 			    forest_name_item);
1690c8e26105Sjp 		}
1691*d0bed8f2SGordon Ross 		return (&ctx->global_catalog);
1692c8e26105Sjp 	}
16934d61c878SJulian Pullen 	return (NULL);
1694c8e26105Sjp }
1695c8e26105Sjp 
1696c8e26105Sjp 
1697b3700b07SGordon Ross ad_disc_ds_t *
16984d61c878SJulian Pullen ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req,
16997a8a68f5SJulian Pullen 			boolean_t *auto_discovered)
1700c8e26105Sjp {
1701b3700b07SGordon Ross 	ad_disc_ds_t *global_catalog = NULL;
17024d61c878SJulian Pullen 	ad_item_t *global_catalog_item;
17034d61c878SJulian Pullen 
17044d61c878SJulian Pullen 	global_catalog_item = validate_GlobalCatalog(ctx, req);
1705c8e26105Sjp 
17064d61c878SJulian Pullen 	if (global_catalog_item != NULL) {
17074d61c878SJulian Pullen 		global_catalog = ds_dup(global_catalog_item->value);
17084d61c878SJulian Pullen 		if (auto_discovered != NULL)
17094d61c878SJulian Pullen 			*auto_discovered =
17104d61c878SJulian Pullen 			    (global_catalog_item->state == AD_STATE_AUTO);
17114d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
17127a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
1713c8e26105Sjp 
1714c8e26105Sjp 	return (global_catalog);
1715c8e26105Sjp }
1716c8e26105Sjp 
1717c8e26105Sjp 
17184d61c878SJulian Pullen static ad_item_t *
17194d61c878SJulian Pullen validate_TrustedDomains(ad_disc_t ctx)
17204d61c878SJulian Pullen {
17214d61c878SJulian Pullen 	LDAP *ld = NULL;
17224d61c878SJulian Pullen 	ad_item_t *global_catalog_item;
17234d61c878SJulian Pullen 	ad_item_t *forest_name_item;
17244d61c878SJulian Pullen 	ad_disc_trusteddomains_t *trusted_domains;
17254d61c878SJulian Pullen 	char *dn = NULL;
17264d61c878SJulian Pullen 	char *forest_name_dn;
17274d61c878SJulian Pullen 	int len;
17284d61c878SJulian Pullen 	int num_parts;
17294d61c878SJulian Pullen 
17304d61c878SJulian Pullen 	if (is_fixed(&ctx->trusted_domains))
17314d61c878SJulian Pullen 		return (&ctx->trusted_domains);
17324d61c878SJulian Pullen 
17334d61c878SJulian Pullen 	global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL);
17344d61c878SJulian Pullen 	if (global_catalog_item == NULL)
17354d61c878SJulian Pullen 		return (NULL);
17364d61c878SJulian Pullen 
17374d61c878SJulian Pullen 	forest_name_item = validate_ForestName(ctx);
17384d61c878SJulian Pullen 	if (forest_name_item == NULL)
17394d61c878SJulian Pullen 		return (NULL);
17404d61c878SJulian Pullen 
17414d61c878SJulian Pullen 	if (!is_valid(&ctx->trusted_domains) ||
17424d61c878SJulian Pullen 	    is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) ||
17434d61c878SJulian Pullen 	    is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) {
17444d61c878SJulian Pullen 
17454d61c878SJulian Pullen 		forest_name_dn = ldap_dns_to_dn(forest_name_item->value,
17464d61c878SJulian Pullen 		    &num_parts);
17474d61c878SJulian Pullen 		if (forest_name_dn == NULL)
17484d61c878SJulian Pullen 			return (NULL);
17494d61c878SJulian Pullen 
17504d61c878SJulian Pullen 		len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1;
17514d61c878SJulian Pullen 		dn = malloc(len);
17524d61c878SJulian Pullen 		if (dn == NULL)  {
17534d61c878SJulian Pullen 			free(forest_name_dn);
17544d61c878SJulian Pullen 			return (NULL);
17554d61c878SJulian Pullen 		}
17564d61c878SJulian Pullen 		(void) snprintf(dn, len, "CN=System,%s", forest_name_dn);
17574d61c878SJulian Pullen 		free(forest_name_dn);
17584d61c878SJulian Pullen 
17594d61c878SJulian Pullen 		trusted_domains = ldap_lookup_trusted_domains(
17604d61c878SJulian Pullen 		    &ld, global_catalog_item->value, dn);
17614d61c878SJulian Pullen 
17624d61c878SJulian Pullen 		if (ld != NULL)
17634d61c878SJulian Pullen 			(void) ldap_unbind(ld);
17644d61c878SJulian Pullen 		free(dn);
17654d61c878SJulian Pullen 
17664d61c878SJulian Pullen 		if (trusted_domains == NULL)
17674d61c878SJulian Pullen 			return (NULL);
17684d61c878SJulian Pullen 
17694d61c878SJulian Pullen 		update_item(&ctx->trusted_domains, trusted_domains,
17704d61c878SJulian Pullen 		    AD_STATE_AUTO, 0);
17714d61c878SJulian Pullen 		update_version(&ctx->trusted_domains, PARAM1,
17724d61c878SJulian Pullen 		    global_catalog_item);
17734d61c878SJulian Pullen 		update_version(&ctx->trusted_domains, PARAM2,
17744d61c878SJulian Pullen 		    forest_name_item);
17754d61c878SJulian Pullen 	}
17764d61c878SJulian Pullen 
17774d61c878SJulian Pullen 	return (&ctx->trusted_domains);
17784d61c878SJulian Pullen }
17794d61c878SJulian Pullen 
17804d61c878SJulian Pullen 
17814d61c878SJulian Pullen ad_disc_trusteddomains_t *
17827a8a68f5SJulian Pullen ad_disc_get_TrustedDomains(ad_disc_t ctx, boolean_t *auto_discovered)
17834d61c878SJulian Pullen {
17844d61c878SJulian Pullen 	ad_disc_trusteddomains_t *trusted_domains = NULL;
17854d61c878SJulian Pullen 	ad_item_t *trusted_domains_item;
17864d61c878SJulian Pullen 
17874d61c878SJulian Pullen 	trusted_domains_item = validate_TrustedDomains(ctx);
17884d61c878SJulian Pullen 
17894d61c878SJulian Pullen 	if (trusted_domains_item != NULL) {
17904d61c878SJulian Pullen 		trusted_domains = td_dup(trusted_domains_item->value);
17914d61c878SJulian Pullen 		if (auto_discovered != NULL)
17924d61c878SJulian Pullen 			*auto_discovered =
17934d61c878SJulian Pullen 			    (trusted_domains_item->state == AD_STATE_AUTO);
17944d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
17957a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
17964d61c878SJulian Pullen 
17974d61c878SJulian Pullen 	return (trusted_domains);
17984d61c878SJulian Pullen }
17994d61c878SJulian Pullen 
18004d61c878SJulian Pullen 
18014d61c878SJulian Pullen static ad_item_t *
18024d61c878SJulian Pullen validate_DomainsInForest(ad_disc_t ctx)
18034d61c878SJulian Pullen {
18044d61c878SJulian Pullen 	ad_item_t *global_catalog_item;
18054d61c878SJulian Pullen 	LDAP *ld = NULL;
18064d61c878SJulian Pullen 	ad_disc_domainsinforest_t *domains_in_forest;
18074d61c878SJulian Pullen 
18084d61c878SJulian Pullen 	if (is_fixed(&ctx->domains_in_forest))
18094d61c878SJulian Pullen 		return (&ctx->domains_in_forest);
18104d61c878SJulian Pullen 
18114d61c878SJulian Pullen 	global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL);
18124d61c878SJulian Pullen 	if (global_catalog_item == NULL)
18134d61c878SJulian Pullen 		return (NULL);
18144d61c878SJulian Pullen 
18154d61c878SJulian Pullen 	if (!is_valid(&ctx->domains_in_forest) ||
18164d61c878SJulian Pullen 	    is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) {
18174d61c878SJulian Pullen 
18184d61c878SJulian Pullen 		domains_in_forest = ldap_lookup_domains_in_forest(
18194d61c878SJulian Pullen 		    &ld, global_catalog_item->value);
18204d61c878SJulian Pullen 
18214d61c878SJulian Pullen 		if (ld != NULL)
18224d61c878SJulian Pullen 			(void) ldap_unbind(ld);
18234d61c878SJulian Pullen 
18244d61c878SJulian Pullen 		if (domains_in_forest == NULL)
18254d61c878SJulian Pullen 			return (NULL);
18264d61c878SJulian Pullen 
18274d61c878SJulian Pullen 		update_item(&ctx->domains_in_forest, domains_in_forest,
18284d61c878SJulian Pullen 		    AD_STATE_AUTO, 0);
18294d61c878SJulian Pullen 		update_version(&ctx->domains_in_forest, PARAM1,
18304d61c878SJulian Pullen 		    global_catalog_item);
18314d61c878SJulian Pullen 	}
18324d61c878SJulian Pullen 	return (&ctx->domains_in_forest);
18334d61c878SJulian Pullen }
18344d61c878SJulian Pullen 
18354d61c878SJulian Pullen 
18364d61c878SJulian Pullen ad_disc_domainsinforest_t *
18377a8a68f5SJulian Pullen ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered)
18384d61c878SJulian Pullen {
18394d61c878SJulian Pullen 	ad_disc_domainsinforest_t *domains_in_forest = NULL;
18404d61c878SJulian Pullen 	ad_item_t *domains_in_forest_item;
18414d61c878SJulian Pullen 
18424d61c878SJulian Pullen 	domains_in_forest_item = validate_DomainsInForest(ctx);
18434d61c878SJulian Pullen 
18444d61c878SJulian Pullen 	if (domains_in_forest_item != NULL) {
18454d61c878SJulian Pullen 		domains_in_forest = df_dup(domains_in_forest_item->value);
18464d61c878SJulian Pullen 		if (auto_discovered != NULL)
18474d61c878SJulian Pullen 			*auto_discovered =
18484d61c878SJulian Pullen 			    (domains_in_forest_item->state == AD_STATE_AUTO);
18494d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
18507a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
18514d61c878SJulian Pullen 
18524d61c878SJulian Pullen 	return (domains_in_forest);
18534d61c878SJulian Pullen }
18544d61c878SJulian Pullen 
1855b3700b07SGordon Ross static ad_item_t *
1856b3700b07SGordon Ross validate_PreferredDC(ad_disc_t ctx)
1857b3700b07SGordon Ross {
1858b3700b07SGordon Ross 	if (is_valid(&ctx->preferred_dc))
1859b3700b07SGordon Ross 		return (&ctx->preferred_dc);
1860b3700b07SGordon Ross 
1861b3700b07SGordon Ross 	return (NULL);
1862b3700b07SGordon Ross }
1863b3700b07SGordon Ross 
1864b3700b07SGordon Ross ad_disc_ds_t *
1865b3700b07SGordon Ross ad_disc_get_PreferredDC(ad_disc_t ctx, boolean_t *auto_discovered)
1866b3700b07SGordon Ross {
1867b3700b07SGordon Ross 	ad_disc_ds_t *preferred_dc = NULL;
1868b3700b07SGordon Ross 	ad_item_t *preferred_dc_item;
1869b3700b07SGordon Ross 
1870b3700b07SGordon Ross 	preferred_dc_item = validate_PreferredDC(ctx);
1871b3700b07SGordon Ross 
1872b3700b07SGordon Ross 	if (preferred_dc_item != NULL) {
1873b3700b07SGordon Ross 		preferred_dc = ds_dup(preferred_dc_item->value);
1874b3700b07SGordon Ross 		if (auto_discovered != NULL)
1875b3700b07SGordon Ross 			*auto_discovered =
1876b3700b07SGordon Ross 			    (preferred_dc_item->state == AD_STATE_AUTO);
1877b3700b07SGordon Ross 	} else if (auto_discovered != NULL)
1878b3700b07SGordon Ross 		*auto_discovered = B_FALSE;
1879b3700b07SGordon Ross 
1880b3700b07SGordon Ross 	return (preferred_dc);
1881b3700b07SGordon Ross }
18824d61c878SJulian Pullen 
18834d61c878SJulian Pullen 
18844d61c878SJulian Pullen 
1885c8e26105Sjp int
1886c8e26105Sjp ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName)
1887c8e26105Sjp {
1888c8e26105Sjp 	char *domain_name = NULL;
1889c8e26105Sjp 	if (domainName != NULL) {
1890c8e26105Sjp 		domain_name = strdup(domainName);
1891c8e26105Sjp 		if (domain_name == NULL)
1892c8e26105Sjp 			return (-1);
18934d61c878SJulian Pullen 		update_item(&ctx->domain_name, domain_name,
18944d61c878SJulian Pullen 		    AD_STATE_FIXED, 0);
18954d61c878SJulian Pullen 	} else if (ctx->domain_name.state == AD_STATE_FIXED)
18964d61c878SJulian Pullen 		ctx->domain_name.state = AD_STATE_INVALID;
1897c8e26105Sjp 	return (0);
1898c8e26105Sjp }
1899c8e26105Sjp 
1900b3700b07SGordon Ross int
1901b3700b07SGordon Ross ad_disc_set_DomainGUID(ad_disc_t ctx, uchar_t *u)
1902b3700b07SGordon Ross {
1903b3700b07SGordon Ross 	char *domain_guid = NULL;
1904b3700b07SGordon Ross 	if (u != NULL) {
1905b3700b07SGordon Ross 		domain_guid = uuid_dup(u);
1906b3700b07SGordon Ross 		if (domain_guid == NULL)
1907b3700b07SGordon Ross 			return (-1);
1908b3700b07SGordon Ross 		update_item(&ctx->domain_guid, domain_guid,
1909b3700b07SGordon Ross 		    AD_STATE_FIXED, 0);
1910b3700b07SGordon Ross 	} else if (ctx->domain_guid.state == AD_STATE_FIXED)
1911b3700b07SGordon Ross 		ctx->domain_guid.state = AD_STATE_INVALID;
1912b3700b07SGordon Ross 	return (0);
1913b3700b07SGordon Ross }
1914b3700b07SGordon Ross 
1915b3700b07SGordon Ross void
1916b3700b07SGordon Ross auto_set_DomainGUID(ad_disc_t ctx, uchar_t *u)
1917b3700b07SGordon Ross {
1918b3700b07SGordon Ross 	char *domain_guid = NULL;
1919b3700b07SGordon Ross 
1920b3700b07SGordon Ross 	if (is_fixed(&ctx->domain_guid))
1921b3700b07SGordon Ross 		return;
1922b3700b07SGordon Ross 
1923b3700b07SGordon Ross 	domain_guid = uuid_dup(u);
1924b3700b07SGordon Ross 	if (domain_guid == NULL)
1925b3700b07SGordon Ross 		return;
1926b3700b07SGordon Ross 	update_item(&ctx->domain_guid, domain_guid, AD_STATE_AUTO, 0);
1927b3700b07SGordon Ross }
1928c8e26105Sjp 
1929c8e26105Sjp int
1930c8e26105Sjp ad_disc_set_DomainController(ad_disc_t ctx,
1931b3700b07SGordon Ross 				const ad_disc_ds_t *domainController)
1932c8e26105Sjp {
1933b3700b07SGordon Ross 	ad_disc_ds_t *domain_controller = NULL;
1934c8e26105Sjp 	if (domainController != NULL) {
19354d61c878SJulian Pullen 		domain_controller = ds_dup(domainController);
1936c8e26105Sjp 		if (domain_controller == NULL)
1937c8e26105Sjp 			return (-1);
19384d61c878SJulian Pullen 		update_item(&ctx->domain_controller, domain_controller,
19394d61c878SJulian Pullen 		    AD_STATE_FIXED, 0);
19404d61c878SJulian Pullen 	} else if (ctx->domain_controller.state == AD_STATE_FIXED)
19414d61c878SJulian Pullen 		ctx->domain_controller.state = AD_STATE_INVALID;
1942c8e26105Sjp 	return (0);
1943c8e26105Sjp }
1944c8e26105Sjp 
1945c8e26105Sjp int
1946c8e26105Sjp ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName)
1947c8e26105Sjp {
1948c8e26105Sjp 	char *site_name = NULL;
1949c8e26105Sjp 	if (siteName != NULL) {
1950c8e26105Sjp 		site_name = strdup(siteName);
1951c8e26105Sjp 		if (site_name == NULL)
1952c8e26105Sjp 			return (-1);
19534d61c878SJulian Pullen 		update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0);
19544d61c878SJulian Pullen 	} else if (ctx->site_name.state == AD_STATE_FIXED)
19554d61c878SJulian Pullen 		ctx->site_name.state = AD_STATE_INVALID;
1956c8e26105Sjp 	return (0);
1957c8e26105Sjp }
1958c8e26105Sjp 
1959b3700b07SGordon Ross void
1960b3700b07SGordon Ross auto_set_SiteName(ad_disc_t ctx, char *siteName)
1961b3700b07SGordon Ross {
1962b3700b07SGordon Ross 	char *site_name = NULL;
1963b3700b07SGordon Ross 
1964b3700b07SGordon Ross 	if (is_fixed(&ctx->site_name))
1965b3700b07SGordon Ross 		return;
1966b3700b07SGordon Ross 
1967b3700b07SGordon Ross 	site_name = strdup(siteName);
1968b3700b07SGordon Ross 	if (site_name == NULL)
1969b3700b07SGordon Ross 		return;
1970b3700b07SGordon Ross 	update_item(&ctx->site_name, site_name, AD_STATE_AUTO, 0);
1971b3700b07SGordon Ross }
1972b3700b07SGordon Ross 
1973c8e26105Sjp int
1974c8e26105Sjp ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName)
1975c8e26105Sjp {
1976c8e26105Sjp 	char *forest_name = NULL;
1977c8e26105Sjp 	if (forestName != NULL) {
1978c8e26105Sjp 		forest_name = strdup(forestName);
1979c8e26105Sjp 		if (forest_name == NULL)
1980c8e26105Sjp 			return (-1);
19814d61c878SJulian Pullen 		update_item(&ctx->forest_name, forest_name,
19824d61c878SJulian Pullen 		    AD_STATE_FIXED, 0);
19834d61c878SJulian Pullen 	} else if (ctx->forest_name.state == AD_STATE_FIXED)
19844d61c878SJulian Pullen 		ctx->forest_name.state = AD_STATE_INVALID;
1985c8e26105Sjp 	return (0);
1986c8e26105Sjp }
1987c8e26105Sjp 
1988b3700b07SGordon Ross void
1989b3700b07SGordon Ross auto_set_ForestName(ad_disc_t ctx, char *forestName)
1990b3700b07SGordon Ross {
1991b3700b07SGordon Ross 	char *forest_name = NULL;
1992b3700b07SGordon Ross 
1993b3700b07SGordon Ross 	if (is_fixed(&ctx->forest_name))
1994b3700b07SGordon Ross 		return;
1995b3700b07SGordon Ross 
1996b3700b07SGordon Ross 	forest_name = strdup(forestName);
1997b3700b07SGordon Ross 	if (forest_name == NULL)
1998b3700b07SGordon Ross 		return;
1999b3700b07SGordon Ross 	update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0);
2000b3700b07SGordon Ross }
2001b3700b07SGordon Ross 
2002c8e26105Sjp int
2003479ac375Sdm ad_disc_set_GlobalCatalog(ad_disc_t ctx,
2004b3700b07SGordon Ross     const ad_disc_ds_t *globalCatalog)
2005c8e26105Sjp {
2006b3700b07SGordon Ross 	ad_disc_ds_t *global_catalog = NULL;
2007c8e26105Sjp 	if (globalCatalog != NULL) {
20084d61c878SJulian Pullen 		global_catalog = ds_dup(globalCatalog);
2009c8e26105Sjp 		if (global_catalog == NULL)
2010c8e26105Sjp 			return (-1);
20114d61c878SJulian Pullen 		update_item(&ctx->global_catalog, global_catalog,
20124d61c878SJulian Pullen 		    AD_STATE_FIXED, 0);
20134d61c878SJulian Pullen 	} else if (ctx->global_catalog.state == AD_STATE_FIXED)
20144d61c878SJulian Pullen 		ctx->global_catalog.state = AD_STATE_INVALID;
2015c8e26105Sjp 	return (0);
2016c8e26105Sjp }
2017c8e26105Sjp 
2018b3700b07SGordon Ross int
2019b3700b07SGordon Ross ad_disc_set_PreferredDC(ad_disc_t ctx, const ad_disc_ds_t *pref_dc)
2020b3700b07SGordon Ross {
2021b3700b07SGordon Ross 	ad_disc_ds_t *new_pref_dc = NULL;
2022b3700b07SGordon Ross 	if (pref_dc != NULL) {
2023b3700b07SGordon Ross 		new_pref_dc = ds_dup(pref_dc);
2024b3700b07SGordon Ross 		if (new_pref_dc == NULL)
2025b3700b07SGordon Ross 			return (-1);
2026b3700b07SGordon Ross 		update_item(&ctx->preferred_dc, new_pref_dc,
2027b3700b07SGordon Ross 		    AD_STATE_FIXED, 0);
2028b3700b07SGordon Ross 	} else if (ctx->preferred_dc.state == AD_STATE_FIXED)
2029b3700b07SGordon Ross 		ctx->preferred_dc.state = AD_STATE_INVALID;
2030b3700b07SGordon Ross 	return (0);
2031b3700b07SGordon Ross }
2032b3700b07SGordon Ross 
2033b3700b07SGordon Ross void
2034b3700b07SGordon Ross ad_disc_set_StatusFP(ad_disc_t ctx, struct __FILE_TAG *fp)
2035b3700b07SGordon Ross {
2036b3700b07SGordon Ross 	ctx->status_fp = fp;
2037b3700b07SGordon Ross }
2038b3700b07SGordon Ross 
2039c8e26105Sjp 
2040c8e26105Sjp int
2041c8e26105Sjp ad_disc_unset(ad_disc_t ctx)
2042c8e26105Sjp {
20434d61c878SJulian Pullen 	if (ctx->domain_name.state == AD_STATE_FIXED)
20444d61c878SJulian Pullen 		ctx->domain_name.state =  AD_STATE_INVALID;
2045c8e26105Sjp 
20464d61c878SJulian Pullen 	if (ctx->domain_controller.state == AD_STATE_FIXED)
20474d61c878SJulian Pullen 		ctx->domain_controller.state =  AD_STATE_INVALID;
2048c8e26105Sjp 
2049b3700b07SGordon Ross 	if (ctx->preferred_dc.state == AD_STATE_FIXED)
2050b3700b07SGordon Ross 		ctx->preferred_dc.state =  AD_STATE_INVALID;
2051b3700b07SGordon Ross 
20524d61c878SJulian Pullen 	if (ctx->site_name.state == AD_STATE_FIXED)
20534d61c878SJulian Pullen 		ctx->site_name.state =  AD_STATE_INVALID;
2054c8e26105Sjp 
20554d61c878SJulian Pullen 	if (ctx->forest_name.state == AD_STATE_FIXED)
20564d61c878SJulian Pullen 		ctx->forest_name.state =  AD_STATE_INVALID;
2057c8e26105Sjp 
20584d61c878SJulian Pullen 	if (ctx->global_catalog.state == AD_STATE_FIXED)
20594d61c878SJulian Pullen 		ctx->global_catalog.state =  AD_STATE_INVALID;
2060c8e26105Sjp 
2061c8e26105Sjp 	return (0);
2062c8e26105Sjp }
2063c8e26105Sjp 
2064c8e26105Sjp /*
2065c8e26105Sjp  * ad_disc_get_TTL
2066c8e26105Sjp  *
2067c8e26105Sjp  * This routines the time to live for AD
2068c8e26105Sjp  * auto discovered items.
2069c8e26105Sjp  *
2070c8e26105Sjp  *	Returns:
2071c8e26105Sjp  *		-1 if there are no TTL items
2072c8e26105Sjp  *		0  if there are expired items
2073c8e26105Sjp  *		else the number of seconds
20740dcc7149Snw  *
20750dcc7149Snw  * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it
20760dcc7149Snw  * is positive -- min() greater than zero.
2077c8e26105Sjp  */
20780dcc7149Snw #define	MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \
20790dcc7149Snw 		(-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x))))
2080c8e26105Sjp int
2081c8e26105Sjp ad_disc_get_TTL(ad_disc_t ctx)
2082c8e26105Sjp {
2083c5866007SKeyur Desai 	time_t expires;
2084c8e26105Sjp 	int ttl;
2085c8e26105Sjp 
2086c5866007SKeyur Desai 	expires = MIN_GT_ZERO(ctx->domain_controller.expires,
2087c5866007SKeyur Desai 	    ctx->global_catalog.expires);
2088c5866007SKeyur Desai 	expires = MIN_GT_ZERO(expires, ctx->site_domain_controller.expires);
2089c5866007SKeyur Desai 	expires = MIN_GT_ZERO(expires, ctx->site_global_catalog.expires);
2090c8e26105Sjp 
2091c5866007SKeyur Desai 	if (expires == -1) {
2092c8e26105Sjp 		return (-1);
2093c5866007SKeyur Desai 	}
2094c5866007SKeyur Desai 
2095c5866007SKeyur Desai 	if (ctx->expires_not_before != 0 &&
2096c5866007SKeyur Desai 	    expires < ctx->expires_not_before) {
2097c5866007SKeyur Desai 		expires = ctx->expires_not_before;
2098c5866007SKeyur Desai 	}
2099c5866007SKeyur Desai 
2100c5866007SKeyur Desai 	if (ctx->expires_not_after != 0 &&
2101c5866007SKeyur Desai 	    expires > ctx->expires_not_after) {
2102c5866007SKeyur Desai 		expires = ctx->expires_not_after;
2103c5866007SKeyur Desai 	}
2104c5866007SKeyur Desai 
2105c5866007SKeyur Desai 	ttl = expires - time(NULL);
2106c5866007SKeyur Desai 
2107c5866007SKeyur Desai 	if (ttl < 0) {
21080dcc7149Snw 		return (0);
2109c5866007SKeyur Desai 	}
2110c8e26105Sjp 	return (ttl);
2111c8e26105Sjp }
2112c8e26105Sjp 
21137a8a68f5SJulian Pullen boolean_t
2114c8e26105Sjp ad_disc_SubnetChanged(ad_disc_t ctx)
2115c8e26105Sjp {
2116c8e26105Sjp 	ad_subnet_t *subnets;
2117c8e26105Sjp 
2118c8e26105Sjp 	if (ctx->subnets_changed || ctx->subnets == NULL)
21197a8a68f5SJulian Pullen 		return (B_TRUE);
2120c8e26105Sjp 
2121c8e26105Sjp 	if ((subnets = find_subnets()) != NULL) {
2122c8e26105Sjp 		if (cmpsubnets(subnets, ctx->subnets) != 0)
21237a8a68f5SJulian Pullen 			ctx->subnets_changed = B_TRUE;
2124c8e26105Sjp 		free(subnets);
2125c8e26105Sjp 	}
2126c8e26105Sjp 
2127c8e26105Sjp 	return (ctx->subnets_changed);
2128c8e26105Sjp }
2129