2c8e2610jp * CDDL HEADER START
3c8e2610jp *
4c8e2610jp * The contents of this file are subject to the terms of the
5c8e2610jp * Common Development and Distribution License (the "License").
6c8e2610jp * You may not use this file except in compliance with the License.
7c8e2610jp *
8c8e2610jp * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9c8e2610jp * or http://www.opensolaris.org/os/licensing.
10c8e2610jp * See the License for the specific language governing permissions
11c8e2610jp * and limitations under the License.
12c8e2610jp *
13c8e2610jp * When distributing Covered Code, include this CDDL HEADER in each
14c8e2610jp * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15c8e2610jp * If applicable, add the following below this CDDL HEADER, with the
16c8e2610jp * fields enclosed by brackets "[]" replaced with your own identifying
17c8e2610jp * information: Portions Copyright [yyyy] [name of copyright owner]
18c8e2610jp *
19c8e2610jp * CDDL HEADER END
20c8e2610jp */
23148c5f4Alan Wright * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24d0bed8fGordon Ross * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
25c8e2610jp */
280dcc714nw * Active Directory Auto-Discovery.
290dcc714nw *
300dcc714nw * This [project private] API allows the caller to provide whatever
310dcc714nw * details it knows a priori (i.e., provided via configuration so as to
320dcc714nw * override auto-discovery) and in any order.  Then the caller can ask
330dcc714nw * for any of the auto-discoverable parameters in any order.
340dcc714nw *
350dcc714nw * But there is an actual order in which discovery must be done.  Given
360dcc714nw * the discovery mechanism implemented here, that order is:
370dcc714nw *
380dcc714nw *  - the domain name joined must be discovered first
390dcc714nw *  - then the domain controllers
400dcc714nw *  - then the forest name and site name
410dcc714nw *  - then the global catalog servers, and site-specific domain
420dcc714nw *    controllers and global catalog servers.
430dcc714nw *
440dcc714nw * The API does not require it be called in the same order because there
450dcc714nw * may be other discovery mechanisms in the future, and exposing
460dcc714nw * ordering requirements of the current mechanism now can create trouble
470dcc714nw * down the line.  Also, this makes the API easier to use now, which
480dcc714nw * means less work to do some day when we make this a public API.
490dcc714nw *
500dcc714nw * Domain discovery is done by res_nsearch() of the DNS SRV RR name for
510dcc714nw * domain controllers.  As long as the joined domain appears in the DNS
520dcc714nw * resolver's search list then we'll find it.
530dcc714nw *
540dcc714nw * Domain controller discovery is a matter of formatting the DNS SRV RR
550dcc714nw * FQDN for domain controllers and doing a lookup for them.  Knowledge
560dcc714nw * of the domain name is not fundamentally required, but we separate the
570dcc714nw * two processes, which in practice can lead to one more DNS lookup than
580dcc714nw * is strictly required.
590dcc714nw *
600dcc714nw * Forest and site name discovery require an LDAP search of the AD
610dcc714nw * "configuration partition" at a domain controller for the joined
620dcc714nw * domain.  Forest and site name discovery depend on knowing the joined
630dcc714nw * domain name and domain controllers for that domain.
640dcc714nw *
650dcc714nw * Global catalog server discovery requires knowledge of the forest
660dcc714nw * name in order to format the DNS SRV RR FQDN to lookup.  Site-specific
670dcc714nw * domain controller discovery depends on knowing the site name (and,
680dcc714nw * therefore, joined domain, ...).  Site-specific global catalog server
690dcc714nw * discovery depends on knowledge of the forest and site names, which
700dcc714nw * depend on...
710dcc714nw *
720dcc714nw * All the work of discovering particular items is done by functions
730dcc714nw * named validate_<item>().  Each such function calls validate_<item>()
740dcc714nw * for any items that it depends on.
750dcc714nw *
760dcc714nw * This API is not thread-safe.
770dcc714nw */
80c8e2610jp#include <stdio.h>
81c8e2610jp#include <string.h>
82c8e2610jp#include <strings.h>
83c8e2610jp#include <unistd.h>
844d61c87Julian Pullen#include <assert.h>
85c8e2610jp#include <stdlib.h>
86c8e2610jp#include <net/if.h>
87c8e2610jp#include <sys/types.h>
88c8e2610jp#include <sys/socket.h>
89c8e2610jp#include <sys/sockio.h>
90c8e2610jp#include <netinet/in.h>
91c8e2610jp#include <arpa/inet.h>
92c8e2610jp#include <arpa/nameser.h>
93c8e2610jp#include <resolv.h>
94c8e2610jp#include <netdb.h>
95c8e2610jp#include <ctype.h>
96c8e2610jp#include <errno.h>
97c8e2610jp#include <ldap.h>
98b3700b0Gordon Ross#include <note.h>
99c8e2610jp#include <sasl/sasl.h>
1004d61c87Julian Pullen#include <sys/u8_textprep.h>
1017a8a68fJulian Pullen#include <syslog.h>
102b3700b0Gordon Ross#include <uuid/uuid.h>
103b3700b0Gordon Ross#include <ads/dsgetdc.h>
1047a8a68fJulian Pullen#include "adutils_impl.h"
105b3700b0Gordon Ross#include "addisc_impl.h"
107c586600Keyur Desai/*
108c586600Keyur Desai * These set some sanity policies for discovery.  After a discovery
109c586600Keyur Desai * cycle, we will consider the results (successful or unsuccessful)
110c586600Keyur Desai * to be valid for at least MINIMUM_TTL seconds, and for at most
111c586600Keyur Desai * MAXIMUM_TTL seconds.  Note that the caller is free to request
112c586600Keyur Desai * discovery cycles sooner than MINIMUM_TTL if it has reason to believe
113c586600Keyur Desai * that the situation has changed.
114c586600Keyur Desai */
115c586600Keyur Desai#define	MINIMUM_TTL	(5 * 60)
116c586600Keyur Desai#define	MAXIMUM_TTL	(20 * 60)
119c8e2610jp#define	DNS_MAX_NAME	NS_MAXDNAME
121b3700b0Gordon Ross#define	GC_PORT		3268
123c8e2610jp/* SRV RR names for various queries */
124c8e2610jp#define	LDAP_SRV_HEAD		"_ldap._tcp."
125c8e2610jp#define	SITE_SRV_MIDDLE		"%s._sites."
126c8e2610jp#define	GC_SRV_TAIL		"gc._msdcs"
127c8e2610jp#define	DC_SRV_TAIL		"dc._msdcs"
128c8e2610jp#define	ALL_GC_SRV_TAIL		"_gc._tcp"
129c8e2610jp#define	PDC_SRV			 "_ldap._tcp.pdc._msdcs.%s"
131c8e2610jp/* A RR name for all GCs -- last resort this works */
132c8e2610jp#define	GC_ALL_A_NAME_FSTR "gc._msdcs.%s."
1360dcc714nw * We try res_ninit() whenever we don't have one.  res_ninit() fails if
1370dcc714nw * idmapd is running before the network is up!
1380dcc714nw */
139b3700b0Gordon Ross#define	DO_RES_NINIT(ctx)				\
140b3700b0Gordon Ross	if (!(ctx)->res_ninitted)			\
141b3700b0Gordon Ross		(void) do_res_ninit(ctx)
142b3700b0Gordon Ross
143b3700b0Gordon Ross#define	DO_GETNAMEINFO(b, l, s)				\
144b3700b0Gordon Ross	if (ad_disc_getnameinfo(b, l, s) != 0)		\
145b3700b0Gordon Ross		(void) strlcpy(b, "?", l)
146b3700b0Gordon Ross
147b3700b0Gordon Ross#define	DEBUG1STATUS(ctx, ...) do { \
148b3700b0Gordon Ross	if (DBG(DISC, 1)) \
149b3700b0Gordon Ross		logger(LOG_DEBUG, __VA_ARGS__); \
150b3700b0Gordon Ross	if (ctx->status_fp) { \
151b3700b0Gordon Ross		(void) fprintf(ctx->status_fp, __VA_ARGS__); \
152b3700b0Gordon Ross		(void) fprintf(ctx->status_fp, "\n"); \
153b3700b0Gordon Ross	} \
154b3700b0Gordon Ross	_NOTE(CONSTCOND) \
155b3700b0Gordon Ross} while (0)
157c8e2610jp#define	is_fixed(item)					\
1584d61c87Julian Pullen	((item)->state == AD_STATE_FIXED)
160d0bed8fGordon Ross#define	is_changed(item, num, param)			\
161c8e2610jp	((item)->param_version[num] != (param)->version)
163b3700b0Gordon Rossvoid * uuid_dup(void *);
164b3700b0Gordon Ross
165b3700b0Gordon Rossstatic ad_item_t *validate_SiteName(ad_disc_t ctx);
166b3700b0Gordon Rossstatic ad_item_t *validate_PreferredDC(ad_disc_t ctx);
167b3700b0Gordon Ross
169c8e2610jp * Function definitions
170c8e2610jp */
173b3700b0Gordon Rossstatic int
174b3700b0Gordon Rossdo_res_ninit(ad_disc_t ctx)
175b3700b0Gordon Ross{
176b3700b0Gordon Ross	int rc;
177b3700b0Gordon Ross
178b3700b0Gordon Ross	rc = res_ninit(&ctx->res_state);
179b3700b0Gordon Ross	if (rc != 0)
180b3700b0Gordon Ross		return (rc);
181b3700b0Gordon Ross	ctx->res_ninitted = 1;
182b3700b0Gordon Ross	/*
183b3700b0Gordon Ross	 * The SRV records returnd by AD can be larger than 512 bytes,
184b3700b0Gordon Ross	 * so we'd like to use TCP for those searches.  Unfortunately,
185b3700b0Gordon Ross	 * the TCP connect timeout seen by the resolver is very long
186b3700b0Gordon Ross	 * (more than a couple minutes) and we can't wait that long.
187b3700b0Gordon Ross	 * Don't do use TCP until we can override the timeout.
188b3700b0Gordon Ross	 *
189b3700b0Gordon Ross	 * Note that some queries will try TCP anyway.
190b3700b0Gordon Ross	 */
191b3700b0Gordon Ross#if 0
192b3700b0Gordon Ross	ctx->res_state.options |= RES_USEVC;
193b3700b0Gordon Ross#endif
194b3700b0Gordon Ross	return (0);
195b3700b0Gordon Ross}
196b3700b0Gordon Ross
197b3700b0Gordon Ross/*
198b3700b0Gordon Ross * Private getnameinfo(3socket) variant tailored to our needs.
199b3700b0Gordon Ross */
200b3700b0Gordon Rossint
201b3700b0Gordon Rossad_disc_getnameinfo(char *obuf, int olen, struct sockaddr_storage *ss)
202b3700b0Gordon Ross{
203b3700b0Gordon Ross	struct sockaddr *sa;
204b3700b0Gordon Ross	int eai, slen;
205b3700b0Gordon Ross
206b3700b0Gordon Ross	sa = (void *)ss;
207b3700b0Gordon Ross	switch (sa->sa_family) {
208b3700b0Gordon Ross	case AF_INET:
209b3700b0Gordon Ross		slen = sizeof (struct sockaddr_in);
210b3700b0Gordon Ross		break;
211b3700b0Gordon Ross	case AF_INET6:
212b3700b0Gordon Ross		slen = sizeof (struct sockaddr_in6);
213b3700b0Gordon Ross		break;
214b3700b0Gordon Ross	default:
215b3700b0Gordon Ross		return (EAI_FAMILY);
216b3700b0Gordon Ross	}
217b3700b0Gordon Ross
218b3700b0Gordon Ross	eai = getnameinfo(sa, slen, obuf, olen, NULL, 0, NI_NUMERICHOST);
219b3700b0Gordon Ross
220b3700b0Gordon Ross	return (eai);
221b3700b0Gordon Ross}
223c8e2610jpstatic void
224c8e2610jpupdate_version(ad_item_t *item, int  num, ad_item_t *param)
226c8e2610jp	item->param_version[num] = param->version;
2304d61c87Julian Pullen
2317a8a68fJulian Pullenstatic boolean_t
2324d61c87Julian Pullenis_valid(ad_item_t *item)
2344d61c87Julian Pullen	if (item->value != NULL) {
2354d61c87Julian Pullen		if (item->state == AD_STATE_FIXED)
2367a8a68fJulian Pullen			return (B_TRUE);
2374d61c87Julian Pullen		if (item->state == AD_STATE_AUTO &&
238c586600Keyur Desai		    (item->expires == 0 || item->expires > time(NULL)))
2397a8a68fJulian Pullen			return (B_TRUE);
2404d61c87Julian Pullen	}
2417a8a68fJulian Pullen	return (B_FALSE);
245c8e2610jpstatic void
2464d61c87Julian Pullenupdate_item(ad_item_t *item, void *value, enum ad_item_state state,
247c8e2610jp		uint32_t ttl)
2494d61c87Julian Pullen	if (item->value != NULL && value != NULL) {
2504d61c87Julian Pullen		if ((item->type == AD_STRING &&
2514d61c87Julian Pullen		    strcmp(item->value, value) != 0) ||
252b3700b0Gordon Ross		    (item->type == AD_UUID &&
253b3700b0Gordon Ross		    ad_disc_compare_uuid(item->value, value) != 0)||
2544d61c87Julian Pullen		    (item->type == AD_DIRECTORY &&
2554d61c87Julian Pullen		    ad_disc_compare_ds(item->value, value) != 0)||
2564d61c87Julian Pullen		    (item->type == AD_DOMAINS_IN_FOREST &&
2574d61c87Julian Pullen		    ad_disc_compare_domainsinforest(item->value, value) != 0) ||
2584d61c87Julian Pullen		    (item->type == AD_TRUSTED_DOMAINS &&
2594d61c87Julian Pullen		    ad_disc_compare_trusteddomains(item->value, value) != 0))
260c8e2610jp			item->version++;
2614d61c87Julian Pullen	} else if (item->value != value)
262c8e2610jp		item->version++;
2644d61c87Julian Pullen	if (item->value != NULL)
2654d61c87Julian Pullen		free(item->value);
2674d61c87Julian Pullen	item->value = value;
2684d61c87Julian Pullen	item->state = state;
270c8e2610jp	if (ttl == 0)
271c586600Keyur Desai		item->expires = 0;
272c8e2610jp	else
273c586600Keyur Desai		item->expires = time(NULL) + ttl;
276b3700b0Gordon Ross/* Compare UUIDs */
277b3700b0Gordon Rossint
278b3700b0Gordon Rossad_disc_compare_uuid(uuid_t *u1, uuid_t *u2)
279b3700b0Gordon Ross{
280b3700b0Gordon Ross	int rc;
281b3700b0Gordon Ross
282b3700b0Gordon Ross	rc = memcmp(u1, u2, UUID_LEN);
283b3700b0Gordon Ross	return (rc);
284b3700b0Gordon Ross}
285b3700b0Gordon Ross
286b3700b0Gordon Rossvoid *
287b3700b0Gordon Rossuuid_dup(void *src)
288b3700b0Gordon Ross{
289b3700b0Gordon Ross	void *dst;
290b3700b0Gordon Ross	dst = malloc(UUID_LEN);
291b3700b0Gordon Ross	if (dst != NULL)
292b3700b0Gordon Ross		(void) memcpy(dst, src, UUID_LEN);
293b3700b0Gordon Ross	return (dst);
294b3700b0Gordon Ross}
2964d61c87Julian Pullen/* Compare DS lists */
2974d61c87Julian Pullenint
298b3700b0Gordon Rossad_disc_compare_ds(ad_disc_ds_t *ds1, ad_disc_ds_t *ds2)
3007a8a68fJulian Pullen	int		i, j;
3017a8a68fJulian Pullen	int		num_ds1;
3027a8a68fJulian Pullen	int		num_ds2;
3037a8a68fJulian Pullen	boolean_t	match;
3054d61c87Julian Pullen	for (i = 0; ds1[i].host[0] != '\0'; i++)
3064d61c87Julian Pullen		continue;
3074d61c87Julian Pullen	num_ds1 = i;
3084d61c87Julian Pullen	for (j = 0; ds2[j].host[0] != '\0'; j++)
3094d61c87Julian Pullen		continue;
3104d61c87Julian Pullen	num_ds2 = j;
3114d61c87Julian Pullen	if (num_ds1 != num_ds2)
3124d61c87Julian Pullen		return (1);
3144d61c87Julian Pullen	for (i = 0; i < num_ds1; i++) {
3157a8a68fJulian Pullen		match = B_FALSE;
3164d61c87Julian Pullen		for (j = 0; j < num_ds2; j++) {
317928e1f9Jordan Brown			if (strcmp(ds1[i].host, ds2[j].host) == 0 &&
318928e1f9Jordan Brown			    ds1[i].port == ds2[j].port) {
3197a8a68fJulian Pullen				match = B_TRUE;
3204d61c87Julian Pullen				break;
3214d61c87Julian Pullen			}
3224d61c87Julian Pullen		}
3234d61c87Julian Pullen		if (!match)
3244d61c87Julian Pullen			return (1);
3254d61c87Julian Pullen	}
3264d61c87Julian Pullen	return (0);
3274d61c87Julian Pullen}
3294d61c87Julian Pullen
3304d61c87Julian Pullen/* Copy a list of DSs */
331b3700b0Gordon Rossstatic ad_disc_ds_t *
332b3700b0Gordon Rossds_dup(const ad_disc_ds_t *srv)
3334d61c87Julian Pullen{
3344d61c87Julian Pullen	int	i;
3354d61c87Julian Pullen	int	size;
336b3700b0Gordon Ross	ad_disc_ds_t *new = NULL;
3374d61c87Julian Pullen
3384d61c87Julian Pullen	for (i = 0; srv[i].host[0] != '\0'; i++)
3394d61c87Julian Pullen		continue;
3404d61c87Julian Pullen
341b3700b0Gordon Ross	size = (i + 1) * sizeof (ad_disc_ds_t);
3424d61c87Julian Pullen	new = malloc(size);
3434d61c87Julian Pullen	if (new != NULL)
344148c5f4Alan Wright		(void) memcpy(new, srv, size);
3454d61c87Julian Pullen	return (new);
3494d61c87Julian Pullenint
3504d61c87Julian Pullenad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1,
3514d61c87Julian Pullen			ad_disc_trusteddomains_t *td2)
3537a8a68fJulian Pullen	int		i, j;
3547a8a68fJulian Pullen	int		num_td1;
3557a8a68fJulian Pullen	int		num_td2;
3567a8a68fJulian Pullen	boolean_t	match;
3584d61c87Julian Pullen	for (i = 0; td1[i].domain[0] != '\0'; i++)
3594d61c87Julian Pullen		continue;
3604d61c87Julian Pullen	num_td1 = i;
3624d61c87Julian Pullen	for (j = 0; td2[j].domain[0] != '\0'; j++)
3634d61c87Julian Pullen		continue;
3644d61c87Julian Pullen	num_td2 = j;
3654d61c87Julian Pullen
3664d61c87Julian Pullen	if (num_td1 != num_td2)
3674d61c87Julian Pullen		return (1);
3684d61c87Julian Pullen
3694d61c87Julian Pullen	for (i = 0; i < num_td1; i++) {
3707a8a68fJulian Pullen		match = B_FALSE;
3714d61c87Julian Pullen		for (j = 0; j < num_td2; j++) {
3721fcced4Jordan Brown			if (domain_eq(td1[i].domain, td2[j].domain)) {
3737a8a68fJulian Pullen				match = B_TRUE;
3744d61c87Julian Pullen				break;
3754d61c87Julian Pullen			}
3764d61c87Julian Pullen		}
3774d61c87Julian Pullen		if (!match)
3784d61c87Julian Pullen			return (1);
3794d61c87Julian Pullen	}
3804d61c87Julian Pullen	return (0);
3814d61c87Julian Pullen}
3824d61c87Julian Pullen
3834d61c87Julian Pullen
3844d61c87Julian Pullen
3854d61c87Julian Pullen/* Copy a list of Trusted Domains */
3864d61c87Julian Pullenstatic ad_disc_trusteddomains_t *
3874d61c87Julian Pullentd_dup(const ad_disc_trusteddomains_t *td)
3884d61c87Julian Pullen{
3894d61c87Julian Pullen	int	i;
3904d61c87Julian Pullen	int	size;
3914d61c87Julian Pullen	ad_disc_trusteddomains_t *new = NULL;
3924d61c87Julian Pullen
3934d61c87Julian Pullen	for (i = 0; td[i].domain[0] != '\0'; i++)
3944d61c87Julian Pullen		continue;
3954d61c87Julian Pullen
3964d61c87Julian Pullen	size = (i + 1) * sizeof (ad_disc_trusteddomains_t);
3974d61c87Julian Pullen	new = malloc(size);
3984d61c87Julian Pullen	if (new != NULL)
399148c5f4Alan Wright		(void) memcpy(new, td, size);
4004d61c87Julian Pullen	return (new);
4014d61c87Julian Pullen}
4024d61c87Julian Pullen
4034d61c87Julian Pullen
4044d61c87Julian Pullen
4054d61c87Julian Pullenint
4064d61c87Julian Pullenad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *df1,
4074d61c87Julian Pullen			ad_disc_domainsinforest_t *df2)
4084d61c87Julian Pullen{
4097a8a68fJulian Pullen	int		i, j;
4107a8a68fJulian Pullen	int		num_df1;
4117a8a68fJulian Pullen	int		num_df2;
4127a8a68fJulian Pullen	boolean_t	match;
4134d61c87Julian Pullen
4144d61c87Julian Pullen	for (i = 0; df1[i].domain[0] != '\0'; i++)
4154d61c87Julian Pullen		continue;
4164d61c87Julian Pullen	num_df1 = i;
4174d61c87Julian Pullen
4184d61c87Julian Pullen	for (j = 0; df2[j].domain[0] != '\0'; j++)
4194d61c87Julian Pullen		continue;
4204d61c87Julian Pullen	num_df2 = j;
4214d61c87Julian Pullen
4224d61c87Julian Pullen	if (num_df1 != num_df2)
4234d61c87Julian Pullen		return (1);
4244d61c87Julian Pullen
4254d61c87Julian Pullen	for (i = 0; i < num_df1; i++) {
4267a8a68fJulian Pullen		match = B_FALSE;
4274d61c87Julian Pullen		for (j = 0; j < num_df2; j++) {
4281fcced4Jordan Brown			if (domain_eq(df1[i].domain, df2[j].domain) &&
429928e1f9Jordan Brown			    strcmp(df1[i].sid, df2[j].sid) == 0) {
4307a8a68fJulian Pullen				match = B_TRUE;
4314d61c87Julian Pullen				break;
4324d61c87Julian Pullen			}
4334d61c87Julian Pullen		}
4344d61c87Julian Pullen		if (!match)
4354d61c87Julian Pullen			return (1);
4364d61c87Julian Pullen	}
4374d61c87Julian Pullen	return (0);
4384d61c87Julian Pullen}
4404d61c87Julian Pullen
4414d61c87Julian Pullen
4424d61c87Julian Pullen/* Copy a list of Trusted Domains */
4434d61c87Julian Pullenstatic ad_disc_domainsinforest_t *
4444d61c87Julian Pullendf_dup(const ad_disc_domainsinforest_t *df)
4454d61c87Julian Pullen{
4464d61c87Julian Pullen	int	i;
4474d61c87Julian Pullen	int	size;
4484d61c87Julian Pullen	ad_disc_domainsinforest_t *new = NULL;
4494d61c87Julian Pullen
4504d61c87Julian Pullen	for (i = 0; df[i].domain[0] != '\0'; i++)
4514d61c87Julian Pullen		continue;
4524d61c87Julian Pullen
4534d61c87Julian Pullen	size = (i + 1) * sizeof (ad_disc_domainsinforest_t);
4544d61c87Julian Pullen	new = malloc(size);
4554d61c87Julian Pullen	if (new != NULL)
456148c5f4Alan Wright		(void) memcpy(new, df, size);
4574d61c87Julian Pullen	return (new);
4624d61c87Julian Pullen
4634d61c87Julian Pullen
465c8e2610jp * Returns an array of IPv4 address/prefix length
466c8e2610jp * The last subnet is NULL
467c8e2610jp */
468c8e2610jpstatic ad_subnet_t *
471c8e2610jp	int		sock, n, i;
472c8e2610jp	struct lifconf	lifc;
473c8e2610jp	struct lifreq	lifr, *lifrp;
474c8e2610jp	struct lifnum	lifn;
475c8e2610jp	uint32_t	prefix_len;
476c8e2610jp	char		*s;
477c8e2610jp	ad_subnet_t	*results;
479c8e2610jp	lifrp = &lifr;
481c8e2610jp	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
4827a8a68fJulian Pullen		logger(LOG_ERR, "Failed to open IPv4 socket for "
48371590c9nw		    "listing network interfaces (%s)", strerror(errno));
484c8e2610jp		return (NULL);
485c8e2610jp	}
487c8e2610jp	lifn.lifn_family = AF_INET;
488c8e2610jp	lifn.lifn_flags = 0;
489c8e2610jp	if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
4907a8a68fJulian Pullen		logger(LOG_ERR,
49171590c9nw		    "Failed to find the number of network interfaces (%s)",
49271590c9nw		    strerror(errno));
493148c5f4Alan Wright		(void) close(sock);
494c8e2610jp		return (NULL);
495c8e2610jp	}
497c8e2610jp	if (lifn.lifn_count < 1) {
4987a8a68fJulian Pullen		logger(LOG_ERR, "No IPv4 network interfaces found");
499148c5f4Alan Wright		(void) close(sock);
500c8e2610jp		return (NULL);
501c8e2610jp	}
503c8e2610jp	lifc.lifc_family = AF_INET;
504c8e2610jp	lifc.lifc_flags = 0;
505c8e2610jp	lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
506c8e2610jp	lifc.lifc_buf = malloc(lifc.lifc_len);
508c8e2610jp	if (lifc.lifc_buf == NULL) {
5097a8a68fJulian Pullen		logger(LOG_ERR, "Out of memory");
510148c5f4Alan Wright		(void) close(sock);
511c8e2610jp		return (NULL);
512c8e2610jp	}
514c8e2610jp	if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) {
5157a8a68fJulian Pullen		logger(LOG_ERR, "Failed to list network interfaces (%s)",
51671590c9nw		    strerror(errno));
517c8e2610jp		free(lifc.lifc_buf);
518148c5f4Alan Wright		(void) close(sock);
519c8e2610jp		return (NULL);
520c8e2610jp	}
522c8e2610jp	n = lifc.lifc_len / (int)sizeof (struct lifreq);
524c8e2610jp	if ((results = calloc(n + 1, sizeof (ad_subnet_t))) == NULL) {
525c8e2610jp		free(lifc.lifc_buf);
526148c5f4Alan Wright		(void) close(sock);
527c8e2610jp		return (NULL);
528c8e2610jp	}
530c8e2610jp	for (i = 0, lifrp = lifc.lifc_req; i < n; i++, lifrp++) {
531c8e2610jp		if (ioctl(sock, SIOCGLIFFLAGS, lifrp) < 0)
532c8e2610jp			continue;
534c8e2610jp		if ((lifrp->lifr_flags & IFF_UP) == 0)
535c8e2610jp			continue;
537c8e2610jp		if (ioctl(sock, SIOCGLIFSUBNET, lifrp) < 0)
538c8e2610jp			continue;
540c8e2610jp		prefix_len = lifrp->lifr_addrlen;
542c8e2610jp		s = inet_ntoa(((struct sockaddr_in *)
543c8e2610jp		    &lifrp->lifr_addr)->sin_addr);
545c8e2610jp		(void) snprintf(results[i].subnet, sizeof (ad_subnet_t),
546c8e2610jp		    "%s/%d", s, prefix_len);
547c8e2610jp	}
549c8e2610jp	free(lifc.lifc_buf);
550148c5f4Alan Wright	(void) close(sock);
552c8e2610jp	return (results);
555c8e2610jpstatic int
556c8e2610jpcmpsubnets(ad_subnet_t *subnets1, ad_subnet_t *subnets2)
558c8e2610jp	int num_subnets1;
559c8e2610jp	int num_subnets2;
5607a8a68fJulian Pullen	boolean_t matched;
561c8e2610jp	int i, j;
563c8e2610jp	for (i = 0; subnets1[i].subnet[0] != '\0'; i++)
5644d61c87Julian Pullen		continue;
565c8e2610jp	num_subnets1 = i;
567c8e2610jp	for (i = 0; subnets2[i].subnet[0] != '\0'; i++)
5684d61c87Julian Pullen		continue;
569c8e2610jp	num_subnets2 = i;
571c8e2610jp	if (num_subnets1 != num_subnets2)
572c8e2610jp		return (1);
574c8e2610jp	for (i = 0;  i < num_subnets1; i++) {
5757a8a68fJulian Pullen		matched = B_FALSE;
576c8e2610jp		for (j = 0; j < num_subnets2; j++) {
577c8e2610jp			if (strcmp(subnets1[i].subnet,
578c8e2610jp			    subnets2[j].subnet) == 0) {
5797a8a68fJulian Pullen				matched = B_TRUE;
580c8e2610jp				break;
581c8e2610jp			}
582c8e2610jp		}
583c8e2610jp		if (!matched)
584c8e2610jp			return (1);
585c8e2610jp	}
586c8e2610jp	return (0);
592c8e2610jp/* Convert a DN's DC components into a DNS domainname */
5937a8a68fJulian Pullenchar *
594c8e2610jpDN_to_DNS(const char *dn_name)
596c8e2610jp	char	dns[DNS_MAX_NAME];
597c8e2610jp	char	*dns_name;
598c8e2610jp	int	i, j;
599c8e2610jp	int	num = 0;
601c8e2610jp	j = 0;
602c8e2610jp	i = 0;
6044d61c87Julian Pullen	if (dn_name == NULL)
6054d61c87Julian Pullen		return (NULL);
606c8e2610jp	/*
607c8e2610jp	 * Find all DC=<value> and form DNS name of the
608c8e2610jp	 * form <value1>.<value2>...
609c8e2610jp	 */
6104d61c87Julian Pullen	while (dn_name[i] != '\0') {
611c8e2610jp		if (strncasecmp(&dn_name[i], "DC=", 3) == 0) {
612c8e2610jp			i += 3;
6134d61c87Julian Pullen			if (dn_name[i] != '\0' && num > 0)
614c8e2610jp				dns[j++] = '.';
6154d61c87Julian Pullen			while (dn_name[i] != '\0' &&
616c8e2610jp			    dn_name[i] != ',' && dn_name[i] != '+')
617c8e2610jp				dns[j++] = dn_name[i++];
618c8e2610jp			num++;
619c8e2610jp		} else {
620c8e2610jp			/* Skip attr=value as it is not DC= */
6214d61c87Julian Pullen			while (dn_name[i] != '\0' &&
622c8e2610jp			    dn_name[i] != ',' && dn_name[i] != '+')
623c8e2610jp				i++;
624c8e2610jp		}
625c8e2610jp		/* Skip over separator ','  or '+' */
6264d61c87Julian Pullen		if (dn_name[i] != '\0') i++;
627c8e2610jp	}
628c8e2610jp	dns[j] = '\0';
629c8e2610jp	dns_name = malloc(j + 1);
630c8e2610jp	if (dns_name != NULL)
631c8e2610jp		(void) strlcpy(dns_name, dns, j + 1);
632c8e2610jp	return (dns_name);
6364d61c87Julian Pullen/*
6374d61c87Julian Pullen * A utility function to bind to a Directory server
6384d61c87Julian Pullen */
6394d61c87Julian Pullen
640c586600Keyur Desaistatic
641c586600Keyur DesaiLDAP *
642b3700b0Gordon Rossldap_lookup_init(ad_disc_ds_t *ds)
6434d61c87Julian Pullen{
644d0bed8fGordon Ross	int	i;
6454d61c87Julian Pullen	int	rc, ldversion;
6464d61c87Julian Pullen	int	zero = 0;
647d0bed8fGordon Ross	int	timeoutms = 5 * 1000;
648d0bed8fGordon Ross	char	*saslmech = "GSSAPI";
6494d61c87Julian Pullen	uint32_t saslflags = LDAP_SASL_INTERACTIVE;
650d0bed8fGordon Ross	LDAP	*ld = NULL;
6514d61c87Julian Pullen
6524d61c87Julian Pullen	for (i = 0; ds[i].host[0] != '\0'; i++) {
653b3700b0Gordon Ross		if (DBG(LDAP, 2)) {
654b3700b0Gordon Ross			logger(LOG_DEBUG, "adutils: ldap_lookup_init, host %s",
655b3700b0Gordon Ross			    ds[i].host);
656b3700b0Gordon Ross		}
657b3700b0Gordon Ross
6584d61c87Julian Pullen		ld = ldap_init(ds[i].host, ds[i].port);
6594d61c87Julian Pullen		if (ld == NULL) {
660148c5f4Alan Wright			if (DBG(LDAP, 1)) {
661148c5f4Alan Wright				logger(LOG_DEBUG,
662148c5f4Alan Wright				    "Couldn't connect to AD DC %s:%d (%s)",
663148c5f4Alan Wright				    ds[i].host, ds[i].port,
664148c5f4Alan Wright				    strerror(errno));
665148c5f4Alan Wright			}
6664d61c87Julian Pullen			continue;
6674d61c87Julian Pullen		}
6684d61c87Julian Pullen
6694d61c87Julian Pullen		ldversion = LDAP_VERSION3;
6704d61c87Julian Pullen		(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
6714d61c87Julian Pullen		    &ldversion);
6724d61c87Julian Pullen		(void) ldap_set_option(ld, LDAP_OPT_REFERRALS,
6734d61c87Julian Pullen		    LDAP_OPT_OFF);
6744d61c87Julian Pullen		(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
6754d61c87Julian Pullen		(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
6764d61c87Julian Pullen		/* setup TCP/IP connect timeout */
6774d61c87Julian Pullen		(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
6784d61c87Julian Pullen		    &timeoutms);
6794d61c87Julian Pullen		(void) ldap_set_option(ld, LDAP_OPT_RESTART,
6804d61c87Julian Pullen		    LDAP_OPT_ON);
6814d61c87Julian Pullen
682bd42852Julian Pullen		rc = adutils_set_thread_functions(ld);
683bd42852Julian Pullen		if (rc != LDAP_SUCCESS) {
684bd42852Julian Pullen			/* Error has already been logged */
685bd42852Julian Pullen			(void) ldap_unbind(ld);
686bd42852Julian Pullen			ld = NULL;
687bd42852Julian Pullen			continue;
688bd42852Julian Pullen		}
689bd42852Julian Pullen
6904d61c87Julian Pullen		rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */,
6914d61c87Julian Pullen		    saslmech, NULL, NULL, saslflags, &saslcallback,
6924d61c87Julian Pullen		    NULL /* defaults */);
6934d61c87Julian Pullen		if (rc == LDAP_SUCCESS)
6944d61c87Julian Pullen			break;
6954d61c87Julian Pullen
696148c5f4Alan Wright		if (DBG(LDAP, 0)) {
697148c5f4Alan Wright			logger(LOG_INFO, "LDAP: %s:%d: %s",
698148c5f4Alan Wright			    ds[i].host, ds[i].port, ldap_err2string(rc));
699148c5f4Alan Wright			ldap_perror(ld, ds[i].host);
700148c5f4Alan Wright		}
7014d61c87Julian Pullen		(void) ldap_unbind(ld);
7024d61c87Julian Pullen		ld = NULL;
7034d61c87Julian Pullen	}
7044d61c87Julian Pullen	return (ld);
7054d61c87Julian Pullen}
7064d61c87Julian Pullen
7087a8a68fJulian Pullen
7104d61c87Julian Pullen * Lookup the trusted domains in the global catalog.
7114d61c87Julian Pullen *
7124d61c87Julian Pullen * Returns:
7134d61c87Julian Pullen *	array of trusted domains which is terminated by
7144d61c87Julian Pullen *		an empty trusted domain.
7154d61c87Julian Pullen *	NULL an error occured
7164d61c87Julian Pullen */
7174d61c87Julian Pullenad_disc_trusteddomains_t *
718b3700b0Gordon Rossldap_lookup_trusted_domains(LDAP **ld, ad_disc_ds_t *globalCatalog,
7194d61c87Julian Pullen			char *base_dn)
7204d61c87Julian Pullen{
7214d61c87Julian Pullen	int		scope = LDAP_SCOPE_SUBTREE;
7224d61c87Julian Pullen	char		*attrs[3];
7234d61c87Julian Pullen	int		rc;
7244d61c87Julian Pullen	LDAPMessage	*results = NULL;
7254d61c87Julian Pullen	LDAPMessage	*entry;
7264d61c87Julian Pullen	char		*filter;
7274d61c87Julian Pullen	char		**partner = NULL;
7284d61c87Julian Pullen	char		**direction = NULL;
7294d61c87Julian Pullen	int		num = 0;
7304d61c87Julian Pullen	ad_disc_trusteddomains_t *trusted_domains = NULL;
7314d61c87Julian Pullen
732148c5f4Alan Wright	if (DBG(DISC, 1))
733148c5f4Alan Wright		logger(LOG_DEBUG, "Looking for trusted domains...");
7344d61c87Julian Pullen
7354d61c87Julian Pullen	if (*ld == NULL)
7364d61c87Julian Pullen		*ld = ldap_lookup_init(globalCatalog);
7374d61c87Julian Pullen
738b3700b0Gordon Ross	if (*ld == NULL) {
739b3700b0Gordon Ross		logger(LOG_ERR, "adutils: ldap_lookup_init failed");
7404d61c87Julian Pullen		return (NULL);
741b3700b0Gordon Ross	}
7424d61c87Julian Pullen
7434d61c87Julian Pullen	attrs[0] = "trustPartner";
7444d61c87Julian Pullen	attrs[1] = "trustDirection";
7454d61c87Julian Pullen	attrs[2] = NULL;
7464d61c87Julian Pullen
747148c5f4Alan Wright	/*
748148c5f4Alan Wright	 * Trust direction values:
749148c5f4Alan Wright	 * 1 - inbound (they trust us)
750148c5f4Alan Wright	 * 2 - outbound (we trust them)
751148c5f4Alan Wright	 * 3 - bidirectional (we trust each other)
752148c5f4Alan Wright	 */
7534d61c87Julian Pullen	filter = "(&(objectclass=trustedDomain)"
754148c5f4Alan Wright	    "(|(trustDirection=3)(trustDirection=2)))";
7554d61c87Julian Pullen
7564d61c87Julian Pullen	rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results);
757148c5f4Alan Wright	if (DBG(DISC, 1))
758148c5f4Alan Wright		logger(LOG_DEBUG, "Trusted domains:");
7594d61c87Julian Pullen	if (rc == LDAP_SUCCESS) {
7604d61c87Julian Pullen		for (entry = ldap_first_entry(*ld, results);
7614d61c87Julian Pullen		    entry != NULL; entry = ldap_next_entry(*ld, entry)) {
7624d61c87Julian Pullen			partner = ldap_get_values(*ld, entry, "trustPartner");
7634d61c87Julian Pullen			direction = ldap_get_values(
7644d61c87Julian Pullen			    *ld, entry, "trustDirection");
7654d61c87Julian Pullen
7664d61c87Julian Pullen			if (partner != NULL && direction != NULL) {
767148c5f4Alan Wright				if (DBG(DISC, 1)) {
768148c5f4Alan Wright					logger(LOG_DEBUG, "    %s (%s)",
769148c5f4Alan Wright					    partner[0], direction[0]);
770148c5f4Alan Wright				}
7714d61c87Julian Pullen				num++;
772c586600Keyur Desai				void *tmp = realloc(trusted_domains,
7734d61c87Julian Pullen				    (num + 1) *
7744d61c87Julian Pullen				    sizeof (ad_disc_trusteddomains_t));
775c586600Keyur Desai				if (tmp == NULL) {
776c586600Keyur Desai					free(trusted_domains);
7774d61c87Julian Pullen					ldap_value_free(partner);
7784d61c87Julian Pullen					ldap_value_free(direction);
779148c5f4Alan Wright					(void) ldap_msgfree(results);
7804d61c87Julian Pullen					return (NULL);
7814d61c87Julian Pullen				}
782c586600Keyur Desai				trusted_domains = tmp;
7834d61c87Julian Pullen				/* Last element should be zero */
784148c5f4Alan Wright				(void) memset(&trusted_domains[num], 0,
7854d61c87Julian Pullen				    sizeof (ad_disc_trusteddomains_t));
786148c5f4Alan Wright				(void) strcpy(trusted_domains[num - 1].domain,
7874d61c87Julian Pullen				    partner[0]);
7884d61c87Julian Pullen				trusted_domains[num - 1].direction =
7894d61c87Julian Pullen				    atoi(direction[0]);
7904d61c87Julian Pullen			}
7914d61c87Julian Pullen			if (partner != NULL)
7924d61c87Julian Pullen				ldap_value_free(partner);
7934d61c87Julian Pullen			if (direction != NULL)
7944d61c87Julian Pullen				ldap_value_free(direction);
7954d61c87Julian Pullen		}
7964d61c87Julian Pullen	} else if (rc == LDAP_NO_RESULTS_RETURNED) {
7974d61c87Julian Pullen		/* This is not an error - return empty trusted domain */
7984d61c87Julian Pullen		trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t));
799148c5f4Alan Wright		if (DBG(DISC, 1))
800148c5f4Alan Wright			logger(LOG_DEBUG, "    not found");
801b3700b0Gordon Ross	} else {
802b3700b0Gordon Ross		if (DBG(DISC, 1))
803b3700b0Gordon Ross			logger(LOG_DEBUG, "    rc=%d", rc);
8044d61c87Julian Pullen	}
8054d61c87Julian Pullen	if (results != NULL)
806148c5f4Alan Wright		(void) ldap_msgfree(results);
8074d61c87Julian Pullen
8084d61c87Julian Pullen	return (trusted_domains);
8094d61c87Julian Pullen}
8104d61c87Julian Pullen
8114d61c87Julian Pullen
8124d61c87Julian Pullen/*
8134d61c87Julian Pullen * This functions finds all the domains in a forest.
8144d61c87Julian Pullen */
8154d61c87Julian Pullenad_disc_domainsinforest_t *
816b3700b0Gordon Rossldap_lookup_domains_in_forest(LDAP **ld, ad_disc_ds_t *globalCatalogs)
8174d61c87Julian Pullen{
818928e1f9Jordan Brown	static char	*attrs[] = {
819928e1f9Jordan Brown		"objectSid",
820928e1f9Jordan Brown		NULL,
821928e1f9Jordan Brown	};
8224d61c87Julian Pullen	int		rc;
8234d61c87Julian Pullen	LDAPMessage	*result = NULL;
8244d61c87Julian Pullen	LDAPMessage	*entry;
825928e1f9Jordan Brown	int		ndomains = 0;
826928e1f9Jordan Brown	int		nresults;
8274d61c87Julian Pullen	ad_disc_domainsinforest_t *domains = NULL;
8284d61c87Julian Pullen
829b3700b0Gordon Ross	if (DBG(DISC, 1))
830148c5f4Alan Wright		logger(LOG_DEBUG, "Looking for domains in forest...");
831148c5f4Alan Wright
8324d61c87Julian Pullen	if (*ld == NULL)
8334d61c87Julian Pullen		*ld = ldap_lookup_init(globalCatalogs);
8344d61c87Julian Pullen
835b3700b0Gordon Ross	if (*ld == NULL) {
836b3700b0Gordon Ross		logger(LOG_ERR, "adutils: ldap_lookup_init failed");
8374d61c87Julian Pullen		return (NULL);
838b3700b0Gordon Ross	}
8394d61c87Julian Pullen
840928e1f9Jordan Brown	/* Find domains */
841928e1f9Jordan Brown	rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE,
842928e1f9Jordan Brown	    "(objectClass=Domain)", attrs, 0, &result);
843b3700b0Gordon Ross	if (rc != LDAP_SUCCESS) {
844b3700b0Gordon Ross		logger(LOG_ERR, "adutils: ldap_search, rc=%d", rc);
845b3700b0Gordon Ross		goto err;
846b3700b0Gordon Ross	}
847148c5f4Alan Wright	if (DBG(DISC, 1))
848148c5f4Alan Wright		logger(LOG_DEBUG, "Domains in forest:");
849928e1f9Jordan Brown
850928e1f9Jordan Brown	nresults = ldap_count_entries(*ld, result);
851928e1f9Jordan Brown	domains = calloc(nresults + 1, sizeof (*domains));
852b3700b0Gordon Ross	if (domains == NULL) {
853b3700b0Gordon Ross		if (DBG(DISC, 1))
854b3700b0Gordon Ross			logger(LOG_DEBUG, "    (nomem)");
855928e1f9Jordan Brown		goto err;
856b3700b0Gordon Ross	}
857928e1f9Jordan Brown
858928e1f9Jordan Brown	for (entry = ldap_first_entry(*ld, result);
859928e1f9Jordan Brown	    entry != NULL;
860928e1f9Jordan Brown	    entry = ldap_next_entry(*ld, entry)) {
861928e1f9Jordan Brown		struct berval	**sid_ber;
862928e1f9Jordan Brown		adutils_sid_t	sid;
863928e1f9Jordan Brown		char		*sid_str;
864d0bed8fGordon Ross		char		*name;
865bbf6f00Jordan Brown		char		*dn;
866928e1f9Jordan Brown
867928e1f9Jordan Brown		sid_ber = ldap_get_values_len(*ld, entry,
868928e1f9Jordan Brown		    "objectSid");
869928e1f9Jordan Brown		if (sid_ber == NULL)
870928e1f9Jordan Brown			continue;
8714d61c87Julian Pullen
872928e1f9Jordan Brown		rc = adutils_getsid(sid_ber[0], &sid);
873928e1f9Jordan Brown		ldap_value_free_len(sid_ber);
874928e1f9Jordan Brown		if (rc < 0)
875928e1f9Jordan Brown			goto err;
8764d61c87Julian Pullen
877928e1f9Jordan Brown		if ((sid_str = adutils_sid2txt(&sid)) == NULL)
878928e1f9Jordan Brown			goto err;
8794d61c87Julian Pullen
880148c5f4Alan Wright		(void) strcpy(domains[ndomains].sid, sid_str);
881928e1f9Jordan Brown		free(sid_str);
882928e1f9Jordan Brown
883bbf6f00Jordan Brown		dn = ldap_get_dn(*ld, entry);
884bbf6f00Jordan Brown		name = DN_to_DNS(dn);
885bbf6f00Jordan Brown		free(dn);
886928e1f9Jordan Brown		if (name == NULL)
887928e1f9Jordan Brown			goto err;
888928e1f9Jordan Brown
889148c5f4Alan Wright		(void) strcpy(domains[ndomains].domain, name);
890928e1f9Jordan Brown		free(name);
891928e1f9Jordan Brown
892148c5f4Alan Wright		if (DBG(DISC, 1))
893148c5f4Alan Wright			logger(LOG_DEBUG, "    %s", domains[ndomains].domain);
894928e1f9Jordan Brown
895928e1f9Jordan Brown		ndomains++;
8964d61c87Julian Pullen	}
8974d61c87Julian Pullen
898148c5f4Alan Wright	if (ndomains == 0) {
899148c5f4Alan Wright		if (DBG(DISC, 1))
900148c5f4Alan Wright			logger(LOG_DEBUG, "    not found");
901928e1f9Jordan Brown		goto err;
902148c5f4Alan Wright	}
903928e1f9Jordan Brown
904928e1f9Jordan Brown	if (ndomains < nresults) {
905928e1f9Jordan Brown		ad_disc_domainsinforest_t *tmp;
9061fcced4Jordan Brown		tmp = realloc(domains, (ndomains + 1) * sizeof (*domains));
907928e1f9Jordan Brown		if (tmp == NULL)
908928e1f9Jordan Brown			goto err;
909928e1f9Jordan Brown		domains = tmp;
9104d61c87Julian Pullen	}
911928e1f9Jordan Brown
912928e1f9Jordan Brown	if (result != NULL)
913148c5f4Alan Wright		(void) ldap_msgfree(result);
9144d61c87Julian Pullen
9154d61c87Julian Pullen	return (domains);
916928e1f9Jordan Brown
917928e1f9Jordan Brownerr:
918928e1f9Jordan Brown	free(domains);
919928e1f9Jordan Brown	if (result != NULL)
920148c5f4Alan Wright		(void) ldap_msgfree(result);
921928e1f9Jordan Brown	return (NULL);
9224d61c87Julian Pullen}
9234d61c87Julian Pullen
928c8e2610jp	struct ad_disc *ctx;
929c8e2610jp	ctx = calloc(1, sizeof (struct ad_disc));
9300dcc714nw	if (ctx != NULL)
9310dcc714nw		DO_RES_NINIT(ctx);
9324d61c87Julian Pullen
9334d61c87Julian Pullen	ctx->domain_name.type = AD_STRING;
934b3700b0Gordon Ross	ctx->domain_guid.type = AD_UUID;
9354d61c87Julian Pullen	ctx->domain_controller.type = AD_DIRECTORY;
936b3700b0Gordon Ross	ctx->preferred_dc.type = AD_DIRECTORY;
9374d61c87Julian Pullen	ctx->site_name.type = AD_STRING;
9384d61c87Julian Pullen	ctx->forest_name.type = AD_STRING;
9394d61c87Julian Pullen	ctx->global_catalog.type = AD_DIRECTORY;
9404d61c87Julian Pullen	ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST;
9414d61c87Julian Pullen	ctx->trusted_domains.type = AD_TRUSTED_DOMAINS;
9424d61c87Julian Pullen	/* Site specific versions */
9434d61c87Julian Pullen	ctx->site_domain_controller.type = AD_DIRECTORY;
9444d61c87Julian Pullen	ctx->site_global_catalog.type = AD_DIRECTORY;
945c8e2610jp	return (ctx);
949c8e2610jpad_disc_fini(ad_disc_t ctx)
951cd37da7nw	if (ctx == NULL)
952cd37da7nw		return;
9540dcc714nw	if (ctx->res_ninitted)
9554d61c87Julian Pullen		res_ndestroy(&ctx->res_state);
957c8e2610jp	if (ctx->subnets != NULL)
958c8e2610jp		free(ctx->subnets);
9604d61c87Julian Pullen	if (ctx->domain_name.value != NULL)
9614d61c87Julian Pullen		free(ctx->domain_name.value);
9624d61c87Julian Pullen
963b3700b0Gordon Ross	if (ctx->domain_guid.value != NULL)
964b3700b0Gordon Ross		free(ctx->domain_guid.value);
965b3700b0Gordon Ross
9664d61c87Julian Pullen	if (ctx->domain_controller.value != NULL)
9674d61c87Julian Pullen		free(ctx->domain_controller.value);
9684d61c87Julian Pullen
969b3700b0Gordon Ross	if (ctx->preferred_dc.value != NULL)
970b3700b0Gordon Ross		free(ctx->preferred_dc.value);
971b3700b0Gordon Ross
9724d61c87Julian Pullen	if (ctx->site_name.value != NULL)
9734d61c87Julian Pullen		free(ctx->site_name.value);
9754d61c87Julian Pullen	if (ctx->forest_name.value != NULL)
9764d61c87Julian Pullen		free(ctx->forest_name.value);
9784d61c87Julian Pullen	if (ctx->global_catalog.value != NULL)
9794d61c87Julian Pullen		free(ctx->global_catalog.value);
9814d61c87Julian Pullen	if (ctx->domains_in_forest.value != NULL)
9824d61c87Julian Pullen		free(ctx->domains_in_forest.value);
9844d61c87Julian Pullen	if (ctx->trusted_domains.value != NULL)
9854d61c87Julian Pullen		free(ctx->trusted_domains.value);
9874d61c87Julian Pullen	/* Site specific versions */
9884d61c87Julian Pullen	if (ctx->site_domain_controller.value != NULL)
9894d61c87Julian Pullen		free(ctx->site_domain_controller.value);
9914d61c87Julian Pullen	if (ctx->site_global_catalog.value != NULL)
9924d61c87Julian Pullen		free(ctx->site_global_catalog.value);
994c8e2610jp	free(ctx);
998c8e2610jpad_disc_refresh(ad_disc_t ctx)