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