xref: /illumos-gate/usr/src/lib/libadutils/common/addisc.c (revision bd42852645f5ef79a1729096abd7405b72e159f7)
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 /*
237a8a68f5SJulian Pullen  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24c8e26105Sjp  * Use is subject to license terms.
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 <net/if.h>
88c8e26105Sjp #include <sys/types.h>
89c8e26105Sjp #include <sys/socket.h>
90c8e26105Sjp #include <sys/sockio.h>
91c8e26105Sjp #include <netinet/in.h>
92c8e26105Sjp #include <netinet/in.h>
93c8e26105Sjp #include <arpa/inet.h>
94c8e26105Sjp #include <arpa/nameser.h>
95c8e26105Sjp #include <resolv.h>
96c8e26105Sjp #include <netdb.h>
97c8e26105Sjp #include <ctype.h>
98c8e26105Sjp #include <errno.h>
99c8e26105Sjp #include <ldap.h>
100c8e26105Sjp #include <sasl/sasl.h>
1014d61c878SJulian Pullen #include <sys/u8_textprep.h>
1027a8a68f5SJulian Pullen #include <syslog.h>
1037a8a68f5SJulian Pullen #include "adutils_impl.h"
104c8e26105Sjp #include "addisc.h"
105c8e26105Sjp 
106c8e26105Sjp 
1074d61c878SJulian Pullen enum ad_item_state {
1084d61c878SJulian Pullen 		AD_STATE_INVALID = 0,	/* The value is not valid */
1094d61c878SJulian Pullen 		AD_STATE_FIXED,		/* The value was fixed by caller */
1104d61c878SJulian Pullen 		AD_STATE_AUTO		/* The value is auto discovered */
1114d61c878SJulian Pullen 		};
1124d61c878SJulian Pullen 
1134d61c878SJulian Pullen enum ad_data_type {
1144d61c878SJulian Pullen 		AD_STRING = 123,
1154d61c878SJulian Pullen 		AD_DIRECTORY,
1164d61c878SJulian Pullen 		AD_DOMAINS_IN_FOREST,
1174d61c878SJulian Pullen 		AD_TRUSTED_DOMAINS
1184d61c878SJulian Pullen 		};
1194d61c878SJulian Pullen 
1204d61c878SJulian Pullen 
1214d61c878SJulian Pullen typedef struct ad_subnet {
1224d61c878SJulian Pullen 	char subnet[24];
1234d61c878SJulian Pullen } ad_subnet_t;
1244d61c878SJulian Pullen 
1254d61c878SJulian Pullen 
1264d61c878SJulian Pullen typedef struct ad_item {
1274d61c878SJulian Pullen 	enum ad_item_state	state;
1284d61c878SJulian Pullen 	enum ad_data_type	type;
1294d61c878SJulian Pullen 	void 			*value;
1304d61c878SJulian Pullen 	time_t 			ttl;
1314d61c878SJulian Pullen 	unsigned int 		version;	/* Version is only changed */
1324d61c878SJulian Pullen 						/* if the value changes */
1334d61c878SJulian Pullen #define	PARAM1		0
1344d61c878SJulian Pullen #define	PARAM2		1
1354d61c878SJulian Pullen 	int 		param_version[2];
1364d61c878SJulian Pullen 					/* These holds the version of */
1374d61c878SJulian Pullen 					/* dependents so that a dependent */
1384d61c878SJulian Pullen 					/* change can be detected */
1394d61c878SJulian Pullen } ad_item_t;
1404d61c878SJulian Pullen 
1414d61c878SJulian Pullen typedef struct ad_disc {
1424d61c878SJulian Pullen 	struct __res_state res_state;
1434d61c878SJulian Pullen 	int		res_ninitted;
1444d61c878SJulian Pullen 	ad_subnet_t	*subnets;
1457a8a68f5SJulian Pullen 	boolean_t	subnets_changed;
1464d61c878SJulian Pullen 	time_t		subnets_last_check;
1474d61c878SJulian Pullen 	ad_item_t	domain_name;		/* DNS hostname string */
1484d61c878SJulian Pullen 	ad_item_t	domain_controller;	/* Directory hostname and */
1494d61c878SJulian Pullen 						/* port array */
1504d61c878SJulian Pullen 	ad_item_t	site_name;		/* String */
1514d61c878SJulian Pullen 	ad_item_t	forest_name;		/* DNS forestname string */
1524d61c878SJulian Pullen 	ad_item_t	global_catalog;		/* Directory hostname and */
1534d61c878SJulian Pullen 						/* port array */
1544d61c878SJulian Pullen 	ad_item_t	domains_in_forest;	/* DNS domainname and SID */
1554d61c878SJulian Pullen 						/* array */
1564d61c878SJulian Pullen 	ad_item_t	trusted_domains;	/* DNS domainname and trust */
1574d61c878SJulian Pullen 						/* direction array */
1584d61c878SJulian Pullen 	/* Site specfic versions */
1594d61c878SJulian Pullen 	ad_item_t	site_domain_controller;	/* Directory hostname and */
1604d61c878SJulian Pullen 						/* port array */
1614d61c878SJulian Pullen 	ad_item_t	site_global_catalog;	/* Directory hostname and */
1624d61c878SJulian Pullen 						/* port array */
1634d61c878SJulian Pullen } ad_disc;
1644d61c878SJulian Pullen 
165c8e26105Sjp 
166c8e26105Sjp #define	DNS_MAX_NAME	NS_MAXDNAME
167c8e26105Sjp 
168c8e26105Sjp 
169c8e26105Sjp /* SRV RR names for various queries */
170c8e26105Sjp #define	LDAP_SRV_HEAD		"_ldap._tcp."
171c8e26105Sjp #define	SITE_SRV_MIDDLE		"%s._sites."
172c8e26105Sjp #define	GC_SRV_TAIL		"gc._msdcs"
173c8e26105Sjp #define	DC_SRV_TAIL		"dc._msdcs"
174c8e26105Sjp #define	ALL_GC_SRV_TAIL		"_gc._tcp"
175c8e26105Sjp #define	PDC_SRV			 "_ldap._tcp.pdc._msdcs.%s"
176c8e26105Sjp 
177c8e26105Sjp /* A RR name for all GCs -- last resort this works */
178c8e26105Sjp #define	GC_ALL_A_NAME_FSTR "gc._msdcs.%s."
179c8e26105Sjp 
180c8e26105Sjp 
1810dcc7149Snw /*
1820dcc7149Snw  * We try res_ninit() whenever we don't have one.  res_ninit() fails if
1830dcc7149Snw  * idmapd is running before the network is up!
1840dcc7149Snw  */
1850dcc7149Snw #define	DO_RES_NINIT(ctx)   if (!(ctx)->res_ninitted) \
1864d61c878SJulian Pullen 		(ctx)->res_ninitted = (res_ninit(&ctx->res_state) != -1)
187c8e26105Sjp 
188c8e26105Sjp #define	is_fixed(item)					\
1894d61c878SJulian Pullen 	((item)->state == AD_STATE_FIXED)
190c8e26105Sjp 
191c8e26105Sjp #define	is_changed(item, num, param) 			\
192c8e26105Sjp 	((item)->param_version[num] != (param)->version)
193c8e26105Sjp 
194c8e26105Sjp /*LINTLIBRARY*/
195c8e26105Sjp 
196c8e26105Sjp /*
197c8e26105Sjp  * Function definitions
198c8e26105Sjp  */
1994d61c878SJulian Pullen static ad_item_t *
2004d61c878SJulian Pullen validate_SiteName(ad_disc_t ctx);
201c8e26105Sjp 
202c8e26105Sjp 
203c8e26105Sjp 
204c8e26105Sjp static void
205c8e26105Sjp update_version(ad_item_t *item, int  num, ad_item_t *param)
206c8e26105Sjp {
207c8e26105Sjp 	item->param_version[num] = param->version;
208c8e26105Sjp }
209c8e26105Sjp 
210c8e26105Sjp 
2114d61c878SJulian Pullen 
2127a8a68f5SJulian Pullen static boolean_t
2134d61c878SJulian Pullen is_valid(ad_item_t *item)
214c8e26105Sjp {
2154d61c878SJulian Pullen 	if (item->value != NULL) {
2164d61c878SJulian Pullen 		if (item->state == AD_STATE_FIXED)
2177a8a68f5SJulian Pullen 			return (B_TRUE);
2184d61c878SJulian Pullen 		if (item->state == AD_STATE_AUTO &&
2194d61c878SJulian Pullen 		    (item->ttl == 0 || item->ttl > time(NULL)))
2207a8a68f5SJulian Pullen 			return (B_TRUE);
2214d61c878SJulian Pullen 	}
2227a8a68f5SJulian Pullen 	return (B_FALSE);
223c8e26105Sjp }
224c8e26105Sjp 
225c8e26105Sjp 
226c8e26105Sjp static void
2274d61c878SJulian Pullen update_item(ad_item_t *item, void *value, enum ad_item_state state,
228c8e26105Sjp 		uint32_t ttl)
229c8e26105Sjp {
2304d61c878SJulian Pullen 	if (item->value != NULL && value != NULL) {
2314d61c878SJulian Pullen 		if ((item->type == AD_STRING &&
2324d61c878SJulian Pullen 		    strcmp(item->value, value) != 0) ||
2334d61c878SJulian Pullen 		    (item->type == AD_DIRECTORY &&
2344d61c878SJulian Pullen 		    ad_disc_compare_ds(item->value, value) != 0)||
2354d61c878SJulian Pullen 		    (item->type == AD_DOMAINS_IN_FOREST &&
2364d61c878SJulian Pullen 		    ad_disc_compare_domainsinforest(item->value, value) != 0) ||
2374d61c878SJulian Pullen 		    (item->type == AD_TRUSTED_DOMAINS &&
2384d61c878SJulian Pullen 		    ad_disc_compare_trusteddomains(item->value, value) != 0))
239c8e26105Sjp 			item->version++;
2404d61c878SJulian Pullen 	} else if (item->value != value)
241c8e26105Sjp 		item->version++;
242c8e26105Sjp 
2434d61c878SJulian Pullen 	if (item->value != NULL)
2444d61c878SJulian Pullen 		free(item->value);
245c8e26105Sjp 
2464d61c878SJulian Pullen 	item->value = value;
2474d61c878SJulian Pullen 	item->state = state;
248c8e26105Sjp 
249c8e26105Sjp 	if (ttl == 0)
250c8e26105Sjp 		item->ttl = 0;
251c8e26105Sjp 	else
252c8e26105Sjp 		item->ttl = time(NULL) + ttl;
253c8e26105Sjp }
254c8e26105Sjp 
255c8e26105Sjp 
2564d61c878SJulian Pullen /* Compare DS lists */
2574d61c878SJulian Pullen int
2584d61c878SJulian Pullen ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2)
259c8e26105Sjp {
2607a8a68f5SJulian Pullen 	int		i, j;
2617a8a68f5SJulian Pullen 	int		num_ds1;
2627a8a68f5SJulian Pullen 	int		num_ds2;
2637a8a68f5SJulian Pullen 	boolean_t	match;
264c8e26105Sjp 
2654d61c878SJulian Pullen 	for (i = 0; ds1[i].host[0] != '\0'; i++)
2664d61c878SJulian Pullen 		continue;
2674d61c878SJulian Pullen 	num_ds1 = i;
2684d61c878SJulian Pullen 	for (j = 0; ds2[j].host[0] != '\0'; j++)
2694d61c878SJulian Pullen 		continue;
2704d61c878SJulian Pullen 	num_ds2 = j;
2714d61c878SJulian Pullen 	if (num_ds1 != num_ds2)
2724d61c878SJulian Pullen 		return (1);
273c8e26105Sjp 
2744d61c878SJulian Pullen 	for (i = 0; i < num_ds1; i++) {
2757a8a68f5SJulian Pullen 		match = B_FALSE;
2764d61c878SJulian Pullen 		for (j = 0; j < num_ds2; j++) {
277928e1f97SJordan Brown 			if (strcmp(ds1[i].host, ds2[j].host) == 0 &&
278928e1f97SJordan Brown 			    ds1[i].port == ds2[j].port) {
2797a8a68f5SJulian Pullen 				match = B_TRUE;
2804d61c878SJulian Pullen 				break;
2814d61c878SJulian Pullen 			}
2824d61c878SJulian Pullen 		}
2834d61c878SJulian Pullen 		if (!match)
2844d61c878SJulian Pullen 			return (1);
2854d61c878SJulian Pullen 	}
2864d61c878SJulian Pullen 	return (0);
2874d61c878SJulian Pullen }
288c8e26105Sjp 
2894d61c878SJulian Pullen 
2904d61c878SJulian Pullen /* Copy a list of DSs */
2914d61c878SJulian Pullen static idmap_ad_disc_ds_t *
2924d61c878SJulian Pullen ds_dup(const idmap_ad_disc_ds_t *srv)
2934d61c878SJulian Pullen {
2944d61c878SJulian Pullen 	int	i;
2954d61c878SJulian Pullen 	int	size;
2964d61c878SJulian Pullen 	idmap_ad_disc_ds_t *new = NULL;
2974d61c878SJulian Pullen 
2984d61c878SJulian Pullen 	for (i = 0; srv[i].host[0] != '\0'; i++)
2994d61c878SJulian Pullen 		continue;
3004d61c878SJulian Pullen 
3014d61c878SJulian Pullen 	size = (i + 1) * sizeof (idmap_ad_disc_ds_t);
3024d61c878SJulian Pullen 	new = malloc(size);
3034d61c878SJulian Pullen 	if (new != NULL)
3044d61c878SJulian Pullen 		memcpy(new, srv, size);
3054d61c878SJulian Pullen 	return (new);
306c8e26105Sjp }
307c8e26105Sjp 
308c8e26105Sjp 
3094d61c878SJulian Pullen int
3104d61c878SJulian Pullen ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1,
3114d61c878SJulian Pullen 			ad_disc_trusteddomains_t *td2)
312c8e26105Sjp {
3137a8a68f5SJulian Pullen 	int		i, j;
3147a8a68f5SJulian Pullen 	int		num_td1;
3157a8a68f5SJulian Pullen 	int		num_td2;
3167a8a68f5SJulian Pullen 	boolean_t	match;
317c8e26105Sjp 
3184d61c878SJulian Pullen 	for (i = 0; td1[i].domain[0] != '\0'; i++)
3194d61c878SJulian Pullen 		continue;
3204d61c878SJulian Pullen 	num_td1 = i;
321c8e26105Sjp 
3224d61c878SJulian Pullen 	for (j = 0; td2[j].domain[0] != '\0'; j++)
3234d61c878SJulian Pullen 		continue;
3244d61c878SJulian Pullen 	num_td2 = j;
3254d61c878SJulian Pullen 
3264d61c878SJulian Pullen 	if (num_td1 != num_td2)
3274d61c878SJulian Pullen 		return (1);
3284d61c878SJulian Pullen 
3294d61c878SJulian Pullen 	for (i = 0; i < num_td1; i++) {
3307a8a68f5SJulian Pullen 		match = B_FALSE;
3314d61c878SJulian Pullen 		for (j = 0; j < num_td2; j++) {
3321fcced4cSJordan Brown 			if (domain_eq(td1[i].domain, td2[j].domain)) {
3337a8a68f5SJulian Pullen 				match = B_TRUE;
3344d61c878SJulian Pullen 				break;
3354d61c878SJulian Pullen 			}
3364d61c878SJulian Pullen 		}
3374d61c878SJulian Pullen 		if (!match)
3384d61c878SJulian Pullen 			return (1);
3394d61c878SJulian Pullen 	}
3404d61c878SJulian Pullen 	return (0);
3414d61c878SJulian Pullen }
3424d61c878SJulian Pullen 
3434d61c878SJulian Pullen 
3444d61c878SJulian Pullen 
3454d61c878SJulian Pullen /* Copy a list of Trusted Domains */
3464d61c878SJulian Pullen static ad_disc_trusteddomains_t *
3474d61c878SJulian Pullen td_dup(const ad_disc_trusteddomains_t *td)
3484d61c878SJulian Pullen {
3494d61c878SJulian Pullen 	int	i;
3504d61c878SJulian Pullen 	int	size;
3514d61c878SJulian Pullen 	ad_disc_trusteddomains_t *new = NULL;
3524d61c878SJulian Pullen 
3534d61c878SJulian Pullen 	for (i = 0; td[i].domain[0] != '\0'; i++)
3544d61c878SJulian Pullen 		continue;
3554d61c878SJulian Pullen 
3564d61c878SJulian Pullen 	size = (i + 1) * sizeof (ad_disc_trusteddomains_t);
3574d61c878SJulian Pullen 	new = malloc(size);
3584d61c878SJulian Pullen 	if (new != NULL)
3594d61c878SJulian Pullen 		memcpy(new, td, size);
3604d61c878SJulian Pullen 	return (new);
3614d61c878SJulian Pullen }
3624d61c878SJulian Pullen 
3634d61c878SJulian Pullen 
3644d61c878SJulian Pullen 
3654d61c878SJulian Pullen int
3664d61c878SJulian Pullen ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *df1,
3674d61c878SJulian Pullen 			ad_disc_domainsinforest_t *df2)
3684d61c878SJulian Pullen {
3697a8a68f5SJulian Pullen 	int		i, j;
3707a8a68f5SJulian Pullen 	int		num_df1;
3717a8a68f5SJulian Pullen 	int		num_df2;
3727a8a68f5SJulian Pullen 	boolean_t	match;
3734d61c878SJulian Pullen 
3744d61c878SJulian Pullen 	for (i = 0; df1[i].domain[0] != '\0'; i++)
3754d61c878SJulian Pullen 		continue;
3764d61c878SJulian Pullen 	num_df1 = i;
3774d61c878SJulian Pullen 
3784d61c878SJulian Pullen 	for (j = 0; df2[j].domain[0] != '\0'; j++)
3794d61c878SJulian Pullen 		continue;
3804d61c878SJulian Pullen 	num_df2 = j;
3814d61c878SJulian Pullen 
3824d61c878SJulian Pullen 	if (num_df1 != num_df2)
3834d61c878SJulian Pullen 		return (1);
3844d61c878SJulian Pullen 
3854d61c878SJulian Pullen 	for (i = 0; i < num_df1; i++) {
3867a8a68f5SJulian Pullen 		match = B_FALSE;
3874d61c878SJulian Pullen 		for (j = 0; j < num_df2; j++) {
3881fcced4cSJordan Brown 			if (domain_eq(df1[i].domain, df2[j].domain) &&
389928e1f97SJordan Brown 			    strcmp(df1[i].sid, df2[j].sid) == 0) {
3907a8a68f5SJulian Pullen 				match = B_TRUE;
3914d61c878SJulian Pullen 				break;
3924d61c878SJulian Pullen 			}
3934d61c878SJulian Pullen 		}
3944d61c878SJulian Pullen 		if (!match)
3954d61c878SJulian Pullen 			return (1);
3964d61c878SJulian Pullen 	}
3974d61c878SJulian Pullen 	return (0);
3984d61c878SJulian Pullen }
399c8e26105Sjp 
4004d61c878SJulian Pullen 
4014d61c878SJulian Pullen 
4024d61c878SJulian Pullen /* Copy a list of Trusted Domains */
4034d61c878SJulian Pullen static ad_disc_domainsinforest_t *
4044d61c878SJulian Pullen df_dup(const ad_disc_domainsinforest_t *df)
4054d61c878SJulian Pullen {
4064d61c878SJulian Pullen 	int	i;
4074d61c878SJulian Pullen 	int	size;
4084d61c878SJulian Pullen 	ad_disc_domainsinforest_t *new = NULL;
4094d61c878SJulian Pullen 
4104d61c878SJulian Pullen 	for (i = 0; df[i].domain[0] != '\0'; i++)
4114d61c878SJulian Pullen 		continue;
4124d61c878SJulian Pullen 
4134d61c878SJulian Pullen 	size = (i + 1) * sizeof (ad_disc_domainsinforest_t);
4144d61c878SJulian Pullen 	new = malloc(size);
4154d61c878SJulian Pullen 	if (new != NULL)
4164d61c878SJulian Pullen 		memcpy(new, df, size);
4174d61c878SJulian Pullen 	return (new);
418c8e26105Sjp }
419c8e26105Sjp 
420c8e26105Sjp 
421c8e26105Sjp 
4224d61c878SJulian Pullen 
4234d61c878SJulian Pullen 
424c8e26105Sjp /*
425c8e26105Sjp  * Returns an array of IPv4 address/prefix length
426c8e26105Sjp  * The last subnet is NULL
427c8e26105Sjp  */
428c8e26105Sjp static ad_subnet_t *
429c8e26105Sjp find_subnets()
430c8e26105Sjp {
431c8e26105Sjp 	int		sock, n, i;
432c8e26105Sjp 	struct lifconf	lifc;
433c8e26105Sjp 	struct lifreq	lifr, *lifrp;
434c8e26105Sjp 	struct lifnum	lifn;
435c8e26105Sjp 	uint32_t	prefix_len;
436c8e26105Sjp 	char		*s;
437c8e26105Sjp 	ad_subnet_t	*results;
438c8e26105Sjp 
439c8e26105Sjp 	lifrp = &lifr;
440c8e26105Sjp 
441c8e26105Sjp 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
4427a8a68f5SJulian Pullen 		logger(LOG_ERR, "Failed to open IPv4 socket for "
44371590c90Snw 		    "listing network interfaces (%s)", strerror(errno));
444c8e26105Sjp 		return (NULL);
445c8e26105Sjp 	}
446c8e26105Sjp 
447c8e26105Sjp 	lifn.lifn_family = AF_INET;
448c8e26105Sjp 	lifn.lifn_flags = 0;
449c8e26105Sjp 	if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
4507a8a68f5SJulian Pullen 		logger(LOG_ERR,
45171590c90Snw 		    "Failed to find the number of network interfaces (%s)",
45271590c90Snw 		    strerror(errno));
453c8e26105Sjp 		close(sock);
454c8e26105Sjp 		return (NULL);
455c8e26105Sjp 	}
456c8e26105Sjp 
457c8e26105Sjp 	if (lifn.lifn_count < 1) {
4587a8a68f5SJulian Pullen 		logger(LOG_ERR, "No IPv4 network interfaces found");
459c8e26105Sjp 		close(sock);
460c8e26105Sjp 		return (NULL);
461c8e26105Sjp 	}
462c8e26105Sjp 
463c8e26105Sjp 	lifc.lifc_family = AF_INET;
464c8e26105Sjp 	lifc.lifc_flags = 0;
465c8e26105Sjp 	lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
466c8e26105Sjp 	lifc.lifc_buf = malloc(lifc.lifc_len);
467c8e26105Sjp 
468c8e26105Sjp 	if (lifc.lifc_buf == NULL) {
4697a8a68f5SJulian Pullen 		logger(LOG_ERR, "Out of memory");
470c8e26105Sjp 		close(sock);
471c8e26105Sjp 		return (NULL);
472c8e26105Sjp 	}
473c8e26105Sjp 
474c8e26105Sjp 	if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) {
4757a8a68f5SJulian Pullen 		logger(LOG_ERR, "Failed to list network interfaces (%s)",
47671590c90Snw 		    strerror(errno));
477c8e26105Sjp 		free(lifc.lifc_buf);
478c8e26105Sjp 		close(sock);
479c8e26105Sjp 		return (NULL);
480c8e26105Sjp 	}
481c8e26105Sjp 
482c8e26105Sjp 	n = lifc.lifc_len / (int)sizeof (struct lifreq);
483c8e26105Sjp 
484c8e26105Sjp 	if ((results = calloc(n + 1, sizeof (ad_subnet_t))) == NULL) {
485c8e26105Sjp 		free(lifc.lifc_buf);
486c8e26105Sjp 		close(sock);
487c8e26105Sjp 		return (NULL);
488c8e26105Sjp 	}
489c8e26105Sjp 
490c8e26105Sjp 	for (i = 0, lifrp = lifc.lifc_req; i < n; i++, lifrp++) {
491c8e26105Sjp 		if (ioctl(sock, SIOCGLIFFLAGS, lifrp) < 0)
492c8e26105Sjp 			continue;
493c8e26105Sjp 
494c8e26105Sjp 		if ((lifrp->lifr_flags & IFF_UP) == 0)
495c8e26105Sjp 			continue;
496c8e26105Sjp 
497c8e26105Sjp 		if (ioctl(sock, SIOCGLIFSUBNET, lifrp) < 0)
498c8e26105Sjp 			continue;
499c8e26105Sjp 
500c8e26105Sjp 		prefix_len = lifrp->lifr_addrlen;
501c8e26105Sjp 
502c8e26105Sjp 		s = inet_ntoa(((struct sockaddr_in *)
503c8e26105Sjp 		    &lifrp->lifr_addr)->sin_addr);
504c8e26105Sjp 
505c8e26105Sjp 		(void) snprintf(results[i].subnet, sizeof (ad_subnet_t),
506c8e26105Sjp 		    "%s/%d", s, prefix_len);
507c8e26105Sjp 	}
508c8e26105Sjp 
509c8e26105Sjp 	free(lifc.lifc_buf);
510c8e26105Sjp 	close(sock);
511c8e26105Sjp 
512c8e26105Sjp 	return (results);
513c8e26105Sjp }
514c8e26105Sjp 
515c8e26105Sjp static int
516c8e26105Sjp cmpsubnets(ad_subnet_t *subnets1, ad_subnet_t *subnets2)
517c8e26105Sjp {
518c8e26105Sjp 	int num_subnets1;
519c8e26105Sjp 	int num_subnets2;
5207a8a68f5SJulian Pullen 	boolean_t matched;
521c8e26105Sjp 	int i, j;
522c8e26105Sjp 
523c8e26105Sjp 	for (i = 0; subnets1[i].subnet[0] != '\0'; i++)
5244d61c878SJulian Pullen 		continue;
525c8e26105Sjp 	num_subnets1 = i;
526c8e26105Sjp 
527c8e26105Sjp 	for (i = 0; subnets2[i].subnet[0] != '\0'; i++)
5284d61c878SJulian Pullen 		continue;
529c8e26105Sjp 	num_subnets2 = i;
530c8e26105Sjp 
531c8e26105Sjp 	if (num_subnets1 != num_subnets2)
532c8e26105Sjp 		return (1);
533c8e26105Sjp 
534c8e26105Sjp 	for (i = 0;  i < num_subnets1; i++) {
5357a8a68f5SJulian Pullen 		matched = B_FALSE;
536c8e26105Sjp 		for (j = 0; j < num_subnets2; j++) {
537c8e26105Sjp 			if (strcmp(subnets1[i].subnet,
538c8e26105Sjp 			    subnets2[j].subnet) == 0) {
5397a8a68f5SJulian Pullen 				matched = B_TRUE;
540c8e26105Sjp 				break;
541c8e26105Sjp 			}
542c8e26105Sjp 		}
543c8e26105Sjp 		if (!matched)
544c8e26105Sjp 			return (1);
545c8e26105Sjp 	}
546c8e26105Sjp 	return (0);
547c8e26105Sjp }
548c8e26105Sjp 
549c8e26105Sjp 
550c8e26105Sjp 
551c8e26105Sjp 
552c8e26105Sjp /* Convert a DN's DC components into a DNS domainname */
5537a8a68f5SJulian Pullen char *
554c8e26105Sjp DN_to_DNS(const char *dn_name)
555c8e26105Sjp {
556c8e26105Sjp 	char	dns[DNS_MAX_NAME];
557c8e26105Sjp 	char	*dns_name;
558c8e26105Sjp 	int	i, j;
559c8e26105Sjp 	int	num = 0;
560c8e26105Sjp 
561c8e26105Sjp 	j = 0;
562c8e26105Sjp 	i = 0;
563c8e26105Sjp 
5644d61c878SJulian Pullen 	if (dn_name == NULL)
5654d61c878SJulian Pullen 		return (NULL);
566c8e26105Sjp 	/*
567c8e26105Sjp 	 * Find all DC=<value> and form DNS name of the
568c8e26105Sjp 	 * form <value1>.<value2>...
569c8e26105Sjp 	 */
5704d61c878SJulian Pullen 	while (dn_name[i] != '\0') {
571c8e26105Sjp 		if (strncasecmp(&dn_name[i], "DC=", 3) == 0) {
572c8e26105Sjp 			i += 3;
5734d61c878SJulian Pullen 			if (dn_name[i] != '\0' && num > 0)
574c8e26105Sjp 				dns[j++] = '.';
5754d61c878SJulian Pullen 			while (dn_name[i] != '\0' &&
576c8e26105Sjp 			    dn_name[i] != ',' && dn_name[i] != '+')
577c8e26105Sjp 				dns[j++] = dn_name[i++];
578c8e26105Sjp 			num++;
579c8e26105Sjp 		} else {
580c8e26105Sjp 			/* Skip attr=value as it is not DC= */
5814d61c878SJulian Pullen 			while (dn_name[i] != '\0' &&
582c8e26105Sjp 			    dn_name[i] != ',' && dn_name[i] != '+')
583c8e26105Sjp 				i++;
584c8e26105Sjp 		}
585c8e26105Sjp 		/* Skip over separator ','  or '+' */
5864d61c878SJulian Pullen 		if (dn_name[i] != '\0') i++;
587c8e26105Sjp 	}
588c8e26105Sjp 	dns[j] = '\0';
589c8e26105Sjp 	dns_name = malloc(j + 1);
590c8e26105Sjp 	if (dns_name != NULL)
591c8e26105Sjp 		(void) strlcpy(dns_name, dns, j + 1);
592c8e26105Sjp 	return (dns_name);
593c8e26105Sjp }
594c8e26105Sjp 
595c8e26105Sjp 
596c8e26105Sjp /* Format the DN of an AD LDAP subnet object for some subnet */
597c8e26105Sjp static char *
598c8e26105Sjp subnet_to_DN(const char *subnet, const char *baseDN)
599c8e26105Sjp {
600c8e26105Sjp 	char *result;
601c8e26105Sjp 	int len;
602c8e26105Sjp 
603c8e26105Sjp 	len = snprintf(NULL, 0,
604c8e26105Sjp 	    "CN=%s,CN=Subnets,CN=Sites,%s",
605c8e26105Sjp 	    subnet, baseDN) + 1;
606c8e26105Sjp 
607c8e26105Sjp 	result = malloc(len);
608c8e26105Sjp 	if (result != NULL)
609c8e26105Sjp 		(void) snprintf(result, len,
610c8e26105Sjp 		    "CN=%s,CN=Subnets,CN=Sites,%s",
611c8e26105Sjp 		    subnet, baseDN);
612c8e26105Sjp 	return (result);
613c8e26105Sjp }
614c8e26105Sjp 
615c8e26105Sjp 
616c8e26105Sjp /* Make a list of subnet object DNs from a list of subnets */
617c8e26105Sjp static char **
618c8e26105Sjp subnets_to_DNs(ad_subnet_t *subnets, const char *base_dn)
619c8e26105Sjp {
620c8e26105Sjp 	char **results;
621c8e26105Sjp 	int i, j;
622c8e26105Sjp 
623c8e26105Sjp 	for (i = 0; subnets[i].subnet[0] != '\0'; i++)
6244d61c878SJulian Pullen 		continue;
625c8e26105Sjp 
626c8e26105Sjp 	results = calloc(i + 1, sizeof (char *));
627c8e26105Sjp 	if (results == NULL)
628c8e26105Sjp 		return (NULL);
629c8e26105Sjp 
630c8e26105Sjp 	for (i = 0; subnets[i].subnet[0] != '\0'; i++) {
631c8e26105Sjp 		if ((results[i] = subnet_to_DN(subnets[i].subnet, base_dn))
632c8e26105Sjp 		    == NULL) {
633c8e26105Sjp 			for (j = 0; j < i; j++)
634c8e26105Sjp 				free(results[j]);
635c8e26105Sjp 			free(results);
636c8e26105Sjp 			return (NULL);
637c8e26105Sjp 		}
638c8e26105Sjp 	}
639c8e26105Sjp 
640c8e26105Sjp 	return (results);
641c8e26105Sjp }
642c8e26105Sjp 
643c8e26105Sjp 
644c8e26105Sjp /* Compare SRC RRs; used with qsort() */
645c8e26105Sjp static int
646479ac375Sdm srvcmp(idmap_ad_disc_ds_t *s1, idmap_ad_disc_ds_t *s2)
647c8e26105Sjp {
648c8e26105Sjp 	if (s1->priority < s2->priority)
649c8e26105Sjp 		return (1);
650c8e26105Sjp 	else if (s1->priority > s2->priority)
651c8e26105Sjp 		return (-1);
652c8e26105Sjp 
653c8e26105Sjp 	if (s1->weight < s2->weight)
654c8e26105Sjp 		return (1);
655c8e26105Sjp 	else if (s1->weight > s2->weight)
656c8e26105Sjp 		return (-1);
657c8e26105Sjp 
658c8e26105Sjp 	return (0);
659c8e26105Sjp }
660c8e26105Sjp 
661c8e26105Sjp 
662c8e26105Sjp /*
663c8e26105Sjp  * Query or search the SRV RRs for a given name.
664c8e26105Sjp  *
665c8e26105Sjp  * If name == NULL then search (as in res_nsearch(3RESOLV), honoring any
666c8e26105Sjp  * search list/option), else query (as in res_nquery(3RESOLV)).
667c8e26105Sjp  *
668c8e26105Sjp  * The output TTL will be the one of the SRV RR with the lowest TTL.
669c8e26105Sjp  */
670479ac375Sdm idmap_ad_disc_ds_t *
671c8e26105Sjp srv_query(res_state state, const char *svc_name, const char *dname,
672c8e26105Sjp 		char **rrname, uint32_t *ttl)
673c8e26105Sjp {
674479ac375Sdm 	idmap_ad_disc_ds_t *srv;
675479ac375Sdm 	idmap_ad_disc_ds_t *srv_res;
676c8e26105Sjp 	union {
677c8e26105Sjp 		HEADER hdr;
678c8e26105Sjp 		uchar_t buf[NS_MAXMSG];
679c8e26105Sjp 	} msg;
680c8e26105Sjp 	int len, cnt, qdcount, ancount;
681c8e26105Sjp 	uchar_t *ptr, *eom;
682c8e26105Sjp 	uchar_t *end;
683c8e26105Sjp 	uint16_t type;
684c8e26105Sjp 	/* LINTED  E_FUNC_SET_NOT_USED */
685c8e26105Sjp 	uint16_t class;
686c8e26105Sjp 	uint32_t rttl;
687c8e26105Sjp 	uint16_t size;
688c8e26105Sjp 	char namebuf[NS_MAXDNAME];
689c8e26105Sjp 
6900dcc7149Snw 	if (state == NULL)
6910dcc7149Snw 		return (NULL);
6920dcc7149Snw 
693c8e26105Sjp 	/* Set negative result TTL */
694c8e26105Sjp 	*ttl = 5 * 60;
695c8e26105Sjp 
696c8e26105Sjp 	/* 1. query necessary resource records */
697c8e26105Sjp 
698c8e26105Sjp 	/* Search, querydomain or query */
699c8e26105Sjp 	if (rrname != NULL) {
700c8e26105Sjp 		*rrname = NULL;
701c8e26105Sjp 		len = res_nsearch(state, svc_name, C_IN, T_SRV,
702c8e26105Sjp 		    msg.buf, sizeof (msg.buf));
7037a8a68f5SJulian Pullen 		logger(LOG_DEBUG, "Searching DNS for SRV RRs named '%s'",
7047a8a68f5SJulian Pullen 		    svc_name);
7057a8a68f5SJulian Pullen 		if (len < 0) {
7067a8a68f5SJulian Pullen 			logger(LOG_DEBUG, "DNS search for '%s' failed (%s)",
7077a8a68f5SJulian Pullen 			    svc_name, hstrerror(state->res_h_errno));
7087a8a68f5SJulian Pullen 			return (NULL);
7097a8a68f5SJulian Pullen 		}
710c8e26105Sjp 	} else if (dname != NULL) {
7117a8a68f5SJulian Pullen 		logger(LOG_DEBUG,
7127a8a68f5SJulian Pullen 		    "Querying DNS for SRV RRs named '%s' for '%s' ",
7137a8a68f5SJulian Pullen 		    svc_name, dname);
714c8e26105Sjp 
715e3f2c991SKeyur Desai 		len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV,
716e3f2c991SKeyur Desai 		    msg.buf, sizeof (msg.buf));
717e3f2c991SKeyur Desai 
7187a8a68f5SJulian Pullen 		if (len < 0) {
7197a8a68f5SJulian Pullen 			logger(LOG_DEBUG,
7207a8a68f5SJulian Pullen 			    "DNS query for '%s' for '%s' failed (%s)",
7217a8a68f5SJulian Pullen 			    svc_name, dname, hstrerror(state->res_h_errno));
7227a8a68f5SJulian Pullen 			return (NULL);
7237a8a68f5SJulian Pullen 		}
724c8e26105Sjp 	}
7257a8a68f5SJulian Pullen 
726c8e26105Sjp 	if (len > sizeof (msg.buf)) {
7277a8a68f5SJulian Pullen 		logger(LOG_ERR, "DNS query %ib message doesn't fit"
728c8e26105Sjp 		    " into %ib buffer",
72971590c90Snw 		    len, sizeof (msg.buf));
730c8e26105Sjp 		return (NULL);
731c8e26105Sjp 	}
732c8e26105Sjp 
733c8e26105Sjp 	/* 2. parse the reply, skip header and question sections */
734c8e26105Sjp 
735c8e26105Sjp 	ptr = msg.buf + sizeof (msg.hdr);
736c8e26105Sjp 	eom = msg.buf + len;
737c8e26105Sjp 	qdcount = ntohs(msg.hdr.qdcount);
738c8e26105Sjp 	ancount = ntohs(msg.hdr.ancount);
739c8e26105Sjp 
740c8e26105Sjp 	for (cnt = qdcount; cnt > 0; --cnt) {
741c8e26105Sjp 		if ((len = dn_skipname(ptr, eom)) < 0) {
7427a8a68f5SJulian Pullen 			logger(LOG_ERR, "DNS query invalid message format");
743c8e26105Sjp 			return (NULL);
744c8e26105Sjp 		}
745c8e26105Sjp 		ptr += len + QFIXEDSZ;
746c8e26105Sjp 	}
747c8e26105Sjp 
748c8e26105Sjp 	/* 3. walk through the answer section */
749c8e26105Sjp 
750479ac375Sdm 	srv_res = calloc(ancount + 1, sizeof (idmap_ad_disc_ds_t));
751c8e26105Sjp 	*ttl = (uint32_t)-1;
752c8e26105Sjp 
753c8e26105Sjp 	for (srv = srv_res, cnt = ancount;
754c8e26105Sjp 	    cnt > 0; --cnt, srv++) {
755c8e26105Sjp 
756c8e26105Sjp 		len = dn_expand(msg.buf, eom, ptr, namebuf,
757c8e26105Sjp 		    sizeof (namebuf));
758c8e26105Sjp 		if (len < 0) {
7597a8a68f5SJulian Pullen 			logger(LOG_ERR, "DNS query invalid message format");
760c8e26105Sjp 			return (NULL);
761c8e26105Sjp 		}
762c8e26105Sjp 		if (rrname != NULL && *rrname == NULL)
763c8e26105Sjp 			*rrname = strdup(namebuf);
764c8e26105Sjp 		ptr += len;
765c8e26105Sjp 		NS_GET16(type, ptr);
766c8e26105Sjp 		NS_GET16(class, ptr);
767c8e26105Sjp 		NS_GET32(rttl, ptr);
768c8e26105Sjp 		NS_GET16(size, ptr);
769c8e26105Sjp 		if ((end = ptr + size) > eom) {
7707a8a68f5SJulian Pullen 			logger(LOG_ERR, "DNS query invalid message format");
771c8e26105Sjp 			return (NULL);
772c8e26105Sjp 		}
773c8e26105Sjp 
774c8e26105Sjp 		if (type != T_SRV) {
775c8e26105Sjp 			ptr = end;
776c8e26105Sjp 			continue;
777c8e26105Sjp 		}
778c8e26105Sjp 
779c8e26105Sjp 		NS_GET16(srv->priority, ptr);
780c8e26105Sjp 		NS_GET16(srv->weight, ptr);
781c8e26105Sjp 		NS_GET16(srv->port, ptr);
782c8e26105Sjp 		len = dn_expand(msg.buf, eom, ptr, srv->host,
783c8e26105Sjp 		    sizeof (srv->host));
784c8e26105Sjp 		if (len < 0) {
7857a8a68f5SJulian Pullen 			logger(LOG_ERR, "DNS query invalid SRV record");
786c8e26105Sjp 			return (NULL);
787c8e26105Sjp 		}
788c8e26105Sjp 
789c8e26105Sjp 		if (rttl < *ttl)
790c8e26105Sjp 			*ttl = rttl;
791c8e26105Sjp 
7927a8a68f5SJulian Pullen 		logger(LOG_DEBUG, "Found %s %d IN SRV [%d][%d] %s:%d",
79371590c90Snw 		    namebuf, rttl, srv->priority, srv->weight, srv->host,
79471590c90Snw 		    srv->port);
795c8e26105Sjp 
796c8e26105Sjp 		/* 3. move ptr to the end of current record */
797c8e26105Sjp 
798c8e26105Sjp 		ptr = end;
799c8e26105Sjp 	}
800c8e26105Sjp 
801c8e26105Sjp 	if (ancount > 1)
802c8e26105Sjp 		qsort(srv_res, ancount, sizeof (*srv_res),
803c8e26105Sjp 		    (int (*)(const void *, const void *))srvcmp);
804c8e26105Sjp 
805c8e26105Sjp 	return (srv_res);
806c8e26105Sjp }
807c8e26105Sjp 
808c8e26105Sjp 
8094d61c878SJulian Pullen /*
8104d61c878SJulian Pullen  * A utility function to bind to a Directory server
8114d61c878SJulian Pullen  */
8124d61c878SJulian Pullen 
8134d61c878SJulian Pullen static LDAP*
8144d61c878SJulian Pullen ldap_lookup_init(idmap_ad_disc_ds_t *ds)
8154d61c878SJulian Pullen {
8164d61c878SJulian Pullen 	int 	i;
8174d61c878SJulian Pullen 	int	rc, ldversion;
8184d61c878SJulian Pullen 	int	zero = 0;
8194d61c878SJulian Pullen 	int 	timeoutms = 5 * 1000;
8204d61c878SJulian Pullen 	char 	*saslmech = "GSSAPI";
8214d61c878SJulian Pullen 	uint32_t saslflags = LDAP_SASL_INTERACTIVE;
8224d61c878SJulian Pullen 	LDAP 	*ld = NULL;
8234d61c878SJulian Pullen 
8244d61c878SJulian Pullen 	for (i = 0; ds[i].host[0] != '\0'; i++) {
8254d61c878SJulian Pullen 		ld = ldap_init(ds[i].host, ds[i].port);
8264d61c878SJulian Pullen 		if (ld == NULL) {
8277a8a68f5SJulian Pullen 			logger(LOG_DEBUG, "Couldn't connect to "
8284d61c878SJulian Pullen 			    "AD DC %s:%d (%s)",
8294d61c878SJulian Pullen 			    ds[i].host, ds[i].port,
8304d61c878SJulian Pullen 			    strerror(errno));
8314d61c878SJulian Pullen 			continue;
8324d61c878SJulian Pullen 		}
8334d61c878SJulian Pullen 
8344d61c878SJulian Pullen 		ldversion = LDAP_VERSION3;
8354d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
8364d61c878SJulian Pullen 		    &ldversion);
8374d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_REFERRALS,
8384d61c878SJulian Pullen 		    LDAP_OPT_OFF);
8394d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
8404d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
8414d61c878SJulian Pullen 		/* setup TCP/IP connect timeout */
8424d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
8434d61c878SJulian Pullen 		    &timeoutms);
8444d61c878SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_RESTART,
8454d61c878SJulian Pullen 		    LDAP_OPT_ON);
8464d61c878SJulian Pullen 
847*bd428526SJulian Pullen 		rc = adutils_set_thread_functions(ld);
848*bd428526SJulian Pullen 		if (rc != LDAP_SUCCESS) {
849*bd428526SJulian Pullen 			/* Error has already been logged */
850*bd428526SJulian Pullen 			(void) ldap_unbind(ld);
851*bd428526SJulian Pullen 			ld = NULL;
852*bd428526SJulian Pullen 			continue;
853*bd428526SJulian Pullen 		}
854*bd428526SJulian Pullen 
8554d61c878SJulian Pullen 		rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */,
8564d61c878SJulian Pullen 		    saslmech, NULL, NULL, saslflags, &saslcallback,
8574d61c878SJulian Pullen 		    NULL /* defaults */);
8584d61c878SJulian Pullen 		if (rc == LDAP_SUCCESS)
8594d61c878SJulian Pullen 			break;
8604d61c878SJulian Pullen 
8617a8a68f5SJulian Pullen 		logger(LOG_INFO, "LDAP SASL bind to %s:%d failed (%s)",
8624d61c878SJulian Pullen 		    ds[i].host, ds[i].port, ldap_err2string(rc));
8634d61c878SJulian Pullen 		(void) ldap_unbind(ld);
8644d61c878SJulian Pullen 		ld = NULL;
8654d61c878SJulian Pullen 	}
8664d61c878SJulian Pullen 	return (ld);
8674d61c878SJulian Pullen }
8684d61c878SJulian Pullen 
869c8e26105Sjp 
8707a8a68f5SJulian Pullen 
871c8e26105Sjp /*
872c8e26105Sjp  * A utility function to get the value of some attribute of one of one
873c8e26105Sjp  * or more AD LDAP objects named by the dn_list; first found one wins.
874c8e26105Sjp  */
875c8e26105Sjp static char *
876479ac375Sdm ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers,
877c8e26105Sjp 			char **dn_list, char *attr)
878c8e26105Sjp {
879c8e26105Sjp 	int 	i;
8804d61c878SJulian Pullen 	int	rc;
881c8e26105Sjp 	int	scope = LDAP_SCOPE_BASE;
882c8e26105Sjp 	char	*attrs[2];
883c8e26105Sjp 	LDAPMessage *results = NULL;
884c8e26105Sjp 	LDAPMessage *entry;
885c8e26105Sjp 	char	**values = NULL;
886c8e26105Sjp 	char	*val = NULL;
887c8e26105Sjp 
888c8e26105Sjp 	attrs[0] = attr;
889c8e26105Sjp 	attrs[1] = NULL;
890c8e26105Sjp 
8914d61c878SJulian Pullen 	if (*ld == NULL)
8924d61c878SJulian Pullen 		*ld = ldap_lookup_init(domainControllers);
893c8e26105Sjp 
8944d61c878SJulian Pullen 	if (*ld == NULL)
895c8e26105Sjp 		return (NULL);
896c8e26105Sjp 
897c8e26105Sjp 	for (i = 0; dn_list[i] != NULL; i++) {
898c8e26105Sjp 		rc = ldap_search_s(*ld, dn_list[i], scope,
899c8e26105Sjp 		    "(objectclass=*)", attrs, 0, &results);
900c8e26105Sjp 		if (rc == LDAP_SUCCESS) {
901c8e26105Sjp 			for (entry = ldap_first_entry(*ld, results);
902c8e26105Sjp 			    entry != NULL && values == NULL;
903c8e26105Sjp 			    entry = ldap_next_entry(*ld, entry)) {
904c8e26105Sjp 				values = ldap_get_values(
905c8e26105Sjp 				    *ld, entry, attr);
906c8e26105Sjp 			}
907c8e26105Sjp 
908c8e26105Sjp 			if (values != NULL) {
909c8e26105Sjp 				(void) ldap_msgfree(results);
910c8e26105Sjp 				val = strdup(values[0]);
911c8e26105Sjp 				ldap_value_free(values);
912c8e26105Sjp 				return (val);
913c8e26105Sjp 			}
914c8e26105Sjp 		}
915c8e26105Sjp 		if (results != NULL) {
916c8e26105Sjp 			(void) ldap_msgfree(results);
917c8e26105Sjp 			results = NULL;
918c8e26105Sjp 		}
919c8e26105Sjp 	}
920c8e26105Sjp 
921c8e26105Sjp 	return (NULL);
922c8e26105Sjp }
923c8e26105Sjp 
924c8e26105Sjp 
9254d61c878SJulian Pullen /*
9264d61c878SJulian Pullen  * Lookup the trusted domains in the global catalog.
9274d61c878SJulian Pullen  *
9284d61c878SJulian Pullen  * Returns:
9294d61c878SJulian Pullen  *	array of trusted domains which is terminated by
9304d61c878SJulian Pullen  *		an empty trusted domain.
9314d61c878SJulian Pullen  *	NULL an error occured
9324d61c878SJulian Pullen  */
9334d61c878SJulian Pullen ad_disc_trusteddomains_t *
9344d61c878SJulian Pullen ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog,
9354d61c878SJulian Pullen 			char *base_dn)
9364d61c878SJulian Pullen {
9374d61c878SJulian Pullen 	int		scope = LDAP_SCOPE_SUBTREE;
9384d61c878SJulian Pullen 	char		*attrs[3];
9394d61c878SJulian Pullen 	int		rc;
9404d61c878SJulian Pullen 	LDAPMessage	*results = NULL;
9414d61c878SJulian Pullen 	LDAPMessage	*entry;
9424d61c878SJulian Pullen 	char		*filter;
9434d61c878SJulian Pullen 	char		**partner = NULL;
9444d61c878SJulian Pullen 	char		**direction = NULL;
9454d61c878SJulian Pullen 	int		num = 0;
9464d61c878SJulian Pullen 	ad_disc_trusteddomains_t *trusted_domains = NULL;
9474d61c878SJulian Pullen 
9484d61c878SJulian Pullen 
9494d61c878SJulian Pullen 	if (*ld == NULL)
9504d61c878SJulian Pullen 		*ld = ldap_lookup_init(globalCatalog);
9514d61c878SJulian Pullen 
9524d61c878SJulian Pullen 	if (*ld == NULL)
9534d61c878SJulian Pullen 		return (NULL);
9544d61c878SJulian Pullen 
9554d61c878SJulian Pullen 	attrs[0] = "trustPartner";
9564d61c878SJulian Pullen 	attrs[1] = "trustDirection";
9574d61c878SJulian Pullen 	attrs[2] = NULL;
9584d61c878SJulian Pullen 
9594d61c878SJulian Pullen 	/* trustDirection values - inbound = 1 and bidirectional = 3 */
9604d61c878SJulian Pullen 	filter = "(&(objectclass=trustedDomain)"
9614d61c878SJulian Pullen 	    "(|(trustDirection=3)(trustDirection=1)))";
9624d61c878SJulian Pullen 
9634d61c878SJulian Pullen 	rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results);
9644d61c878SJulian Pullen 	if (rc == LDAP_SUCCESS) {
9654d61c878SJulian Pullen 		for (entry = ldap_first_entry(*ld, results);
9664d61c878SJulian Pullen 		    entry != NULL; entry = ldap_next_entry(*ld, entry)) {
9674d61c878SJulian Pullen 			partner = ldap_get_values(*ld, entry, "trustPartner");
9684d61c878SJulian Pullen 			direction = ldap_get_values(
9694d61c878SJulian Pullen 			    *ld, entry, "trustDirection");
9704d61c878SJulian Pullen 
9714d61c878SJulian Pullen 			if (partner != NULL && direction != NULL) {
9724d61c878SJulian Pullen 				num++;
9734d61c878SJulian Pullen 				trusted_domains = realloc(trusted_domains,
9744d61c878SJulian Pullen 				    (num + 1) *
9754d61c878SJulian Pullen 				    sizeof (ad_disc_trusteddomains_t));
9764d61c878SJulian Pullen 				if (trusted_domains == NULL) {
9774d61c878SJulian Pullen 					ldap_value_free(partner);
9784d61c878SJulian Pullen 					ldap_value_free(direction);
9794d61c878SJulian Pullen 					ldap_msgfree(results);
9804d61c878SJulian Pullen 					return (NULL);
9814d61c878SJulian Pullen 				}
9824d61c878SJulian Pullen 				/* Last element should be zero */
9834d61c878SJulian Pullen 				memset(&trusted_domains[num], 0,
9844d61c878SJulian Pullen 				    sizeof (ad_disc_trusteddomains_t));
9854d61c878SJulian Pullen 				strcpy(trusted_domains[num - 1].domain,
9864d61c878SJulian Pullen 				    partner[0]);
9874d61c878SJulian Pullen 				trusted_domains[num - 1].direction =
9884d61c878SJulian Pullen 				    atoi(direction[0]);
9894d61c878SJulian Pullen 			}
9904d61c878SJulian Pullen 			if (partner != NULL)
9914d61c878SJulian Pullen 				ldap_value_free(partner);
9924d61c878SJulian Pullen 			if (direction != NULL)
9934d61c878SJulian Pullen 				ldap_value_free(direction);
9944d61c878SJulian Pullen 		}
9954d61c878SJulian Pullen 	} else if (rc == LDAP_NO_RESULTS_RETURNED) {
9964d61c878SJulian Pullen 		/* This is not an error - return empty trusted domain */
9974d61c878SJulian Pullen 		trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t));
9984d61c878SJulian Pullen 	}
9994d61c878SJulian Pullen 	if (results != NULL)
10004d61c878SJulian Pullen 		ldap_msgfree(results);
10014d61c878SJulian Pullen 
10024d61c878SJulian Pullen 	return (trusted_domains);
10034d61c878SJulian Pullen }
10044d61c878SJulian Pullen 
10054d61c878SJulian Pullen 
10064d61c878SJulian Pullen /*
10074d61c878SJulian Pullen  * This functions finds all the domains in a forest.
10084d61c878SJulian Pullen  */
10094d61c878SJulian Pullen ad_disc_domainsinforest_t *
10104d61c878SJulian Pullen ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs)
10114d61c878SJulian Pullen {
1012928e1f97SJordan Brown 	static char	*attrs[] = {
1013928e1f97SJordan Brown 		"objectSid",
1014928e1f97SJordan Brown 		NULL,
1015928e1f97SJordan Brown 	};
10164d61c878SJulian Pullen 	int		rc;
10174d61c878SJulian Pullen 	LDAPMessage	*result = NULL;
10184d61c878SJulian Pullen 	LDAPMessage	*entry;
1019928e1f97SJordan Brown 	int		ndomains = 0;
1020928e1f97SJordan Brown 	int		nresults;
10214d61c878SJulian Pullen 	ad_disc_domainsinforest_t *domains = NULL;
10224d61c878SJulian Pullen 
10234d61c878SJulian Pullen 	if (*ld == NULL)
10244d61c878SJulian Pullen 		*ld = ldap_lookup_init(globalCatalogs);
10254d61c878SJulian Pullen 
10267a8a68f5SJulian Pullen 	if (*ld == NULL)
10274d61c878SJulian Pullen 		return (NULL);
10284d61c878SJulian Pullen 
1029928e1f97SJordan Brown 	logger(LOG_DEBUG, "Looking for domains in forest...");
1030928e1f97SJordan Brown 	/* Find domains */
1031928e1f97SJordan Brown 	rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE,
1032928e1f97SJordan Brown 	    "(objectClass=Domain)", attrs, 0, &result);
1033928e1f97SJordan Brown 	if (rc != LDAP_SUCCESS)
1034928e1f97SJordan Brown 		goto err;
1035928e1f97SJordan Brown 
1036928e1f97SJordan Brown 	nresults = ldap_count_entries(*ld, result);
1037928e1f97SJordan Brown 	domains = calloc(nresults + 1, sizeof (*domains));
1038928e1f97SJordan Brown 	if (domains == NULL)
1039928e1f97SJordan Brown 		goto err;
1040928e1f97SJordan Brown 
1041928e1f97SJordan Brown 	for (entry = ldap_first_entry(*ld, result);
1042928e1f97SJordan Brown 	    entry != NULL;
1043928e1f97SJordan Brown 	    entry = ldap_next_entry(*ld, entry)) {
1044928e1f97SJordan Brown 		struct berval	**sid_ber;
1045928e1f97SJordan Brown 		adutils_sid_t	sid;
1046928e1f97SJordan Brown 		char		*sid_str;
1047928e1f97SJordan Brown 		char 		*name;
1048928e1f97SJordan Brown 
1049928e1f97SJordan Brown 		sid_ber = ldap_get_values_len(*ld, entry,
1050928e1f97SJordan Brown 		    "objectSid");
1051928e1f97SJordan Brown 		if (sid_ber == NULL)
1052928e1f97SJordan Brown 			continue;
10534d61c878SJulian Pullen 
1054928e1f97SJordan Brown 		rc = adutils_getsid(sid_ber[0], &sid);
1055928e1f97SJordan Brown 		ldap_value_free_len(sid_ber);
1056928e1f97SJordan Brown 		if (rc < 0)
1057928e1f97SJordan Brown 			goto err;
10584d61c878SJulian Pullen 
1059928e1f97SJordan Brown 		if ((sid_str = adutils_sid2txt(&sid)) == NULL)
1060928e1f97SJordan Brown 			goto err;
10614d61c878SJulian Pullen 
1062928e1f97SJordan Brown 		strcpy(domains[ndomains].sid, sid_str);
1063928e1f97SJordan Brown 		free(sid_str);
1064928e1f97SJordan Brown 
1065928e1f97SJordan Brown 		name = DN_to_DNS(ldap_get_dn(*ld, entry));
1066928e1f97SJordan Brown 		if (name == NULL)
1067928e1f97SJordan Brown 			goto err;
1068928e1f97SJordan Brown 
1069928e1f97SJordan Brown 		strcpy(domains[ndomains].domain, name);
1070928e1f97SJordan Brown 		free(name);
1071928e1f97SJordan Brown 
1072928e1f97SJordan Brown 		logger(LOG_DEBUG, "    found %s", domains[ndomains].domain);
1073928e1f97SJordan Brown 
1074928e1f97SJordan Brown 		ndomains++;
10754d61c878SJulian Pullen 	}
10764d61c878SJulian Pullen 
1077928e1f97SJordan Brown 	if (ndomains == 0)
1078928e1f97SJordan Brown 		goto err;
1079928e1f97SJordan Brown 
1080928e1f97SJordan Brown 	if (ndomains < nresults) {
1081928e1f97SJordan Brown 		ad_disc_domainsinforest_t *tmp;
10821fcced4cSJordan Brown 		tmp = realloc(domains, (ndomains + 1) * sizeof (*domains));
1083928e1f97SJordan Brown 		if (tmp == NULL)
1084928e1f97SJordan Brown 			goto err;
1085928e1f97SJordan Brown 		domains = tmp;
10864d61c878SJulian Pullen 	}
1087928e1f97SJordan Brown 
1088928e1f97SJordan Brown 	if (result != NULL)
1089928e1f97SJordan Brown 		ldap_msgfree(result);
10904d61c878SJulian Pullen 
10914d61c878SJulian Pullen 	return (domains);
1092928e1f97SJordan Brown 
1093928e1f97SJordan Brown err:
1094928e1f97SJordan Brown 	free(domains);
1095928e1f97SJordan Brown 	if (result != NULL)
1096928e1f97SJordan Brown 		ldap_msgfree(result);
1097928e1f97SJordan Brown 	return (NULL);
10984d61c878SJulian Pullen }
10994d61c878SJulian Pullen 
1100c8e26105Sjp 
1101c8e26105Sjp ad_disc_t
1102c8e26105Sjp ad_disc_init(void)
1103c8e26105Sjp {
1104c8e26105Sjp 	struct ad_disc *ctx;
1105c8e26105Sjp 	ctx = calloc(1, sizeof (struct ad_disc));
11060dcc7149Snw 	if (ctx != NULL)
11070dcc7149Snw 		DO_RES_NINIT(ctx);
11084d61c878SJulian Pullen 
11094d61c878SJulian Pullen 	ctx->domain_name.type = AD_STRING;
11104d61c878SJulian Pullen 	ctx->domain_controller.type = AD_DIRECTORY;
11114d61c878SJulian Pullen 	ctx->site_name.type = AD_STRING;
11124d61c878SJulian Pullen 	ctx->forest_name.type = AD_STRING;
11134d61c878SJulian Pullen 	ctx->global_catalog.type = AD_DIRECTORY;
11144d61c878SJulian Pullen 	ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST;
11154d61c878SJulian Pullen 	ctx->trusted_domains.type = AD_TRUSTED_DOMAINS;
11164d61c878SJulian Pullen 	/* Site specific versions */
11174d61c878SJulian Pullen 	ctx->site_domain_controller.type = AD_DIRECTORY;
11184d61c878SJulian Pullen 	ctx->site_global_catalog.type = AD_DIRECTORY;
1119c8e26105Sjp 	return (ctx);
1120c8e26105Sjp }
1121c8e26105Sjp 
1122c8e26105Sjp 
1123c8e26105Sjp void
1124c8e26105Sjp ad_disc_fini(ad_disc_t ctx)
1125c8e26105Sjp {
1126cd37da74Snw 	if (ctx == NULL)
1127cd37da74Snw 		return;
1128cd37da74Snw 
11290dcc7149Snw 	if (ctx->res_ninitted)
11304d61c878SJulian Pullen 		res_ndestroy(&ctx->res_state);
1131c8e26105Sjp 
1132c8e26105Sjp 	if (ctx->subnets != NULL)
1133c8e26105Sjp 		free(ctx->subnets);
1134c8e26105Sjp 
11354d61c878SJulian Pullen 	if (ctx->domain_name.value != NULL)
11364d61c878SJulian Pullen 		free(ctx->domain_name.value);
11374d61c878SJulian Pullen 
11384d61c878SJulian Pullen 	if (ctx->domain_controller.value != NULL)
11394d61c878SJulian Pullen 		free(ctx->domain_controller.value);
11404d61c878SJulian Pullen 
11414d61c878SJulian Pullen 	if (ctx->site_name.value != NULL)
11424d61c878SJulian Pullen 		free(ctx->site_name.value);
1143c8e26105Sjp 
11444d61c878SJulian Pullen 	if (ctx->forest_name.value != NULL)
11454d61c878SJulian Pullen 		free(ctx->forest_name.value);
1146c8e26105Sjp 
11474d61c878SJulian Pullen 	if (ctx->global_catalog.value != NULL)
11484d61c878SJulian Pullen 		free(ctx->global_catalog.value);
1149c8e26105Sjp 
11504d61c878SJulian Pullen 	if (ctx->domains_in_forest.value != NULL)
11514d61c878SJulian Pullen 		free(ctx->domains_in_forest.value);
1152c8e26105Sjp 
11534d61c878SJulian Pullen 	if (ctx->trusted_domains.value != NULL)
11544d61c878SJulian Pullen 		free(ctx->trusted_domains.value);
1155c8e26105Sjp 
11564d61c878SJulian Pullen 	/* Site specific versions */
11574d61c878SJulian Pullen 	if (ctx->site_domain_controller.value != NULL)
11584d61c878SJulian Pullen 		free(ctx->site_domain_controller.value);
1159c8e26105Sjp 
11604d61c878SJulian Pullen 	if (ctx->site_global_catalog.value != NULL)
11614d61c878SJulian Pullen 		free(ctx->site_global_catalog.value);
1162c8e26105Sjp 
1163c8e26105Sjp 	free(ctx);
1164c8e26105Sjp }
1165c8e26105Sjp 
1166c8e26105Sjp void
1167c8e26105Sjp ad_disc_refresh(ad_disc_t ctx)
1168c8e26105Sjp {
11690dcc7149Snw 	if (ctx->res_ninitted)
11704d61c878SJulian Pullen 		res_ndestroy(&ctx->res_state);
11714d61c878SJulian Pullen 	(void) memset(&ctx->res_state, 0, sizeof (ctx->res_state));
11724d61c878SJulian Pullen 	ctx->res_ninitted = res_ninit(&ctx->res_state) != -1;
1173c8e26105Sjp 
11744d61c878SJulian Pullen 	if (ctx->domain_name.state == AD_STATE_AUTO)
11754d61c878SJulian Pullen 		ctx->domain_name.state = AD_STATE_INVALID;
1176c8e26105Sjp 
11774d61c878SJulian Pullen 	if (ctx->domain_controller.state == AD_STATE_AUTO)
11784d61c878SJulian Pullen 		ctx->domain_controller.state  = AD_STATE_INVALID;
1179c8e26105Sjp 
11804d61c878SJulian Pullen 	if (ctx->site_name.state == AD_STATE_AUTO)
11814d61c878SJulian Pullen 		ctx->site_name.state = AD_STATE_INVALID;
1182c8e26105Sjp 
11834d61c878SJulian Pullen 	if (ctx->forest_name.state == AD_STATE_AUTO)
11844d61c878SJulian Pullen 		ctx->forest_name.state = AD_STATE_INVALID;
1185c8e26105Sjp 
11864d61c878SJulian Pullen 	if (ctx->global_catalog.state == AD_STATE_AUTO)
11874d61c878SJulian Pullen 		ctx->global_catalog.state = AD_STATE_INVALID;
1188c8e26105Sjp 
11894d61c878SJulian Pullen 	if (ctx->domains_in_forest.state == AD_STATE_AUTO)
11904d61c878SJulian Pullen 		ctx->domains_in_forest.state  = AD_STATE_INVALID;
1191c8e26105Sjp 
11924d61c878SJulian Pullen 	if (ctx->trusted_domains.state == AD_STATE_AUTO)
11934d61c878SJulian Pullen 		ctx->trusted_domains.state  = AD_STATE_INVALID;
11944d61c878SJulian Pullen 
11954d61c878SJulian Pullen 	if (ctx->site_domain_controller.state == AD_STATE_AUTO)
11964d61c878SJulian Pullen 		ctx->site_domain_controller.state  = AD_STATE_INVALID;
11974d61c878SJulian Pullen 
11984d61c878SJulian Pullen 	if (ctx->site_global_catalog.state == AD_STATE_AUTO)
11994d61c878SJulian Pullen 		ctx->site_global_catalog.state = AD_STATE_INVALID;
1200c8e26105Sjp }
1201c8e26105Sjp 
1202c8e26105Sjp 
1203c8e26105Sjp 
12040dcc7149Snw /* Discover joined Active Directory domainName */
12054d61c878SJulian Pullen static ad_item_t *
1206c8e26105Sjp validate_DomainName(ad_disc_t ctx)
1207c8e26105Sjp {
1208479ac375Sdm 	idmap_ad_disc_ds_t *domain_controller = NULL;
1209c8e26105Sjp 	char *dname, *srvname;
1210c8e26105Sjp 	uint32_t ttl = 0;
1211928e1f97SJordan Brown 	int len;
1212c8e26105Sjp 
12134d61c878SJulian Pullen 	if (is_valid(&ctx->domain_name))
12144d61c878SJulian Pullen 		return (&ctx->domain_name);
1215c8e26105Sjp 
1216c8e26105Sjp 
1217c8e26105Sjp 	/* Try to find our domain by searching for DCs for it */
12180dcc7149Snw 	DO_RES_NINIT(ctx);
12194d61c878SJulian Pullen 	domain_controller = srv_query(&ctx->res_state, LDAP_SRV_HEAD
12204d61c878SJulian Pullen 	    DC_SRV_TAIL, ctx->domain_name.value, &srvname, &ttl);
1221c8e26105Sjp 
1222c8e26105Sjp 	/*
1223c8e26105Sjp 	 * If we can't find DCs by via res_nsearch() then there's no
1224c8e26105Sjp 	 * point in trying anything else to discover the AD domain name.
1225c8e26105Sjp 	 */
1226c8e26105Sjp 	if (domain_controller == NULL)
12274d61c878SJulian Pullen 		return (NULL);
1228c8e26105Sjp 
1229c8e26105Sjp 	free(domain_controller);
1230c8e26105Sjp 	/*
1231c8e26105Sjp 	 * We have the FQDN of the SRV RR name, so now we extract the
1232c8e26105Sjp 	 * domainname suffix from it.
1233c8e26105Sjp 	 */
1234c8e26105Sjp 	dname = strdup(srvname + strlen(LDAP_SRV_HEAD DC_SRV_TAIL) +
1235c8e26105Sjp 	    1 /* for the dot between RR name and domainname */);
1236c8e26105Sjp 
1237c8e26105Sjp 	free(srvname);
1238c8e26105Sjp 
1239c8e26105Sjp 	if (dname == NULL) {
12407a8a68f5SJulian Pullen 		logger(LOG_ERR, "Out of memory");
12414d61c878SJulian Pullen 		return (NULL);
1242c8e26105Sjp 	}
1243c8e26105Sjp 
1244c8e26105Sjp 	/* Eat any trailing dot */
1245928e1f97SJordan Brown 	len = strlen(dname);
12461fcced4cSJordan Brown 	if (len > 0 && dname[len - 1] == '.')
12471fcced4cSJordan Brown 		dname[len - 1] = '\0';
1248c8e26105Sjp 
12494d61c878SJulian Pullen 	update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl);
12504d61c878SJulian Pullen 
12514d61c878SJulian Pullen 	return (&ctx->domain_name);
1252c8e26105Sjp }
1253c8e26105Sjp 
1254c8e26105Sjp 
1255c8e26105Sjp char *
12567a8a68f5SJulian Pullen ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered)
1257c8e26105Sjp {
1258c8e26105Sjp 	char *domain_name = NULL;
12594d61c878SJulian Pullen 	ad_item_t *domain_name_item;
1260c8e26105Sjp 
12614d61c878SJulian Pullen 	domain_name_item = validate_DomainName(ctx);
12624d61c878SJulian Pullen 
12634d61c878SJulian Pullen 	if (domain_name_item) {
12644d61c878SJulian Pullen 		domain_name = strdup(domain_name_item->value);
12654d61c878SJulian Pullen 		if (auto_discovered != NULL)
12664d61c878SJulian Pullen 			*auto_discovered =
12674d61c878SJulian Pullen 			    (domain_name_item->state == AD_STATE_AUTO);
12684d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
12697a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
1270c8e26105Sjp 
1271c8e26105Sjp 	return (domain_name);
1272c8e26105Sjp }
1273c8e26105Sjp 
1274c8e26105Sjp 
12750dcc7149Snw /* Discover domain controllers */
12764d61c878SJulian Pullen static ad_item_t *
1277c8e26105Sjp validate_DomainController(ad_disc_t ctx, enum ad_disc_req req)
1278c8e26105Sjp {
1279c8e26105Sjp 	uint32_t ttl = 0;
1280479ac375Sdm 	idmap_ad_disc_ds_t *domain_controller = NULL;
12817a8a68f5SJulian Pullen 	boolean_t validate_global = B_FALSE;
12827a8a68f5SJulian Pullen 	boolean_t validate_site = B_FALSE;
12834d61c878SJulian Pullen 	ad_item_t *domain_name_item;
12844d61c878SJulian Pullen 	ad_item_t *site_name_item = NULL;
1285c8e26105Sjp 
12864d61c878SJulian Pullen 	/* If the values is fixed there will not be a site specific version */
1287c8e26105Sjp 	if (is_fixed(&ctx->domain_controller))
12884d61c878SJulian Pullen 		return (&ctx->domain_controller);
12894d61c878SJulian Pullen 
12904d61c878SJulian Pullen 	domain_name_item = validate_DomainName(ctx);
12914d61c878SJulian Pullen 	if (domain_name_item == NULL)
12924d61c878SJulian Pullen 		return (NULL);
1293c8e26105Sjp 
1294c8e26105Sjp 	if (req == AD_DISC_GLOBAL)
12957a8a68f5SJulian Pullen 		validate_global = B_TRUE;
1296c8e26105Sjp 	else {
12974d61c878SJulian Pullen 		site_name_item = validate_SiteName(ctx);
12984d61c878SJulian Pullen 		if (site_name_item != NULL)
12997a8a68f5SJulian Pullen 			validate_site = B_TRUE;
13004d61c878SJulian Pullen 		else if (req == AD_DISC_PREFER_SITE)
13017a8a68f5SJulian Pullen 			validate_global = B_TRUE;
1302c8e26105Sjp 	}
1303c8e26105Sjp 
13044d61c878SJulian Pullen 	if (validate_global) {
13054d61c878SJulian Pullen 		if (!is_valid(&ctx->domain_controller) ||
13064d61c878SJulian Pullen 		    is_changed(&ctx->domain_controller, PARAM1,
13074d61c878SJulian Pullen 		    domain_name_item)) {
1308c8e26105Sjp 			/*
13090dcc7149Snw 			 * Lookup DNS SRV RR named
13100dcc7149Snw 			 * _ldap._tcp.dc._msdcs.<DomainName>
1311c8e26105Sjp 			 */
13120dcc7149Snw 			DO_RES_NINIT(ctx);
13134d61c878SJulian Pullen 			domain_controller = srv_query(&ctx->res_state,
1314c8e26105Sjp 			    LDAP_SRV_HEAD DC_SRV_TAIL,
13154d61c878SJulian Pullen 			    domain_name_item->value, NULL, &ttl);
1316c8e26105Sjp 
13174d61c878SJulian Pullen 			if (domain_controller == NULL)
13184d61c878SJulian Pullen 				return (NULL);
1319c8e26105Sjp 
13204d61c878SJulian Pullen 			update_item(&ctx->domain_controller, domain_controller,
13214d61c878SJulian Pullen 			    AD_STATE_AUTO, ttl);
13224d61c878SJulian Pullen 			update_version(&ctx->domain_controller, PARAM1,
13234d61c878SJulian Pullen 			    domain_name_item);
13244d61c878SJulian Pullen 		}
13254d61c878SJulian Pullen 		return (&ctx->domain_controller);
13264d61c878SJulian Pullen 	}
1327c8e26105Sjp 
13284d61c878SJulian Pullen 	if (validate_site) {
13294d61c878SJulian Pullen 		if (!is_valid(&ctx->site_domain_controller) ||
13304d61c878SJulian Pullen 		    is_changed(&ctx->site_domain_controller, PARAM1,
13314d61c878SJulian Pullen 		    domain_name_item) ||
13324d61c878SJulian Pullen 		    is_changed(&ctx->site_domain_controller, PARAM2,
13334d61c878SJulian Pullen 		    site_name_item)) {
1334c8e26105Sjp 			char rr_name[DNS_MAX_NAME];
1335c8e26105Sjp 			/*
13360dcc7149Snw 			 * Lookup DNS SRV RR named
13370dcc7149Snw 			 * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName>
1338c8e26105Sjp 			 */
1339c8e26105Sjp 			(void) snprintf(rr_name, sizeof (rr_name),
1340c8e26105Sjp 			    LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL,
13414d61c878SJulian Pullen 			    site_name_item->value);
13420dcc7149Snw 			DO_RES_NINIT(ctx);
13434d61c878SJulian Pullen 			domain_controller = srv_query(&ctx->res_state, rr_name,
13444d61c878SJulian Pullen 			    domain_name_item->value, NULL, &ttl);
13454d61c878SJulian Pullen 			if (domain_controller == NULL)
13464d61c878SJulian Pullen 				return (NULL);
13474d61c878SJulian Pullen 
13484d61c878SJulian Pullen 			update_item(&ctx->site_domain_controller,
13494d61c878SJulian Pullen 			    domain_controller, AD_STATE_AUTO, ttl);
13504d61c878SJulian Pullen 			update_version(&ctx->site_domain_controller, PARAM1,
13514d61c878SJulian Pullen 			    domain_name_item);
13524d61c878SJulian Pullen 			update_version(&ctx->site_domain_controller, PARAM2,
13534d61c878SJulian Pullen 			    site_name_item);
1354c8e26105Sjp 		}
13554d61c878SJulian Pullen 		return (&ctx->site_domain_controller);
1356c8e26105Sjp 	}
13574d61c878SJulian Pullen 	return (NULL);
1358c8e26105Sjp }
1359c8e26105Sjp 
1360479ac375Sdm idmap_ad_disc_ds_t *
13614d61c878SJulian Pullen ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req,
13627a8a68f5SJulian Pullen 			boolean_t *auto_discovered)
1363c8e26105Sjp {
13644d61c878SJulian Pullen 	ad_item_t *domain_controller_item;
1365479ac375Sdm 	idmap_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 
13810dcc7149Snw /* Discover site name (for multi-homed systems the first one found wins) */
13824d61c878SJulian Pullen static ad_item_t *
1383c8e26105Sjp validate_SiteName(ad_disc_t ctx)
1384c8e26105Sjp {
1385c8e26105Sjp 	LDAP *ld = NULL;
1386c8e26105Sjp 	ad_subnet_t *subnets = NULL;
1387c8e26105Sjp 	char **dn_subnets = NULL;
1388c8e26105Sjp 	char *dn_root[2];
1389c8e26105Sjp 	char *config_naming_context = NULL;
1390c8e26105Sjp 	char *site_object = NULL;
1391c8e26105Sjp 	char *site_name = NULL;
1392c8e26105Sjp 	char *forest_name;
1393c8e26105Sjp 	int len;
1394c8e26105Sjp 	int i;
13957a8a68f5SJulian Pullen 	boolean_t update_required = B_FALSE;
13964d61c878SJulian Pullen 	ad_item_t *domain_controller_item;
1397c8e26105Sjp 
1398c8e26105Sjp 	if (is_fixed(&ctx->site_name))
13994d61c878SJulian Pullen 		return (&ctx->site_name);
1400c8e26105Sjp 
14010dcc7149Snw 	/* Can't rely on site-specific DCs */
14024d61c878SJulian Pullen 	domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
14034d61c878SJulian Pullen 	if (domain_controller_item == NULL)
14044d61c878SJulian Pullen 		return (NULL);
1405c8e26105Sjp 
14064d61c878SJulian Pullen 	if (!is_valid(&ctx->site_name) ||
1407c8e26105Sjp 	    is_changed(&ctx->site_name, PARAM1, &ctx->domain_controller) ||
1408c8e26105Sjp 	    ctx->subnets == NULL || ctx->subnets_changed) {
1409c8e26105Sjp 		subnets = find_subnets();
1410c8e26105Sjp 		ctx->subnets_last_check = time(NULL);
14117a8a68f5SJulian Pullen 		update_required = B_TRUE;
1412c8e26105Sjp 	} else if (ctx->subnets_last_check + 60 < time(NULL)) {
1413c8e26105Sjp 		subnets = find_subnets();
1414c8e26105Sjp 		ctx->subnets_last_check = time(NULL);
1415c8e26105Sjp 		if (cmpsubnets(ctx->subnets, subnets) != 0)
14167a8a68f5SJulian Pullen 			update_required = B_TRUE;
1417c8e26105Sjp 	}
1418c8e26105Sjp 
14190dcc7149Snw 	if (!update_required) {
14200dcc7149Snw 		free(subnets);
14214d61c878SJulian Pullen 		return (&ctx->site_name);
14220dcc7149Snw 	}
1423c8e26105Sjp 
14244d61c878SJulian Pullen 	if (subnets == NULL)
14254d61c878SJulian Pullen 		return (NULL);
14260dcc7149Snw 
14274d61c878SJulian Pullen 	dn_root[0] = "";
14284d61c878SJulian Pullen 	dn_root[1] = NULL;
14290dcc7149Snw 
14304d61c878SJulian Pullen 	config_naming_context = ldap_lookup_entry_attr(
14314d61c878SJulian Pullen 	    &ld, ctx->domain_controller.value,
14324d61c878SJulian Pullen 	    dn_root, "configurationNamingContext");
14334d61c878SJulian Pullen 	if (config_naming_context == NULL)
14344d61c878SJulian Pullen 		goto out;
14354d61c878SJulian Pullen 	/*
14364d61c878SJulian Pullen 	 * configurationNamingContext also provides the Forest
14374d61c878SJulian Pullen 	 * Name.
14384d61c878SJulian Pullen 	 */
14394d61c878SJulian Pullen 	if (!is_fixed(&ctx->forest_name)) {
14400dcc7149Snw 		/*
14414d61c878SJulian Pullen 		 * The configurationNamingContext should be of
14424d61c878SJulian Pullen 		 * form:
14434d61c878SJulian Pullen 		 * CN=Configuration,<DNforestName>
14444d61c878SJulian Pullen 		 * Remove the first part and convert to DNS form
14454d61c878SJulian Pullen 		 * (replace ",DC=" with ".")
14460dcc7149Snw 		 */
14474d61c878SJulian Pullen 		char *str = "CN=Configuration,";
14484d61c878SJulian Pullen 		int len = strlen(str);
14494d61c878SJulian Pullen 		if (strncasecmp(config_naming_context, str, len) == 0) {
14504d61c878SJulian Pullen 			forest_name = DN_to_DNS(config_naming_context + len);
14514d61c878SJulian Pullen 			update_item(&ctx->forest_name, forest_name,
14524d61c878SJulian Pullen 			    AD_STATE_AUTO, 0);
14530dcc7149Snw 		}
14544d61c878SJulian Pullen 	}
14554d61c878SJulian Pullen 
14564d61c878SJulian Pullen 	dn_subnets = subnets_to_DNs(subnets, config_naming_context);
14574d61c878SJulian Pullen 	if (dn_subnets == NULL)
14584d61c878SJulian Pullen 		goto out;
14594d61c878SJulian Pullen 
14604d61c878SJulian Pullen 	site_object = ldap_lookup_entry_attr(
14614d61c878SJulian Pullen 	    &ld, domain_controller_item->value,
14624d61c878SJulian Pullen 	    dn_subnets, "siteobject");
14634d61c878SJulian Pullen 	if (site_object != NULL) {
14644d61c878SJulian Pullen 		/*
14654d61c878SJulian Pullen 		 * The site object should be of the form
14664d61c878SJulian Pullen 		 * CN=<site>,CN=Sites,CN=Configuration,
14674d61c878SJulian Pullen 		 *		<DN Domain>
14684d61c878SJulian Pullen 		 */
14694d61c878SJulian Pullen 		if (strncasecmp(site_object, "CN=", 3) == 0) {
14704d61c878SJulian Pullen 			for (len = 0; site_object[len + 3] != ','; len++)
14710dcc7149Snw 					;
14724d61c878SJulian Pullen 			site_name = malloc(len + 1);
14734d61c878SJulian Pullen 			(void) strncpy(site_name, &site_object[3], len);
14744d61c878SJulian Pullen 			site_name[len] = '\0';
14754d61c878SJulian Pullen 			update_item(&ctx->site_name, site_name,
14764d61c878SJulian Pullen 			    AD_STATE_AUTO, 0);
14770dcc7149Snw 		}
14784d61c878SJulian Pullen 	}
1479c8e26105Sjp 
14804d61c878SJulian Pullen 	if (ctx->subnets != NULL) {
14814d61c878SJulian Pullen 		free(ctx->subnets);
14824d61c878SJulian Pullen 		ctx->subnets = NULL;
14830dcc7149Snw 	}
14844d61c878SJulian Pullen 	ctx->subnets = subnets;
14854d61c878SJulian Pullen 	subnets = NULL;
14867a8a68f5SJulian Pullen 	ctx->subnets_changed = B_FALSE;
14874d61c878SJulian Pullen 
1488c8e26105Sjp out:
14890dcc7149Snw 	if (ld != NULL)
14900dcc7149Snw 		(void) ldap_unbind(ld);
1491c8e26105Sjp 
14920dcc7149Snw 	if (dn_subnets != NULL) {
14930dcc7149Snw 		for (i = 0; dn_subnets[i] != NULL; i++)
14940dcc7149Snw 			free(dn_subnets[i]);
14950dcc7149Snw 		free(dn_subnets);
14960dcc7149Snw 	}
14970dcc7149Snw 	if (config_naming_context != NULL)
14980dcc7149Snw 		free(config_naming_context);
14990dcc7149Snw 	if (site_object != NULL)
15000dcc7149Snw 		free(site_object);
15010dcc7149Snw 
15020dcc7149Snw 	free(subnets);
15034d61c878SJulian Pullen 	if (site_name == NULL)
15044d61c878SJulian Pullen 		return (NULL);
15054d61c878SJulian Pullen 	return (&ctx->site_name);
1506c8e26105Sjp 
1507c8e26105Sjp }
1508c8e26105Sjp 
1509c8e26105Sjp 
1510c8e26105Sjp char *
15117a8a68f5SJulian Pullen ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered)
1512c8e26105Sjp {
15134d61c878SJulian Pullen 	ad_item_t *site_name_item;
1514c8e26105Sjp 	char	*site_name = NULL;
1515c8e26105Sjp 
15164d61c878SJulian Pullen 	site_name_item = validate_SiteName(ctx);
15174d61c878SJulian Pullen 	if (site_name_item != NULL) {
15184d61c878SJulian Pullen 		site_name = strdup(site_name_item->value);
15194d61c878SJulian Pullen 		if (auto_discovered != NULL)
15204d61c878SJulian Pullen 			*auto_discovered =
15214d61c878SJulian Pullen 			    (site_name_item->state == AD_STATE_AUTO);
15224d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
15237a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
15244d61c878SJulian Pullen 
1525c8e26105Sjp 	return (site_name);
1526c8e26105Sjp }
1527c8e26105Sjp 
1528c8e26105Sjp 
1529c8e26105Sjp 
15300dcc7149Snw /* Discover forest name */
15314d61c878SJulian Pullen static ad_item_t *
1532c8e26105Sjp validate_ForestName(ad_disc_t ctx)
1533c8e26105Sjp {
1534c8e26105Sjp 	LDAP	*ld = NULL;
1535c8e26105Sjp 	char	*config_naming_context;
1536c8e26105Sjp 	char	*forest_name = NULL;
1537c8e26105Sjp 	char	*dn_list[2];
15384d61c878SJulian Pullen 	ad_item_t *domain_controller_item;
1539c8e26105Sjp 
1540c8e26105Sjp 	if (is_fixed(&ctx->forest_name))
15414d61c878SJulian Pullen 		return (&ctx->forest_name);
1542c8e26105Sjp 	/*
15430dcc7149Snw 	 * We may not have a site name yet, so we won't rely on
15440dcc7149Snw 	 * site-specific DCs.  (But maybe we could replace
15450dcc7149Snw 	 * validate_ForestName() with validate_siteName()?)
1546c8e26105Sjp 	 */
15474d61c878SJulian Pullen 	domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
15484d61c878SJulian Pullen 	if (domain_controller_item == NULL)
15494d61c878SJulian Pullen 		return (NULL);
1550c8e26105Sjp 
15514d61c878SJulian Pullen 	if (!is_valid(&ctx->forest_name) ||
15524d61c878SJulian Pullen 	    is_changed(&ctx->forest_name, PARAM1, domain_controller_item)) {
15534d61c878SJulian Pullen 
15544d61c878SJulian Pullen 		dn_list[0] = "";
15554d61c878SJulian Pullen 		dn_list[1] = NULL;
15564d61c878SJulian Pullen 		config_naming_context = ldap_lookup_entry_attr(
15574d61c878SJulian Pullen 		    &ld, ctx->domain_controller.value,
15584d61c878SJulian Pullen 		    dn_list, "configurationNamingContext");
15594d61c878SJulian Pullen 		if (config_naming_context != NULL) {
15604d61c878SJulian Pullen 			/*
15614d61c878SJulian Pullen 			 * The configurationNamingContext should be of
15624d61c878SJulian Pullen 			 * form:
15634d61c878SJulian Pullen 			 * CN=Configuration,<DNforestName>
15644d61c878SJulian Pullen 			 * Remove the first part and convert to DNS form
15654d61c878SJulian Pullen 			 * (replace ",DC=" with ".")
15664d61c878SJulian Pullen 			 */
15674d61c878SJulian Pullen 			char *str = "CN=Configuration,";
15684d61c878SJulian Pullen 			int len = strlen(str);
15694d61c878SJulian Pullen 			if (strncasecmp(config_naming_context, str, len) == 0) {
15704d61c878SJulian Pullen 				forest_name = DN_to_DNS(
15714d61c878SJulian Pullen 				    config_naming_context + len);
1572c8e26105Sjp 			}
15734d61c878SJulian Pullen 			free(config_naming_context);
1574c8e26105Sjp 		}
15754d61c878SJulian Pullen 		if (ld != NULL)
15764d61c878SJulian Pullen 			(void) ldap_unbind(ld);
15774d61c878SJulian Pullen 
15784d61c878SJulian Pullen 		if (forest_name == NULL)
15794d61c878SJulian Pullen 			return (NULL);
15804d61c878SJulian Pullen 
15814d61c878SJulian Pullen 		update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0);
15824d61c878SJulian Pullen 		update_version(&ctx->forest_name, PARAM1,
15834d61c878SJulian Pullen 		    domain_controller_item);
1584c8e26105Sjp 	}
15854d61c878SJulian Pullen 	return (&ctx->forest_name);
1586c8e26105Sjp }
1587c8e26105Sjp 
1588c8e26105Sjp 
1589c8e26105Sjp char *
15907a8a68f5SJulian Pullen ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered)
1591c8e26105Sjp {
15924d61c878SJulian Pullen 	ad_item_t *forest_name_item;
1593c8e26105Sjp 	char	*forest_name = NULL;
1594c8e26105Sjp 
15954d61c878SJulian Pullen 	forest_name_item = validate_ForestName(ctx);
15964d61c878SJulian Pullen 
15974d61c878SJulian Pullen 	if (forest_name_item != NULL) {
15984d61c878SJulian Pullen 		forest_name = strdup(forest_name_item->value);
15994d61c878SJulian Pullen 		if (auto_discovered != NULL)
16004d61c878SJulian Pullen 			*auto_discovered =
16014d61c878SJulian Pullen 			    (forest_name_item->state == AD_STATE_AUTO);
16024d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
16037a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
1604c8e26105Sjp 
1605c8e26105Sjp 	return (forest_name);
1606c8e26105Sjp }
1607c8e26105Sjp 
1608c8e26105Sjp 
16090dcc7149Snw /* Discover global catalog servers */
16104d61c878SJulian Pullen static ad_item_t *
1611c8e26105Sjp validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req)
1612c8e26105Sjp {
1613479ac375Sdm 	idmap_ad_disc_ds_t *global_catalog = NULL;
16140dcc7149Snw 	uint32_t ttl = 0;
16157a8a68f5SJulian Pullen 	boolean_t validate_global = B_FALSE;
16167a8a68f5SJulian Pullen 	boolean_t validate_site = B_FALSE;
16174d61c878SJulian Pullen 	ad_item_t *forest_name_item;
16184d61c878SJulian Pullen 	ad_item_t *site_name_item;
1619c8e26105Sjp 
16204d61c878SJulian Pullen 	/* If the values is fixed there will not be a site specific version */
1621c8e26105Sjp 	if (is_fixed(&ctx->global_catalog))
16224d61c878SJulian Pullen 		return (&ctx->global_catalog);
16234d61c878SJulian Pullen 
16244d61c878SJulian Pullen 	forest_name_item = validate_ForestName(ctx);
16254d61c878SJulian Pullen 	if (forest_name_item == NULL)
16264d61c878SJulian Pullen 		return (NULL);
1627c8e26105Sjp 
1628c8e26105Sjp 	if (req == AD_DISC_GLOBAL)
16297a8a68f5SJulian Pullen 		validate_global = B_TRUE;
1630c8e26105Sjp 	else {
16314d61c878SJulian Pullen 		site_name_item = validate_SiteName(ctx);
16324d61c878SJulian Pullen 		if (site_name_item != NULL)
16337a8a68f5SJulian Pullen 			validate_site = B_TRUE;
16344d61c878SJulian Pullen 		else if (req == AD_DISC_PREFER_SITE)
16357a8a68f5SJulian Pullen 			validate_global = B_TRUE;
1636c8e26105Sjp 	}
1637c8e26105Sjp 
16384d61c878SJulian Pullen 	if (validate_global) {
16394d61c878SJulian Pullen 		if (!is_valid(&ctx->global_catalog) ||
16404d61c878SJulian Pullen 		    is_changed(&ctx->global_catalog, PARAM1,
16414d61c878SJulian Pullen 		    forest_name_item)) {
1642c8e26105Sjp 			/*
16430dcc7149Snw 			 * Lookup DNS SRV RR named
16440dcc7149Snw 			 * _ldap._tcp.gc._msdcs.<ForestName>
1645c8e26105Sjp 			 */
16460dcc7149Snw 			DO_RES_NINIT(ctx);
1647c8e26105Sjp 			global_catalog =
16484d61c878SJulian Pullen 			    srv_query(&ctx->res_state,
16494d61c878SJulian Pullen 			    LDAP_SRV_HEAD GC_SRV_TAIL,
16504d61c878SJulian Pullen 			    ctx->forest_name.value, NULL, &ttl);
1651c8e26105Sjp 
16524d61c878SJulian Pullen 			if (global_catalog == NULL)
16534d61c878SJulian Pullen 				return (NULL);
1654c8e26105Sjp 
16554d61c878SJulian Pullen 			update_item(&ctx->global_catalog, global_catalog,
16564d61c878SJulian Pullen 			    AD_STATE_AUTO, ttl);
16574d61c878SJulian Pullen 			update_version(&ctx->global_catalog, PARAM1,
16584d61c878SJulian Pullen 			    forest_name_item);
16594d61c878SJulian Pullen 		}
16604d61c878SJulian Pullen 		return (&ctx->global_catalog);
16614d61c878SJulian Pullen 	}
1662c8e26105Sjp 
16634d61c878SJulian Pullen 	if (validate_site) {
16644d61c878SJulian Pullen 		if (!is_valid(&ctx->site_global_catalog) ||
16654d61c878SJulian Pullen 		    is_changed(&ctx->site_global_catalog, PARAM1,
16664d61c878SJulian Pullen 		    forest_name_item) ||
16674d61c878SJulian Pullen 		    is_changed(&ctx->site_global_catalog, PARAM2,
16684d61c878SJulian Pullen 		    site_name_item)) {
1669c8e26105Sjp 			char 	rr_name[DNS_MAX_NAME];
16704d61c878SJulian Pullen 
1671c8e26105Sjp 			/*
16720dcc7149Snw 			 * Lookup DNS SRV RR named:
16730dcc7149Snw 			 * _ldap._tcp.<siteName>._sites.gc.
16740dcc7149Snw 			 *	_msdcs.<ForestName>
1675c8e26105Sjp 			 */
1676c8e26105Sjp 			(void) snprintf(rr_name,
1677c8e26105Sjp 			    sizeof (rr_name),
1678c8e26105Sjp 			    LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL,
16794d61c878SJulian Pullen 			    ctx->site_name.value);
16800dcc7149Snw 			DO_RES_NINIT(ctx);
16814d61c878SJulian Pullen 			global_catalog = srv_query(&ctx->res_state, rr_name,
16824d61c878SJulian Pullen 			    ctx->forest_name.value, NULL, &ttl);
16834d61c878SJulian Pullen 
16844d61c878SJulian Pullen 			if (global_catalog == NULL)
16854d61c878SJulian Pullen 				return (NULL);
16864d61c878SJulian Pullen 			update_item(&ctx->site_global_catalog, global_catalog,
16874d61c878SJulian Pullen 			    AD_STATE_AUTO, ttl);
16884d61c878SJulian Pullen 			update_version(&ctx->site_global_catalog, PARAM1,
16894d61c878SJulian Pullen 			    forest_name_item);
16904d61c878SJulian Pullen 			update_version(&ctx->site_global_catalog, PARAM2,
16914d61c878SJulian Pullen 			    site_name_item);
1692c8e26105Sjp 		}
16934d61c878SJulian Pullen 		return (&ctx->site_global_catalog);
1694c8e26105Sjp 	}
16954d61c878SJulian Pullen 	return (NULL);
1696c8e26105Sjp }
1697c8e26105Sjp 
1698c8e26105Sjp 
1699479ac375Sdm idmap_ad_disc_ds_t *
17004d61c878SJulian Pullen ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req,
17017a8a68f5SJulian Pullen 			boolean_t *auto_discovered)
1702c8e26105Sjp {
1703479ac375Sdm 	idmap_ad_disc_ds_t *global_catalog = NULL;
17044d61c878SJulian Pullen 	ad_item_t *global_catalog_item;
17054d61c878SJulian Pullen 
17064d61c878SJulian Pullen 	global_catalog_item = validate_GlobalCatalog(ctx, req);
1707c8e26105Sjp 
17084d61c878SJulian Pullen 	if (global_catalog_item != NULL) {
17094d61c878SJulian Pullen 		global_catalog = ds_dup(global_catalog_item->value);
17104d61c878SJulian Pullen 		if (auto_discovered != NULL)
17114d61c878SJulian Pullen 			*auto_discovered =
17124d61c878SJulian Pullen 			    (global_catalog_item->state == AD_STATE_AUTO);
17134d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
17147a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
1715c8e26105Sjp 
1716c8e26105Sjp 	return (global_catalog);
1717c8e26105Sjp }
1718c8e26105Sjp 
1719c8e26105Sjp 
17204d61c878SJulian Pullen static ad_item_t *
17214d61c878SJulian Pullen validate_TrustedDomains(ad_disc_t ctx)
17224d61c878SJulian Pullen {
17234d61c878SJulian Pullen 	LDAP *ld = NULL;
17244d61c878SJulian Pullen 	ad_item_t *global_catalog_item;
17254d61c878SJulian Pullen 	ad_item_t *forest_name_item;
17264d61c878SJulian Pullen 	ad_disc_trusteddomains_t *trusted_domains;
17274d61c878SJulian Pullen 	char *dn = NULL;
17284d61c878SJulian Pullen 	char *forest_name_dn;
17294d61c878SJulian Pullen 	int len;
17304d61c878SJulian Pullen 	int num_parts;
17314d61c878SJulian Pullen 
17324d61c878SJulian Pullen 	if (is_fixed(&ctx->trusted_domains))
17334d61c878SJulian Pullen 		return (&ctx->trusted_domains);
17344d61c878SJulian Pullen 
17354d61c878SJulian Pullen 	global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL);
17364d61c878SJulian Pullen 	if (global_catalog_item == NULL)
17374d61c878SJulian Pullen 		return (NULL);
17384d61c878SJulian Pullen 
17394d61c878SJulian Pullen 	forest_name_item = validate_ForestName(ctx);
17404d61c878SJulian Pullen 	if (forest_name_item == NULL)
17414d61c878SJulian Pullen 		return (NULL);
17424d61c878SJulian Pullen 
17434d61c878SJulian Pullen 	if (!is_valid(&ctx->trusted_domains) ||
17444d61c878SJulian Pullen 	    is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) ||
17454d61c878SJulian Pullen 	    is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) {
17464d61c878SJulian Pullen 
17474d61c878SJulian Pullen 		forest_name_dn = ldap_dns_to_dn(forest_name_item->value,
17484d61c878SJulian Pullen 		    &num_parts);
17494d61c878SJulian Pullen 		if (forest_name_dn == NULL)
17504d61c878SJulian Pullen 			return (NULL);
17514d61c878SJulian Pullen 
17524d61c878SJulian Pullen 		len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1;
17534d61c878SJulian Pullen 		dn = malloc(len);
17544d61c878SJulian Pullen 		if (dn == NULL)  {
17554d61c878SJulian Pullen 			free(forest_name_dn);
17564d61c878SJulian Pullen 			return (NULL);
17574d61c878SJulian Pullen 		}
17584d61c878SJulian Pullen 		(void) snprintf(dn, len, "CN=System,%s", forest_name_dn);
17594d61c878SJulian Pullen 		free(forest_name_dn);
17604d61c878SJulian Pullen 
17614d61c878SJulian Pullen 		trusted_domains = ldap_lookup_trusted_domains(
17624d61c878SJulian Pullen 		    &ld, global_catalog_item->value, dn);
17634d61c878SJulian Pullen 
17644d61c878SJulian Pullen 		if (ld != NULL)
17654d61c878SJulian Pullen 			(void) ldap_unbind(ld);
17664d61c878SJulian Pullen 		free(dn);
17674d61c878SJulian Pullen 
17684d61c878SJulian Pullen 		if (trusted_domains == NULL)
17694d61c878SJulian Pullen 			return (NULL);
17704d61c878SJulian Pullen 
17714d61c878SJulian Pullen 		update_item(&ctx->trusted_domains, trusted_domains,
17724d61c878SJulian Pullen 		    AD_STATE_AUTO, 0);
17734d61c878SJulian Pullen 		update_version(&ctx->trusted_domains, PARAM1,
17744d61c878SJulian Pullen 		    global_catalog_item);
17754d61c878SJulian Pullen 		update_version(&ctx->trusted_domains, PARAM2,
17764d61c878SJulian Pullen 		    forest_name_item);
17774d61c878SJulian Pullen 	}
17784d61c878SJulian Pullen 
17794d61c878SJulian Pullen 	return (&ctx->trusted_domains);
17804d61c878SJulian Pullen }
17814d61c878SJulian Pullen 
17824d61c878SJulian Pullen 
17834d61c878SJulian Pullen ad_disc_trusteddomains_t *
17847a8a68f5SJulian Pullen ad_disc_get_TrustedDomains(ad_disc_t ctx, boolean_t *auto_discovered)
17854d61c878SJulian Pullen {
17864d61c878SJulian Pullen 	ad_disc_trusteddomains_t *trusted_domains = NULL;
17874d61c878SJulian Pullen 	ad_item_t *trusted_domains_item;
17884d61c878SJulian Pullen 
17894d61c878SJulian Pullen 	trusted_domains_item = validate_TrustedDomains(ctx);
17904d61c878SJulian Pullen 
17914d61c878SJulian Pullen 	if (trusted_domains_item != NULL) {
17924d61c878SJulian Pullen 		trusted_domains = td_dup(trusted_domains_item->value);
17934d61c878SJulian Pullen 		if (auto_discovered != NULL)
17944d61c878SJulian Pullen 			*auto_discovered =
17954d61c878SJulian Pullen 			    (trusted_domains_item->state == AD_STATE_AUTO);
17964d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
17977a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
17984d61c878SJulian Pullen 
17994d61c878SJulian Pullen 	return (trusted_domains);
18004d61c878SJulian Pullen }
18014d61c878SJulian Pullen 
18024d61c878SJulian Pullen 
18034d61c878SJulian Pullen static ad_item_t *
18044d61c878SJulian Pullen validate_DomainsInForest(ad_disc_t ctx)
18054d61c878SJulian Pullen {
18064d61c878SJulian Pullen 	ad_item_t *global_catalog_item;
18074d61c878SJulian Pullen 	LDAP *ld = NULL;
18084d61c878SJulian Pullen 	ad_disc_domainsinforest_t *domains_in_forest;
18094d61c878SJulian Pullen 
18104d61c878SJulian Pullen 	if (is_fixed(&ctx->domains_in_forest))
18114d61c878SJulian Pullen 		return (&ctx->domains_in_forest);
18124d61c878SJulian Pullen 
18134d61c878SJulian Pullen 	global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL);
18144d61c878SJulian Pullen 	if (global_catalog_item == NULL)
18154d61c878SJulian Pullen 		return (NULL);
18164d61c878SJulian Pullen 
18174d61c878SJulian Pullen 	if (!is_valid(&ctx->domains_in_forest) ||
18184d61c878SJulian Pullen 	    is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) {
18194d61c878SJulian Pullen 
18204d61c878SJulian Pullen 		domains_in_forest = ldap_lookup_domains_in_forest(
18214d61c878SJulian Pullen 		    &ld, global_catalog_item->value);
18224d61c878SJulian Pullen 
18234d61c878SJulian Pullen 		if (ld != NULL)
18244d61c878SJulian Pullen 			(void) ldap_unbind(ld);
18254d61c878SJulian Pullen 
18264d61c878SJulian Pullen 		if (domains_in_forest == NULL)
18274d61c878SJulian Pullen 			return (NULL);
18284d61c878SJulian Pullen 
18294d61c878SJulian Pullen 		update_item(&ctx->domains_in_forest, domains_in_forest,
18304d61c878SJulian Pullen 		    AD_STATE_AUTO, 0);
18314d61c878SJulian Pullen 		update_version(&ctx->domains_in_forest, PARAM1,
18324d61c878SJulian Pullen 		    global_catalog_item);
18334d61c878SJulian Pullen 	}
18344d61c878SJulian Pullen 	return (&ctx->domains_in_forest);
18354d61c878SJulian Pullen }
18364d61c878SJulian Pullen 
18374d61c878SJulian Pullen 
18384d61c878SJulian Pullen ad_disc_domainsinforest_t *
18397a8a68f5SJulian Pullen ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered)
18404d61c878SJulian Pullen {
18414d61c878SJulian Pullen 	ad_disc_domainsinforest_t *domains_in_forest = NULL;
18424d61c878SJulian Pullen 	ad_item_t *domains_in_forest_item;
18434d61c878SJulian Pullen 
18444d61c878SJulian Pullen 	domains_in_forest_item = validate_DomainsInForest(ctx);
18454d61c878SJulian Pullen 
18464d61c878SJulian Pullen 	if (domains_in_forest_item != NULL) {
18474d61c878SJulian Pullen 		domains_in_forest = df_dup(domains_in_forest_item->value);
18484d61c878SJulian Pullen 		if (auto_discovered != NULL)
18494d61c878SJulian Pullen 			*auto_discovered =
18504d61c878SJulian Pullen 			    (domains_in_forest_item->state == AD_STATE_AUTO);
18514d61c878SJulian Pullen 	} else if (auto_discovered != NULL)
18527a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
18534d61c878SJulian Pullen 
18544d61c878SJulian Pullen 	return (domains_in_forest);
18554d61c878SJulian Pullen }
18564d61c878SJulian Pullen 
18574d61c878SJulian Pullen 
18584d61c878SJulian Pullen 
18594d61c878SJulian Pullen 
1860c8e26105Sjp int
1861c8e26105Sjp ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName)
1862c8e26105Sjp {
1863c8e26105Sjp 	char *domain_name = NULL;
1864c8e26105Sjp 	if (domainName != NULL) {
1865c8e26105Sjp 		domain_name = strdup(domainName);
1866c8e26105Sjp 		if (domain_name == NULL)
1867c8e26105Sjp 			return (-1);
18684d61c878SJulian Pullen 		update_item(&ctx->domain_name, domain_name,
18694d61c878SJulian Pullen 		    AD_STATE_FIXED, 0);
18704d61c878SJulian Pullen 	} else if (ctx->domain_name.state == AD_STATE_FIXED)
18714d61c878SJulian Pullen 		ctx->domain_name.state = AD_STATE_INVALID;
1872c8e26105Sjp 	return (0);
1873c8e26105Sjp }
1874c8e26105Sjp 
1875c8e26105Sjp 
1876c8e26105Sjp int
1877c8e26105Sjp ad_disc_set_DomainController(ad_disc_t ctx,
1878479ac375Sdm 				const idmap_ad_disc_ds_t *domainController)
1879c8e26105Sjp {
1880479ac375Sdm 	idmap_ad_disc_ds_t *domain_controller = NULL;
1881c8e26105Sjp 	if (domainController != NULL) {
18824d61c878SJulian Pullen 		domain_controller = ds_dup(domainController);
1883c8e26105Sjp 		if (domain_controller == NULL)
1884c8e26105Sjp 			return (-1);
18854d61c878SJulian Pullen 		update_item(&ctx->domain_controller, domain_controller,
18864d61c878SJulian Pullen 		    AD_STATE_FIXED, 0);
18874d61c878SJulian Pullen 	} else if (ctx->domain_controller.state == AD_STATE_FIXED)
18884d61c878SJulian Pullen 		ctx->domain_controller.state = AD_STATE_INVALID;
1889c8e26105Sjp 	return (0);
1890c8e26105Sjp }
1891c8e26105Sjp 
1892c8e26105Sjp 
1893c8e26105Sjp int
1894c8e26105Sjp ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName)
1895c8e26105Sjp {
1896c8e26105Sjp 	char *site_name = NULL;
1897c8e26105Sjp 	if (siteName != NULL) {
1898c8e26105Sjp 		site_name = strdup(siteName);
1899c8e26105Sjp 		if (site_name == NULL)
1900c8e26105Sjp 			return (-1);
19014d61c878SJulian Pullen 		update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0);
19024d61c878SJulian Pullen 	} else if (ctx->site_name.state == AD_STATE_FIXED)
19034d61c878SJulian Pullen 		ctx->site_name.state = AD_STATE_INVALID;
1904c8e26105Sjp 	return (0);
1905c8e26105Sjp }
1906c8e26105Sjp 
1907c8e26105Sjp int
1908c8e26105Sjp ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName)
1909c8e26105Sjp {
1910c8e26105Sjp 	char *forest_name = NULL;
1911c8e26105Sjp 	if (forestName != NULL) {
1912c8e26105Sjp 		forest_name = strdup(forestName);
1913c8e26105Sjp 		if (forest_name == NULL)
1914c8e26105Sjp 			return (-1);
19154d61c878SJulian Pullen 		update_item(&ctx->forest_name, forest_name,
19164d61c878SJulian Pullen 		    AD_STATE_FIXED, 0);
19174d61c878SJulian Pullen 	} else if (ctx->forest_name.state == AD_STATE_FIXED)
19184d61c878SJulian Pullen 		ctx->forest_name.state = AD_STATE_INVALID;
1919c8e26105Sjp 	return (0);
1920c8e26105Sjp }
1921c8e26105Sjp 
1922c8e26105Sjp int
1923479ac375Sdm ad_disc_set_GlobalCatalog(ad_disc_t ctx,
1924479ac375Sdm     const idmap_ad_disc_ds_t *globalCatalog)
1925c8e26105Sjp {
1926479ac375Sdm 	idmap_ad_disc_ds_t *global_catalog = NULL;
1927c8e26105Sjp 	if (globalCatalog != NULL) {
19284d61c878SJulian Pullen 		global_catalog = ds_dup(globalCatalog);
1929c8e26105Sjp 		if (global_catalog == NULL)
1930c8e26105Sjp 			return (-1);
19314d61c878SJulian Pullen 		update_item(&ctx->global_catalog, global_catalog,
19324d61c878SJulian Pullen 		    AD_STATE_FIXED, 0);
19334d61c878SJulian Pullen 	} else if (ctx->global_catalog.state == AD_STATE_FIXED)
19344d61c878SJulian Pullen 		ctx->global_catalog.state = AD_STATE_INVALID;
1935c8e26105Sjp 	return (0);
1936c8e26105Sjp }
1937c8e26105Sjp 
1938c8e26105Sjp 
1939c8e26105Sjp int
1940c8e26105Sjp ad_disc_unset(ad_disc_t ctx)
1941c8e26105Sjp {
19424d61c878SJulian Pullen 	if (ctx->domain_name.state == AD_STATE_FIXED)
19434d61c878SJulian Pullen 		ctx->domain_name.state =  AD_STATE_INVALID;
1944c8e26105Sjp 
19454d61c878SJulian Pullen 	if (ctx->domain_controller.state == AD_STATE_FIXED)
19464d61c878SJulian Pullen 		ctx->domain_controller.state =  AD_STATE_INVALID;
1947c8e26105Sjp 
19484d61c878SJulian Pullen 	if (ctx->site_name.state == AD_STATE_FIXED)
19494d61c878SJulian Pullen 		ctx->site_name.state =  AD_STATE_INVALID;
1950c8e26105Sjp 
19514d61c878SJulian Pullen 	if (ctx->forest_name.state == AD_STATE_FIXED)
19524d61c878SJulian Pullen 		ctx->forest_name.state =  AD_STATE_INVALID;
1953c8e26105Sjp 
19544d61c878SJulian Pullen 	if (ctx->global_catalog.state == AD_STATE_FIXED)
19554d61c878SJulian Pullen 		ctx->global_catalog.state =  AD_STATE_INVALID;
1956c8e26105Sjp 
1957c8e26105Sjp 	return (0);
1958c8e26105Sjp }
1959c8e26105Sjp 
1960c8e26105Sjp /*
1961c8e26105Sjp  * ad_disc_get_TTL
1962c8e26105Sjp  *
1963c8e26105Sjp  * This routines the time to live for AD
1964c8e26105Sjp  * auto discovered items.
1965c8e26105Sjp  *
1966c8e26105Sjp  *	Returns:
1967c8e26105Sjp  *		-1 if there are no TTL items
1968c8e26105Sjp  *		0  if there are expired items
1969c8e26105Sjp  *		else the number of seconds
19700dcc7149Snw  *
19710dcc7149Snw  * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it
19720dcc7149Snw  * is positive -- min() greater than zero.
1973c8e26105Sjp  */
19740dcc7149Snw #define	MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \
19750dcc7149Snw 		(-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x))))
1976c8e26105Sjp int
1977c8e26105Sjp ad_disc_get_TTL(ad_disc_t ctx)
1978c8e26105Sjp {
1979c8e26105Sjp 	int ttl;
1980c8e26105Sjp 
19810dcc7149Snw 	ttl = MIN_GT_ZERO(ctx->domain_controller.ttl, ctx->global_catalog.ttl);
19820dcc7149Snw 	ttl = MIN_GT_ZERO(ttl, ctx->site_domain_controller.ttl);
19830dcc7149Snw 	ttl = MIN_GT_ZERO(ttl, ctx->site_global_catalog.ttl);
1984c8e26105Sjp 
19850dcc7149Snw 	if (ttl == -1)
1986c8e26105Sjp 		return (-1);
1987c8e26105Sjp 	ttl -= time(NULL);
1988c8e26105Sjp 	if (ttl < 0)
19890dcc7149Snw 		return (0);
1990c8e26105Sjp 	return (ttl);
1991c8e26105Sjp }
1992c8e26105Sjp 
19937a8a68f5SJulian Pullen boolean_t
1994c8e26105Sjp ad_disc_SubnetChanged(ad_disc_t ctx)
1995c8e26105Sjp {
1996c8e26105Sjp 	ad_subnet_t *subnets;
1997c8e26105Sjp 
1998c8e26105Sjp 	if (ctx->subnets_changed || ctx->subnets == NULL)
19997a8a68f5SJulian Pullen 		return (B_TRUE);
2000c8e26105Sjp 
2001c8e26105Sjp 	if ((subnets = find_subnets()) != NULL) {
2002c8e26105Sjp 		if (cmpsubnets(subnets, ctx->subnets) != 0)
20037a8a68f5SJulian Pullen 			ctx->subnets_changed = B_TRUE;
2004c8e26105Sjp 		free(subnets);
2005c8e26105Sjp 	}
2006c8e26105Sjp 
2007c8e26105Sjp 	return (ctx->subnets_changed);
2008c8e26105Sjp }
2009