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