xref: /titanic_50/usr/src/lib/libadutils/common/addisc.c (revision c586600796766c83eb9485c446886fd9ed2359a9)
17a8a68f5SJulian Pullen /*
27a8a68f5SJulian Pullen  * CDDL HEADER START
37a8a68f5SJulian Pullen  *
47a8a68f5SJulian Pullen  * The contents of this file are subject to the terms of the
57a8a68f5SJulian Pullen  * Common Development and Distribution License (the "License").
67a8a68f5SJulian Pullen  * You may not use this file except in compliance with the License.
77a8a68f5SJulian Pullen  *
87a8a68f5SJulian Pullen  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97a8a68f5SJulian Pullen  * or http://www.opensolaris.org/os/licensing.
107a8a68f5SJulian Pullen  * See the License for the specific language governing permissions
117a8a68f5SJulian Pullen  * and limitations under the License.
127a8a68f5SJulian Pullen  *
137a8a68f5SJulian Pullen  * When distributing Covered Code, include this CDDL HEADER in each
147a8a68f5SJulian Pullen  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157a8a68f5SJulian Pullen  * If applicable, add the following below this CDDL HEADER, with the
167a8a68f5SJulian Pullen  * fields enclosed by brackets "[]" replaced with your own identifying
177a8a68f5SJulian Pullen  * information: Portions Copyright [yyyy] [name of copyright owner]
187a8a68f5SJulian Pullen  *
197a8a68f5SJulian Pullen  * CDDL HEADER END
207a8a68f5SJulian Pullen  */
217a8a68f5SJulian Pullen 
227a8a68f5SJulian Pullen /*
23*c5866007SKeyur Desai  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
247a8a68f5SJulian Pullen  */
257a8a68f5SJulian Pullen 
267a8a68f5SJulian Pullen /*
277a8a68f5SJulian Pullen  * Active Directory Auto-Discovery.
287a8a68f5SJulian Pullen  *
297a8a68f5SJulian Pullen  * This [project private] API allows the caller to provide whatever
307a8a68f5SJulian Pullen  * details it knows a priori (i.e., provided via configuration so as to
317a8a68f5SJulian Pullen  * override auto-discovery) and in any order.  Then the caller can ask
327a8a68f5SJulian Pullen  * for any of the auto-discoverable parameters in any order.
337a8a68f5SJulian Pullen  *
347a8a68f5SJulian Pullen  * But there is an actual order in which discovery must be done.  Given
357a8a68f5SJulian Pullen  * the discovery mechanism implemented here, that order is:
367a8a68f5SJulian Pullen  *
377a8a68f5SJulian Pullen  *  - the domain name joined must be discovered first
387a8a68f5SJulian Pullen  *  - then the domain controllers
397a8a68f5SJulian Pullen  *  - then the forest name and site name
407a8a68f5SJulian Pullen  *  - then the global catalog servers, and site-specific domain
417a8a68f5SJulian Pullen  *    controllers and global catalog servers.
427a8a68f5SJulian Pullen  *
437a8a68f5SJulian Pullen  * The API does not require it be called in the same order because there
447a8a68f5SJulian Pullen  * may be other discovery mechanisms in the future, and exposing
457a8a68f5SJulian Pullen  * ordering requirements of the current mechanism now can create trouble
467a8a68f5SJulian Pullen  * down the line.  Also, this makes the API easier to use now, which
477a8a68f5SJulian Pullen  * means less work to do some day when we make this a public API.
487a8a68f5SJulian Pullen  *
497a8a68f5SJulian Pullen  * Domain discovery is done by res_nsearch() of the DNS SRV RR name for
507a8a68f5SJulian Pullen  * domain controllers.  As long as the joined domain appears in the DNS
517a8a68f5SJulian Pullen  * resolver's search list then we'll find it.
527a8a68f5SJulian Pullen  *
537a8a68f5SJulian Pullen  * Domain controller discovery is a matter of formatting the DNS SRV RR
547a8a68f5SJulian Pullen  * FQDN for domain controllers and doing a lookup for them.  Knowledge
557a8a68f5SJulian Pullen  * of the domain name is not fundamentally required, but we separate the
567a8a68f5SJulian Pullen  * two processes, which in practice can lead to one more DNS lookup than
577a8a68f5SJulian Pullen  * is strictly required.
587a8a68f5SJulian Pullen  *
597a8a68f5SJulian Pullen  * Forest and site name discovery require an LDAP search of the AD
607a8a68f5SJulian Pullen  * "configuration partition" at a domain controller for the joined
617a8a68f5SJulian Pullen  * domain.  Forest and site name discovery depend on knowing the joined
627a8a68f5SJulian Pullen  * domain name and domain controllers for that domain.
637a8a68f5SJulian Pullen  *
647a8a68f5SJulian Pullen  * Global catalog server discovery requires knowledge of the forest
657a8a68f5SJulian Pullen  * name in order to format the DNS SRV RR FQDN to lookup.  Site-specific
667a8a68f5SJulian Pullen  * domain controller discovery depends on knowing the site name (and,
677a8a68f5SJulian Pullen  * therefore, joined domain, ...).  Site-specific global catalog server
687a8a68f5SJulian Pullen  * discovery depends on knowledge of the forest and site names, which
697a8a68f5SJulian Pullen  * depend on...
707a8a68f5SJulian Pullen  *
717a8a68f5SJulian Pullen  * All the work of discovering particular items is done by functions
727a8a68f5SJulian Pullen  * named validate_<item>().  Each such function calls validate_<item>()
737a8a68f5SJulian Pullen  * for any items that it depends on.
747a8a68f5SJulian Pullen  *
757a8a68f5SJulian Pullen  * This API is not thread-safe.
767a8a68f5SJulian Pullen  */
777a8a68f5SJulian Pullen 
787a8a68f5SJulian Pullen 
797a8a68f5SJulian Pullen #include <stdio.h>
807a8a68f5SJulian Pullen #include <string.h>
817a8a68f5SJulian Pullen #include <strings.h>
827a8a68f5SJulian Pullen #include <unistd.h>
837a8a68f5SJulian Pullen #include <assert.h>
847a8a68f5SJulian Pullen #include <stdlib.h>
857a8a68f5SJulian Pullen #include <net/if.h>
867a8a68f5SJulian Pullen #include <net/if.h>
877a8a68f5SJulian Pullen #include <sys/types.h>
887a8a68f5SJulian Pullen #include <sys/socket.h>
897a8a68f5SJulian Pullen #include <sys/sockio.h>
907a8a68f5SJulian Pullen #include <netinet/in.h>
917a8a68f5SJulian Pullen #include <netinet/in.h>
927a8a68f5SJulian Pullen #include <arpa/inet.h>
937a8a68f5SJulian Pullen #include <arpa/nameser.h>
947a8a68f5SJulian Pullen #include <resolv.h>
957a8a68f5SJulian Pullen #include <netdb.h>
967a8a68f5SJulian Pullen #include <ctype.h>
977a8a68f5SJulian Pullen #include <errno.h>
987a8a68f5SJulian Pullen #include <ldap.h>
997a8a68f5SJulian Pullen #include <sasl/sasl.h>
1007a8a68f5SJulian Pullen #include <sys/u8_textprep.h>
1017a8a68f5SJulian Pullen #include <syslog.h>
1027a8a68f5SJulian Pullen #include "adutils_impl.h"
1037a8a68f5SJulian Pullen #include "addisc.h"
1047a8a68f5SJulian Pullen 
105*c5866007SKeyur Desai /*
106*c5866007SKeyur Desai  * These set some sanity policies for discovery.  After a discovery
107*c5866007SKeyur Desai  * cycle, we will consider the results (successful or unsuccessful)
108*c5866007SKeyur Desai  * to be valid for at least MINIMUM_TTL seconds, and for at most
109*c5866007SKeyur Desai  * MAXIMUM_TTL seconds.  Note that the caller is free to request
110*c5866007SKeyur Desai  * discovery cycles sooner than MINIMUM_TTL if it has reason to believe
111*c5866007SKeyur Desai  * that the situation has changed.
112*c5866007SKeyur Desai  */
113*c5866007SKeyur Desai #define	MINIMUM_TTL	(5 * 60)
114*c5866007SKeyur Desai #define	MAXIMUM_TTL	(20 * 60)
1157a8a68f5SJulian Pullen 
1167a8a68f5SJulian Pullen enum ad_item_state {
1177a8a68f5SJulian Pullen 		AD_STATE_INVALID = 0,	/* The value is not valid */
1187a8a68f5SJulian Pullen 		AD_STATE_FIXED,		/* The value was fixed by caller */
1197a8a68f5SJulian Pullen 		AD_STATE_AUTO		/* The value is auto discovered */
1207a8a68f5SJulian Pullen 		};
1217a8a68f5SJulian Pullen 
1227a8a68f5SJulian Pullen enum ad_data_type {
1237a8a68f5SJulian Pullen 		AD_STRING = 123,
1247a8a68f5SJulian Pullen 		AD_DIRECTORY,
1257a8a68f5SJulian Pullen 		AD_DOMAINS_IN_FOREST,
1267a8a68f5SJulian Pullen 		AD_TRUSTED_DOMAINS
1277a8a68f5SJulian Pullen 		};
1287a8a68f5SJulian Pullen 
1297a8a68f5SJulian Pullen 
1307a8a68f5SJulian Pullen typedef struct ad_subnet {
1317a8a68f5SJulian Pullen 	char subnet[24];
1327a8a68f5SJulian Pullen } ad_subnet_t;
1337a8a68f5SJulian Pullen 
1347a8a68f5SJulian Pullen 
1357a8a68f5SJulian Pullen typedef struct ad_item {
1367a8a68f5SJulian Pullen 	enum ad_item_state	state;
1377a8a68f5SJulian Pullen 	enum ad_data_type	type;
1387a8a68f5SJulian Pullen 	void 			*value;
139*c5866007SKeyur Desai 	time_t 			expires;
1407a8a68f5SJulian Pullen 	unsigned int 		version;	/* Version is only changed */
1417a8a68f5SJulian Pullen 						/* if the value changes */
1427a8a68f5SJulian Pullen #define	PARAM1		0
1437a8a68f5SJulian Pullen #define	PARAM2		1
1447a8a68f5SJulian Pullen 	int 		param_version[2];
1457a8a68f5SJulian Pullen 					/* These holds the version of */
1467a8a68f5SJulian Pullen 					/* dependents so that a dependent */
1477a8a68f5SJulian Pullen 					/* change can be detected */
1487a8a68f5SJulian Pullen } ad_item_t;
1497a8a68f5SJulian Pullen 
1507a8a68f5SJulian Pullen typedef struct ad_disc {
1517a8a68f5SJulian Pullen 	struct __res_state res_state;
1527a8a68f5SJulian Pullen 	int		res_ninitted;
1537a8a68f5SJulian Pullen 	ad_subnet_t	*subnets;
1547a8a68f5SJulian Pullen 	boolean_t	subnets_changed;
1557a8a68f5SJulian Pullen 	time_t		subnets_last_check;
156*c5866007SKeyur Desai 	time_t		expires_not_before;
157*c5866007SKeyur Desai 	time_t		expires_not_after;
1587a8a68f5SJulian Pullen 	ad_item_t	domain_name;		/* DNS hostname string */
1597a8a68f5SJulian Pullen 	ad_item_t	domain_controller;	/* Directory hostname and */
1607a8a68f5SJulian Pullen 						/* port array */
1617a8a68f5SJulian Pullen 	ad_item_t	site_name;		/* String */
1627a8a68f5SJulian Pullen 	ad_item_t	forest_name;		/* DNS forestname string */
1637a8a68f5SJulian Pullen 	ad_item_t	global_catalog;		/* Directory hostname and */
1647a8a68f5SJulian Pullen 						/* port array */
1657a8a68f5SJulian Pullen 	ad_item_t	domains_in_forest;	/* DNS domainname and SID */
1667a8a68f5SJulian Pullen 						/* array */
1677a8a68f5SJulian Pullen 	ad_item_t	trusted_domains;	/* DNS domainname and trust */
1687a8a68f5SJulian Pullen 						/* direction array */
1697a8a68f5SJulian Pullen 	/* Site specfic versions */
1707a8a68f5SJulian Pullen 	ad_item_t	site_domain_controller;	/* Directory hostname and */
1717a8a68f5SJulian Pullen 						/* port array */
1727a8a68f5SJulian Pullen 	ad_item_t	site_global_catalog;	/* Directory hostname and */
1737a8a68f5SJulian Pullen 						/* port array */
1747a8a68f5SJulian Pullen } ad_disc;
1757a8a68f5SJulian Pullen 
1767a8a68f5SJulian Pullen 
1777a8a68f5SJulian Pullen #define	DNS_MAX_NAME	NS_MAXDNAME
1787a8a68f5SJulian Pullen 
1797a8a68f5SJulian Pullen 
1807a8a68f5SJulian Pullen /* SRV RR names for various queries */
1817a8a68f5SJulian Pullen #define	LDAP_SRV_HEAD		"_ldap._tcp."
1827a8a68f5SJulian Pullen #define	SITE_SRV_MIDDLE		"%s._sites."
1837a8a68f5SJulian Pullen #define	GC_SRV_TAIL		"gc._msdcs"
1847a8a68f5SJulian Pullen #define	DC_SRV_TAIL		"dc._msdcs"
1857a8a68f5SJulian Pullen #define	ALL_GC_SRV_TAIL		"_gc._tcp"
1867a8a68f5SJulian Pullen #define	PDC_SRV			 "_ldap._tcp.pdc._msdcs.%s"
1877a8a68f5SJulian Pullen 
1887a8a68f5SJulian Pullen /* A RR name for all GCs -- last resort this works */
1897a8a68f5SJulian Pullen #define	GC_ALL_A_NAME_FSTR "gc._msdcs.%s."
1907a8a68f5SJulian Pullen 
1917a8a68f5SJulian Pullen 
1927a8a68f5SJulian Pullen /*
1937a8a68f5SJulian Pullen  * We try res_ninit() whenever we don't have one.  res_ninit() fails if
1947a8a68f5SJulian Pullen  * idmapd is running before the network is up!
1957a8a68f5SJulian Pullen  */
1967a8a68f5SJulian Pullen #define	DO_RES_NINIT(ctx)   if (!(ctx)->res_ninitted) \
1977a8a68f5SJulian Pullen 		(ctx)->res_ninitted = (res_ninit(&ctx->res_state) != -1)
1987a8a68f5SJulian Pullen 
1997a8a68f5SJulian Pullen #define	is_fixed(item)					\
2007a8a68f5SJulian Pullen 	((item)->state == AD_STATE_FIXED)
2017a8a68f5SJulian Pullen 
2027a8a68f5SJulian Pullen #define	is_changed(item, num, param) 			\
2037a8a68f5SJulian Pullen 	((item)->param_version[num] != (param)->version)
2047a8a68f5SJulian Pullen 
2057a8a68f5SJulian Pullen /*LINTLIBRARY*/
2067a8a68f5SJulian Pullen 
2077a8a68f5SJulian Pullen /*
2087a8a68f5SJulian Pullen  * Function definitions
2097a8a68f5SJulian Pullen  */
2107a8a68f5SJulian Pullen static ad_item_t *
2117a8a68f5SJulian Pullen validate_SiteName(ad_disc_t ctx);
2127a8a68f5SJulian Pullen 
2137a8a68f5SJulian Pullen 
2147a8a68f5SJulian Pullen 
2157a8a68f5SJulian Pullen static void
2167a8a68f5SJulian Pullen update_version(ad_item_t *item, int  num, ad_item_t *param)
2177a8a68f5SJulian Pullen {
2187a8a68f5SJulian Pullen 	item->param_version[num] = param->version;
2197a8a68f5SJulian Pullen }
2207a8a68f5SJulian Pullen 
2217a8a68f5SJulian Pullen 
2227a8a68f5SJulian Pullen 
2237a8a68f5SJulian Pullen static boolean_t
2247a8a68f5SJulian Pullen is_valid(ad_item_t *item)
2257a8a68f5SJulian Pullen {
2267a8a68f5SJulian Pullen 	if (item->value != NULL) {
2277a8a68f5SJulian Pullen 		if (item->state == AD_STATE_FIXED)
2287a8a68f5SJulian Pullen 			return (B_TRUE);
2297a8a68f5SJulian Pullen 		if (item->state == AD_STATE_AUTO &&
230*c5866007SKeyur Desai 		    (item->expires == 0 || item->expires > time(NULL)))
2317a8a68f5SJulian Pullen 			return (B_TRUE);
2327a8a68f5SJulian Pullen 	}
2337a8a68f5SJulian Pullen 	return (B_FALSE);
2347a8a68f5SJulian Pullen }
2357a8a68f5SJulian Pullen 
2367a8a68f5SJulian Pullen 
2377a8a68f5SJulian Pullen static void
2387a8a68f5SJulian Pullen update_item(ad_item_t *item, void *value, enum ad_item_state state,
2397a8a68f5SJulian Pullen 		uint32_t ttl)
2407a8a68f5SJulian Pullen {
2417a8a68f5SJulian Pullen 	if (item->value != NULL && value != NULL) {
2427a8a68f5SJulian Pullen 		if ((item->type == AD_STRING &&
2437a8a68f5SJulian Pullen 		    strcmp(item->value, value) != 0) ||
2447a8a68f5SJulian Pullen 		    (item->type == AD_DIRECTORY &&
2457a8a68f5SJulian Pullen 		    ad_disc_compare_ds(item->value, value) != 0)||
2467a8a68f5SJulian Pullen 		    (item->type == AD_DOMAINS_IN_FOREST &&
2477a8a68f5SJulian Pullen 		    ad_disc_compare_domainsinforest(item->value, value) != 0) ||
2487a8a68f5SJulian Pullen 		    (item->type == AD_TRUSTED_DOMAINS &&
2497a8a68f5SJulian Pullen 		    ad_disc_compare_trusteddomains(item->value, value) != 0))
2507a8a68f5SJulian Pullen 			item->version++;
2517a8a68f5SJulian Pullen 	} else if (item->value != value)
2527a8a68f5SJulian Pullen 		item->version++;
2537a8a68f5SJulian Pullen 
2547a8a68f5SJulian Pullen 	if (item->value != NULL)
2557a8a68f5SJulian Pullen 		free(item->value);
2567a8a68f5SJulian Pullen 
2577a8a68f5SJulian Pullen 	item->value = value;
2587a8a68f5SJulian Pullen 	item->state = state;
2597a8a68f5SJulian Pullen 
2607a8a68f5SJulian Pullen 	if (ttl == 0)
261*c5866007SKeyur Desai 		item->expires = 0;
2627a8a68f5SJulian Pullen 	else
263*c5866007SKeyur Desai 		item->expires = time(NULL) + ttl;
2647a8a68f5SJulian Pullen }
2657a8a68f5SJulian Pullen 
2667a8a68f5SJulian Pullen 
2677a8a68f5SJulian Pullen /* Compare DS lists */
2687a8a68f5SJulian Pullen int
2697a8a68f5SJulian Pullen ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2)
2707a8a68f5SJulian Pullen {
2717a8a68f5SJulian Pullen 	int		i, j;
2727a8a68f5SJulian Pullen 	int		num_ds1;
2737a8a68f5SJulian Pullen 	int		num_ds2;
2747a8a68f5SJulian Pullen 	boolean_t	match;
2757a8a68f5SJulian Pullen 
2767a8a68f5SJulian Pullen 	for (i = 0; ds1[i].host[0] != '\0'; i++)
2777a8a68f5SJulian Pullen 		continue;
2787a8a68f5SJulian Pullen 	num_ds1 = i;
2797a8a68f5SJulian Pullen 	for (j = 0; ds2[j].host[0] != '\0'; j++)
2807a8a68f5SJulian Pullen 		continue;
2817a8a68f5SJulian Pullen 	num_ds2 = j;
2827a8a68f5SJulian Pullen 	if (num_ds1 != num_ds2)
2837a8a68f5SJulian Pullen 		return (1);
2847a8a68f5SJulian Pullen 
2857a8a68f5SJulian Pullen 	for (i = 0; i < num_ds1; i++) {
2867a8a68f5SJulian Pullen 		match = B_FALSE;
2877a8a68f5SJulian Pullen 		for (j = 0; j < num_ds2; j++) {
288928e1f97SJordan Brown 			if (strcmp(ds1[i].host, ds2[j].host) == 0 &&
289928e1f97SJordan Brown 			    ds1[i].port == ds2[j].port) {
2907a8a68f5SJulian Pullen 				match = B_TRUE;
2917a8a68f5SJulian Pullen 				break;
2927a8a68f5SJulian Pullen 			}
2937a8a68f5SJulian Pullen 		}
2947a8a68f5SJulian Pullen 		if (!match)
2957a8a68f5SJulian Pullen 			return (1);
2967a8a68f5SJulian Pullen 	}
2977a8a68f5SJulian Pullen 	return (0);
2987a8a68f5SJulian Pullen }
2997a8a68f5SJulian Pullen 
3007a8a68f5SJulian Pullen 
3017a8a68f5SJulian Pullen /* Copy a list of DSs */
3027a8a68f5SJulian Pullen static idmap_ad_disc_ds_t *
3037a8a68f5SJulian Pullen ds_dup(const idmap_ad_disc_ds_t *srv)
3047a8a68f5SJulian Pullen {
3057a8a68f5SJulian Pullen 	int	i;
3067a8a68f5SJulian Pullen 	int	size;
3077a8a68f5SJulian Pullen 	idmap_ad_disc_ds_t *new = NULL;
3087a8a68f5SJulian Pullen 
3097a8a68f5SJulian Pullen 	for (i = 0; srv[i].host[0] != '\0'; i++)
3107a8a68f5SJulian Pullen 		continue;
3117a8a68f5SJulian Pullen 
3127a8a68f5SJulian Pullen 	size = (i + 1) * sizeof (idmap_ad_disc_ds_t);
3137a8a68f5SJulian Pullen 	new = malloc(size);
3147a8a68f5SJulian Pullen 	if (new != NULL)
3157a8a68f5SJulian Pullen 		memcpy(new, srv, size);
3167a8a68f5SJulian Pullen 	return (new);
3177a8a68f5SJulian Pullen }
3187a8a68f5SJulian Pullen 
3197a8a68f5SJulian Pullen 
3207a8a68f5SJulian Pullen int
3217a8a68f5SJulian Pullen ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1,
3227a8a68f5SJulian Pullen 			ad_disc_trusteddomains_t *td2)
3237a8a68f5SJulian Pullen {
3247a8a68f5SJulian Pullen 	int		i, j;
3257a8a68f5SJulian Pullen 	int		num_td1;
3267a8a68f5SJulian Pullen 	int		num_td2;
3277a8a68f5SJulian Pullen 	boolean_t	match;
3287a8a68f5SJulian Pullen 
3297a8a68f5SJulian Pullen 	for (i = 0; td1[i].domain[0] != '\0'; i++)
3307a8a68f5SJulian Pullen 		continue;
3317a8a68f5SJulian Pullen 	num_td1 = i;
3327a8a68f5SJulian Pullen 
3337a8a68f5SJulian Pullen 	for (j = 0; td2[j].domain[0] != '\0'; j++)
3347a8a68f5SJulian Pullen 		continue;
3357a8a68f5SJulian Pullen 	num_td2 = j;
3367a8a68f5SJulian Pullen 
3377a8a68f5SJulian Pullen 	if (num_td1 != num_td2)
3387a8a68f5SJulian Pullen 		return (1);
3397a8a68f5SJulian Pullen 
3407a8a68f5SJulian Pullen 	for (i = 0; i < num_td1; i++) {
3417a8a68f5SJulian Pullen 		match = B_FALSE;
3427a8a68f5SJulian Pullen 		for (j = 0; j < num_td2; j++) {
3431fcced4cSJordan Brown 			if (domain_eq(td1[i].domain, td2[j].domain)) {
3447a8a68f5SJulian Pullen 				match = B_TRUE;
3457a8a68f5SJulian Pullen 				break;
3467a8a68f5SJulian Pullen 			}
3477a8a68f5SJulian Pullen 		}
3487a8a68f5SJulian Pullen 		if (!match)
3497a8a68f5SJulian Pullen 			return (1);
3507a8a68f5SJulian Pullen 	}
3517a8a68f5SJulian Pullen 	return (0);
3527a8a68f5SJulian Pullen }
3537a8a68f5SJulian Pullen 
3547a8a68f5SJulian Pullen 
3557a8a68f5SJulian Pullen 
3567a8a68f5SJulian Pullen /* Copy a list of Trusted Domains */
3577a8a68f5SJulian Pullen static ad_disc_trusteddomains_t *
3587a8a68f5SJulian Pullen td_dup(const ad_disc_trusteddomains_t *td)
3597a8a68f5SJulian Pullen {
3607a8a68f5SJulian Pullen 	int	i;
3617a8a68f5SJulian Pullen 	int	size;
3627a8a68f5SJulian Pullen 	ad_disc_trusteddomains_t *new = NULL;
3637a8a68f5SJulian Pullen 
3647a8a68f5SJulian Pullen 	for (i = 0; td[i].domain[0] != '\0'; i++)
3657a8a68f5SJulian Pullen 		continue;
3667a8a68f5SJulian Pullen 
3677a8a68f5SJulian Pullen 	size = (i + 1) * sizeof (ad_disc_trusteddomains_t);
3687a8a68f5SJulian Pullen 	new = malloc(size);
3697a8a68f5SJulian Pullen 	if (new != NULL)
3707a8a68f5SJulian Pullen 		memcpy(new, td, size);
3717a8a68f5SJulian Pullen 	return (new);
3727a8a68f5SJulian Pullen }
3737a8a68f5SJulian Pullen 
3747a8a68f5SJulian Pullen 
3757a8a68f5SJulian Pullen 
3767a8a68f5SJulian Pullen int
3777a8a68f5SJulian Pullen ad_disc_compare_domainsinforest(ad_disc_domainsinforest_t *df1,
3787a8a68f5SJulian Pullen 			ad_disc_domainsinforest_t *df2)
3797a8a68f5SJulian Pullen {
3807a8a68f5SJulian Pullen 	int		i, j;
3817a8a68f5SJulian Pullen 	int		num_df1;
3827a8a68f5SJulian Pullen 	int		num_df2;
3837a8a68f5SJulian Pullen 	boolean_t	match;
3847a8a68f5SJulian Pullen 
3857a8a68f5SJulian Pullen 	for (i = 0; df1[i].domain[0] != '\0'; i++)
3867a8a68f5SJulian Pullen 		continue;
3877a8a68f5SJulian Pullen 	num_df1 = i;
3887a8a68f5SJulian Pullen 
3897a8a68f5SJulian Pullen 	for (j = 0; df2[j].domain[0] != '\0'; j++)
3907a8a68f5SJulian Pullen 		continue;
3917a8a68f5SJulian Pullen 	num_df2 = j;
3927a8a68f5SJulian Pullen 
3937a8a68f5SJulian Pullen 	if (num_df1 != num_df2)
3947a8a68f5SJulian Pullen 		return (1);
3957a8a68f5SJulian Pullen 
3967a8a68f5SJulian Pullen 	for (i = 0; i < num_df1; i++) {
3977a8a68f5SJulian Pullen 		match = B_FALSE;
3987a8a68f5SJulian Pullen 		for (j = 0; j < num_df2; j++) {
3991fcced4cSJordan Brown 			if (domain_eq(df1[i].domain, df2[j].domain) &&
400928e1f97SJordan Brown 			    strcmp(df1[i].sid, df2[j].sid) == 0) {
4017a8a68f5SJulian Pullen 				match = B_TRUE;
4027a8a68f5SJulian Pullen 				break;
4037a8a68f5SJulian Pullen 			}
4047a8a68f5SJulian Pullen 		}
4057a8a68f5SJulian Pullen 		if (!match)
4067a8a68f5SJulian Pullen 			return (1);
4077a8a68f5SJulian Pullen 	}
4087a8a68f5SJulian Pullen 	return (0);
4097a8a68f5SJulian Pullen }
4107a8a68f5SJulian Pullen 
4117a8a68f5SJulian Pullen 
4127a8a68f5SJulian Pullen 
4137a8a68f5SJulian Pullen /* Copy a list of Trusted Domains */
4147a8a68f5SJulian Pullen static ad_disc_domainsinforest_t *
4157a8a68f5SJulian Pullen df_dup(const ad_disc_domainsinforest_t *df)
4167a8a68f5SJulian Pullen {
4177a8a68f5SJulian Pullen 	int	i;
4187a8a68f5SJulian Pullen 	int	size;
4197a8a68f5SJulian Pullen 	ad_disc_domainsinforest_t *new = NULL;
4207a8a68f5SJulian Pullen 
4217a8a68f5SJulian Pullen 	for (i = 0; df[i].domain[0] != '\0'; i++)
4227a8a68f5SJulian Pullen 		continue;
4237a8a68f5SJulian Pullen 
4247a8a68f5SJulian Pullen 	size = (i + 1) * sizeof (ad_disc_domainsinforest_t);
4257a8a68f5SJulian Pullen 	new = malloc(size);
4267a8a68f5SJulian Pullen 	if (new != NULL)
4277a8a68f5SJulian Pullen 		memcpy(new, df, size);
4287a8a68f5SJulian Pullen 	return (new);
4297a8a68f5SJulian Pullen }
4307a8a68f5SJulian Pullen 
4317a8a68f5SJulian Pullen 
4327a8a68f5SJulian Pullen 
4337a8a68f5SJulian Pullen 
4347a8a68f5SJulian Pullen 
4357a8a68f5SJulian Pullen /*
4367a8a68f5SJulian Pullen  * Returns an array of IPv4 address/prefix length
4377a8a68f5SJulian Pullen  * The last subnet is NULL
4387a8a68f5SJulian Pullen  */
4397a8a68f5SJulian Pullen static ad_subnet_t *
4407a8a68f5SJulian Pullen find_subnets()
4417a8a68f5SJulian Pullen {
4427a8a68f5SJulian Pullen 	int		sock, n, i;
4437a8a68f5SJulian Pullen 	struct lifconf	lifc;
4447a8a68f5SJulian Pullen 	struct lifreq	lifr, *lifrp;
4457a8a68f5SJulian Pullen 	struct lifnum	lifn;
4467a8a68f5SJulian Pullen 	uint32_t	prefix_len;
4477a8a68f5SJulian Pullen 	char		*s;
4487a8a68f5SJulian Pullen 	ad_subnet_t	*results;
4497a8a68f5SJulian Pullen 
4507a8a68f5SJulian Pullen 	lifrp = &lifr;
4517a8a68f5SJulian Pullen 
4527a8a68f5SJulian Pullen 	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
4537a8a68f5SJulian Pullen 		logger(LOG_ERR, "Failed to open IPv4 socket for "
4547a8a68f5SJulian Pullen 		    "listing network interfaces (%s)", strerror(errno));
4557a8a68f5SJulian Pullen 		return (NULL);
4567a8a68f5SJulian Pullen 	}
4577a8a68f5SJulian Pullen 
4587a8a68f5SJulian Pullen 	lifn.lifn_family = AF_INET;
4597a8a68f5SJulian Pullen 	lifn.lifn_flags = 0;
4607a8a68f5SJulian Pullen 	if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
4617a8a68f5SJulian Pullen 		logger(LOG_ERR,
4627a8a68f5SJulian Pullen 		    "Failed to find the number of network interfaces (%s)",
4637a8a68f5SJulian Pullen 		    strerror(errno));
4647a8a68f5SJulian Pullen 		close(sock);
4657a8a68f5SJulian Pullen 		return (NULL);
4667a8a68f5SJulian Pullen 	}
4677a8a68f5SJulian Pullen 
4687a8a68f5SJulian Pullen 	if (lifn.lifn_count < 1) {
4697a8a68f5SJulian Pullen 		logger(LOG_ERR, "No IPv4 network interfaces found");
4707a8a68f5SJulian Pullen 		close(sock);
4717a8a68f5SJulian Pullen 		return (NULL);
4727a8a68f5SJulian Pullen 	}
4737a8a68f5SJulian Pullen 
4747a8a68f5SJulian Pullen 	lifc.lifc_family = AF_INET;
4757a8a68f5SJulian Pullen 	lifc.lifc_flags = 0;
4767a8a68f5SJulian Pullen 	lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
4777a8a68f5SJulian Pullen 	lifc.lifc_buf = malloc(lifc.lifc_len);
4787a8a68f5SJulian Pullen 
4797a8a68f5SJulian Pullen 	if (lifc.lifc_buf == NULL) {
4807a8a68f5SJulian Pullen 		logger(LOG_ERR, "Out of memory");
4817a8a68f5SJulian Pullen 		close(sock);
4827a8a68f5SJulian Pullen 		return (NULL);
4837a8a68f5SJulian Pullen 	}
4847a8a68f5SJulian Pullen 
4857a8a68f5SJulian Pullen 	if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) {
4867a8a68f5SJulian Pullen 		logger(LOG_ERR, "Failed to list network interfaces (%s)",
4877a8a68f5SJulian Pullen 		    strerror(errno));
4887a8a68f5SJulian Pullen 		free(lifc.lifc_buf);
4897a8a68f5SJulian Pullen 		close(sock);
4907a8a68f5SJulian Pullen 		return (NULL);
4917a8a68f5SJulian Pullen 	}
4927a8a68f5SJulian Pullen 
4937a8a68f5SJulian Pullen 	n = lifc.lifc_len / (int)sizeof (struct lifreq);
4947a8a68f5SJulian Pullen 
4957a8a68f5SJulian Pullen 	if ((results = calloc(n + 1, sizeof (ad_subnet_t))) == NULL) {
4967a8a68f5SJulian Pullen 		free(lifc.lifc_buf);
4977a8a68f5SJulian Pullen 		close(sock);
4987a8a68f5SJulian Pullen 		return (NULL);
4997a8a68f5SJulian Pullen 	}
5007a8a68f5SJulian Pullen 
5017a8a68f5SJulian Pullen 	for (i = 0, lifrp = lifc.lifc_req; i < n; i++, lifrp++) {
5027a8a68f5SJulian Pullen 		if (ioctl(sock, SIOCGLIFFLAGS, lifrp) < 0)
5037a8a68f5SJulian Pullen 			continue;
5047a8a68f5SJulian Pullen 
5057a8a68f5SJulian Pullen 		if ((lifrp->lifr_flags & IFF_UP) == 0)
5067a8a68f5SJulian Pullen 			continue;
5077a8a68f5SJulian Pullen 
5087a8a68f5SJulian Pullen 		if (ioctl(sock, SIOCGLIFSUBNET, lifrp) < 0)
5097a8a68f5SJulian Pullen 			continue;
5107a8a68f5SJulian Pullen 
5117a8a68f5SJulian Pullen 		prefix_len = lifrp->lifr_addrlen;
5127a8a68f5SJulian Pullen 
5137a8a68f5SJulian Pullen 		s = inet_ntoa(((struct sockaddr_in *)
5147a8a68f5SJulian Pullen 		    &lifrp->lifr_addr)->sin_addr);
5157a8a68f5SJulian Pullen 
5167a8a68f5SJulian Pullen 		(void) snprintf(results[i].subnet, sizeof (ad_subnet_t),
5177a8a68f5SJulian Pullen 		    "%s/%d", s, prefix_len);
5187a8a68f5SJulian Pullen 	}
5197a8a68f5SJulian Pullen 
5207a8a68f5SJulian Pullen 	free(lifc.lifc_buf);
5217a8a68f5SJulian Pullen 	close(sock);
5227a8a68f5SJulian Pullen 
5237a8a68f5SJulian Pullen 	return (results);
5247a8a68f5SJulian Pullen }
5257a8a68f5SJulian Pullen 
5267a8a68f5SJulian Pullen static int
5277a8a68f5SJulian Pullen cmpsubnets(ad_subnet_t *subnets1, ad_subnet_t *subnets2)
5287a8a68f5SJulian Pullen {
5297a8a68f5SJulian Pullen 	int num_subnets1;
5307a8a68f5SJulian Pullen 	int num_subnets2;
5317a8a68f5SJulian Pullen 	boolean_t matched;
5327a8a68f5SJulian Pullen 	int i, j;
5337a8a68f5SJulian Pullen 
5347a8a68f5SJulian Pullen 	for (i = 0; subnets1[i].subnet[0] != '\0'; i++)
5357a8a68f5SJulian Pullen 		continue;
5367a8a68f5SJulian Pullen 	num_subnets1 = i;
5377a8a68f5SJulian Pullen 
5387a8a68f5SJulian Pullen 	for (i = 0; subnets2[i].subnet[0] != '\0'; i++)
5397a8a68f5SJulian Pullen 		continue;
5407a8a68f5SJulian Pullen 	num_subnets2 = i;
5417a8a68f5SJulian Pullen 
5427a8a68f5SJulian Pullen 	if (num_subnets1 != num_subnets2)
5437a8a68f5SJulian Pullen 		return (1);
5447a8a68f5SJulian Pullen 
5457a8a68f5SJulian Pullen 	for (i = 0;  i < num_subnets1; i++) {
5467a8a68f5SJulian Pullen 		matched = B_FALSE;
5477a8a68f5SJulian Pullen 		for (j = 0; j < num_subnets2; j++) {
5487a8a68f5SJulian Pullen 			if (strcmp(subnets1[i].subnet,
5497a8a68f5SJulian Pullen 			    subnets2[j].subnet) == 0) {
5507a8a68f5SJulian Pullen 				matched = B_TRUE;
5517a8a68f5SJulian Pullen 				break;
5527a8a68f5SJulian Pullen 			}
5537a8a68f5SJulian Pullen 		}
5547a8a68f5SJulian Pullen 		if (!matched)
5557a8a68f5SJulian Pullen 			return (1);
5567a8a68f5SJulian Pullen 	}
5577a8a68f5SJulian Pullen 	return (0);
5587a8a68f5SJulian Pullen }
5597a8a68f5SJulian Pullen 
5607a8a68f5SJulian Pullen 
5617a8a68f5SJulian Pullen 
5627a8a68f5SJulian Pullen 
5637a8a68f5SJulian Pullen /* Convert a DN's DC components into a DNS domainname */
5647a8a68f5SJulian Pullen char *
5657a8a68f5SJulian Pullen DN_to_DNS(const char *dn_name)
5667a8a68f5SJulian Pullen {
5677a8a68f5SJulian Pullen 	char	dns[DNS_MAX_NAME];
5687a8a68f5SJulian Pullen 	char	*dns_name;
5697a8a68f5SJulian Pullen 	int	i, j;
5707a8a68f5SJulian Pullen 	int	num = 0;
5717a8a68f5SJulian Pullen 
5727a8a68f5SJulian Pullen 	j = 0;
5737a8a68f5SJulian Pullen 	i = 0;
5747a8a68f5SJulian Pullen 
5757a8a68f5SJulian Pullen 	if (dn_name == NULL)
5767a8a68f5SJulian Pullen 		return (NULL);
5777a8a68f5SJulian Pullen 	/*
5787a8a68f5SJulian Pullen 	 * Find all DC=<value> and form DNS name of the
5797a8a68f5SJulian Pullen 	 * form <value1>.<value2>...
5807a8a68f5SJulian Pullen 	 */
5817a8a68f5SJulian Pullen 	while (dn_name[i] != '\0') {
5827a8a68f5SJulian Pullen 		if (strncasecmp(&dn_name[i], "DC=", 3) == 0) {
5837a8a68f5SJulian Pullen 			i += 3;
5847a8a68f5SJulian Pullen 			if (dn_name[i] != '\0' && num > 0)
5857a8a68f5SJulian Pullen 				dns[j++] = '.';
5867a8a68f5SJulian Pullen 			while (dn_name[i] != '\0' &&
5877a8a68f5SJulian Pullen 			    dn_name[i] != ',' && dn_name[i] != '+')
5887a8a68f5SJulian Pullen 				dns[j++] = dn_name[i++];
5897a8a68f5SJulian Pullen 			num++;
5907a8a68f5SJulian Pullen 		} else {
5917a8a68f5SJulian Pullen 			/* Skip attr=value as it is not DC= */
5927a8a68f5SJulian Pullen 			while (dn_name[i] != '\0' &&
5937a8a68f5SJulian Pullen 			    dn_name[i] != ',' && dn_name[i] != '+')
5947a8a68f5SJulian Pullen 				i++;
5957a8a68f5SJulian Pullen 		}
5967a8a68f5SJulian Pullen 		/* Skip over separator ','  or '+' */
5977a8a68f5SJulian Pullen 		if (dn_name[i] != '\0') i++;
5987a8a68f5SJulian Pullen 	}
5997a8a68f5SJulian Pullen 	dns[j] = '\0';
6007a8a68f5SJulian Pullen 	dns_name = malloc(j + 1);
6017a8a68f5SJulian Pullen 	if (dns_name != NULL)
6027a8a68f5SJulian Pullen 		(void) strlcpy(dns_name, dns, j + 1);
6037a8a68f5SJulian Pullen 	return (dns_name);
6047a8a68f5SJulian Pullen }
6057a8a68f5SJulian Pullen 
6067a8a68f5SJulian Pullen 
6077a8a68f5SJulian Pullen /* Make a list of subnet object DNs from a list of subnets */
6087a8a68f5SJulian Pullen static char **
6097a8a68f5SJulian Pullen subnets_to_DNs(ad_subnet_t *subnets, const char *base_dn)
6107a8a68f5SJulian Pullen {
6117a8a68f5SJulian Pullen 	char **results;
6127a8a68f5SJulian Pullen 	int i, j;
6137a8a68f5SJulian Pullen 
6147a8a68f5SJulian Pullen 	for (i = 0; subnets[i].subnet[0] != '\0'; i++)
6157a8a68f5SJulian Pullen 		continue;
6167a8a68f5SJulian Pullen 
6177a8a68f5SJulian Pullen 	results = calloc(i + 1, sizeof (char *));
6187a8a68f5SJulian Pullen 	if (results == NULL)
6197a8a68f5SJulian Pullen 		return (NULL);
6207a8a68f5SJulian Pullen 
6217a8a68f5SJulian Pullen 	for (i = 0; subnets[i].subnet[0] != '\0'; i++) {
622*c5866007SKeyur Desai 		(void) asprintf(&results[i], "CN=%s,CN=Subnets,CN=Sites,%s",
623*c5866007SKeyur Desai 		    subnets[i].subnet, base_dn);
624*c5866007SKeyur Desai 		if (results[i] == NULL) {
6257a8a68f5SJulian Pullen 			for (j = 0; j < i; j++)
6267a8a68f5SJulian Pullen 				free(results[j]);
6277a8a68f5SJulian Pullen 			free(results);
6287a8a68f5SJulian Pullen 			return (NULL);
6297a8a68f5SJulian Pullen 		}
6307a8a68f5SJulian Pullen 	}
6317a8a68f5SJulian Pullen 
6327a8a68f5SJulian Pullen 	return (results);
6337a8a68f5SJulian Pullen }
6347a8a68f5SJulian Pullen 
6357a8a68f5SJulian Pullen 
6367a8a68f5SJulian Pullen /* Compare SRC RRs; used with qsort() */
6377a8a68f5SJulian Pullen static int
6387a8a68f5SJulian Pullen srvcmp(idmap_ad_disc_ds_t *s1, idmap_ad_disc_ds_t *s2)
6397a8a68f5SJulian Pullen {
6407a8a68f5SJulian Pullen 	if (s1->priority < s2->priority)
6417a8a68f5SJulian Pullen 		return (1);
6427a8a68f5SJulian Pullen 	else if (s1->priority > s2->priority)
6437a8a68f5SJulian Pullen 		return (-1);
6447a8a68f5SJulian Pullen 
6457a8a68f5SJulian Pullen 	if (s1->weight < s2->weight)
6467a8a68f5SJulian Pullen 		return (1);
6477a8a68f5SJulian Pullen 	else if (s1->weight > s2->weight)
6487a8a68f5SJulian Pullen 		return (-1);
6497a8a68f5SJulian Pullen 
6507a8a68f5SJulian Pullen 	return (0);
6517a8a68f5SJulian Pullen }
6527a8a68f5SJulian Pullen 
6537a8a68f5SJulian Pullen 
6547a8a68f5SJulian Pullen /*
6557a8a68f5SJulian Pullen  * Query or search the SRV RRs for a given name.
6567a8a68f5SJulian Pullen  *
6577a8a68f5SJulian Pullen  * If name == NULL then search (as in res_nsearch(3RESOLV), honoring any
6587a8a68f5SJulian Pullen  * search list/option), else query (as in res_nquery(3RESOLV)).
6597a8a68f5SJulian Pullen  *
6607a8a68f5SJulian Pullen  * The output TTL will be the one of the SRV RR with the lowest TTL.
6617a8a68f5SJulian Pullen  */
6627a8a68f5SJulian Pullen idmap_ad_disc_ds_t *
6637a8a68f5SJulian Pullen srv_query(res_state state, const char *svc_name, const char *dname,
6647a8a68f5SJulian Pullen 		char **rrname, uint32_t *ttl)
6657a8a68f5SJulian Pullen {
6667a8a68f5SJulian Pullen 	idmap_ad_disc_ds_t *srv;
667bbf6f00cSJordan Brown 	idmap_ad_disc_ds_t *srv_res = NULL;
6687a8a68f5SJulian Pullen 	union {
6697a8a68f5SJulian Pullen 		HEADER hdr;
6707a8a68f5SJulian Pullen 		uchar_t buf[NS_MAXMSG];
6717a8a68f5SJulian Pullen 	} msg;
6727a8a68f5SJulian Pullen 	int len, cnt, qdcount, ancount;
6737a8a68f5SJulian Pullen 	uchar_t *ptr, *eom;
6747a8a68f5SJulian Pullen 	uchar_t *end;
6757a8a68f5SJulian Pullen 	uint16_t type;
6767a8a68f5SJulian Pullen 	/* LINTED  E_FUNC_SET_NOT_USED */
6777a8a68f5SJulian Pullen 	uint16_t class;
6787a8a68f5SJulian Pullen 	uint32_t rttl;
6797a8a68f5SJulian Pullen 	uint16_t size;
6807a8a68f5SJulian Pullen 	char namebuf[NS_MAXDNAME];
6817a8a68f5SJulian Pullen 
6827a8a68f5SJulian Pullen 	if (state == NULL)
6837a8a68f5SJulian Pullen 		return (NULL);
6847a8a68f5SJulian Pullen 
6857a8a68f5SJulian Pullen 	/* Set negative result TTL */
6867a8a68f5SJulian Pullen 	*ttl = 5 * 60;
6877a8a68f5SJulian Pullen 
6887a8a68f5SJulian Pullen 	/* 1. query necessary resource records */
6897a8a68f5SJulian Pullen 
6907a8a68f5SJulian Pullen 	/* Search, querydomain or query */
6917a8a68f5SJulian Pullen 	if (rrname != NULL) {
6927a8a68f5SJulian Pullen 		*rrname = NULL;
6937a8a68f5SJulian Pullen 		len = res_nsearch(state, svc_name, C_IN, T_SRV,
6947a8a68f5SJulian Pullen 		    msg.buf, sizeof (msg.buf));
6957a8a68f5SJulian Pullen 		logger(LOG_DEBUG, "Searching DNS for SRV RRs named '%s'",
6967a8a68f5SJulian Pullen 		    svc_name);
6977a8a68f5SJulian Pullen 		if (len < 0) {
6987a8a68f5SJulian Pullen 			logger(LOG_DEBUG, "DNS search for '%s' failed (%s)",
6997a8a68f5SJulian Pullen 			    svc_name, hstrerror(state->res_h_errno));
7007a8a68f5SJulian Pullen 			return (NULL);
7017a8a68f5SJulian Pullen 		}
7027a8a68f5SJulian Pullen 	} else if (dname != NULL) {
7037a8a68f5SJulian Pullen 		logger(LOG_DEBUG,
7047a8a68f5SJulian Pullen 		    "Querying DNS for SRV RRs named '%s' for '%s' ",
7057a8a68f5SJulian Pullen 		    svc_name, dname);
7067a8a68f5SJulian Pullen 
707e3f2c991SKeyur Desai 		len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV,
708e3f2c991SKeyur Desai 		    msg.buf, sizeof (msg.buf));
709e3f2c991SKeyur Desai 
7107a8a68f5SJulian Pullen 		if (len < 0) {
7117a8a68f5SJulian Pullen 			logger(LOG_DEBUG,
7127a8a68f5SJulian Pullen 			    "DNS query for '%s' for '%s' failed (%s)",
7137a8a68f5SJulian Pullen 			    svc_name, dname, hstrerror(state->res_h_errno));
7147a8a68f5SJulian Pullen 			return (NULL);
7157a8a68f5SJulian Pullen 		}
7167a8a68f5SJulian Pullen 	}
7177a8a68f5SJulian Pullen 
7187a8a68f5SJulian Pullen 	if (len > sizeof (msg.buf)) {
7197a8a68f5SJulian Pullen 		logger(LOG_ERR, "DNS query %ib message doesn't fit"
7207a8a68f5SJulian Pullen 		    " into %ib buffer",
7217a8a68f5SJulian Pullen 		    len, sizeof (msg.buf));
7227a8a68f5SJulian Pullen 		return (NULL);
7237a8a68f5SJulian Pullen 	}
7247a8a68f5SJulian Pullen 
7257a8a68f5SJulian Pullen 	/* 2. parse the reply, skip header and question sections */
7267a8a68f5SJulian Pullen 
7277a8a68f5SJulian Pullen 	ptr = msg.buf + sizeof (msg.hdr);
7287a8a68f5SJulian Pullen 	eom = msg.buf + len;
7297a8a68f5SJulian Pullen 	qdcount = ntohs(msg.hdr.qdcount);
7307a8a68f5SJulian Pullen 	ancount = ntohs(msg.hdr.ancount);
7317a8a68f5SJulian Pullen 
7327a8a68f5SJulian Pullen 	for (cnt = qdcount; cnt > 0; --cnt) {
7337a8a68f5SJulian Pullen 		if ((len = dn_skipname(ptr, eom)) < 0) {
7347a8a68f5SJulian Pullen 			logger(LOG_ERR, "DNS query invalid message format");
7357a8a68f5SJulian Pullen 			return (NULL);
7367a8a68f5SJulian Pullen 		}
7377a8a68f5SJulian Pullen 		ptr += len + QFIXEDSZ;
7387a8a68f5SJulian Pullen 	}
7397a8a68f5SJulian Pullen 
7407a8a68f5SJulian Pullen 	/* 3. walk through the answer section */
7417a8a68f5SJulian Pullen 
7427a8a68f5SJulian Pullen 	srv_res = calloc(ancount + 1, sizeof (idmap_ad_disc_ds_t));
743bbf6f00cSJordan Brown 	if (srv_res == NULL) {
744bbf6f00cSJordan Brown 		logger(LOG_ERR, "Out of memory");
745bbf6f00cSJordan Brown 		return (NULL);
746bbf6f00cSJordan Brown 	}
747bbf6f00cSJordan Brown 
7487a8a68f5SJulian Pullen 	*ttl = (uint32_t)-1;
7497a8a68f5SJulian Pullen 
7507a8a68f5SJulian Pullen 	for (srv = srv_res, cnt = ancount;
7517a8a68f5SJulian Pullen 	    cnt > 0; --cnt, srv++) {
7527a8a68f5SJulian Pullen 
7537a8a68f5SJulian Pullen 		len = dn_expand(msg.buf, eom, ptr, namebuf,
7547a8a68f5SJulian Pullen 		    sizeof (namebuf));
7557a8a68f5SJulian Pullen 		if (len < 0) {
7567a8a68f5SJulian Pullen 			logger(LOG_ERR, "DNS query invalid message format");
757bbf6f00cSJordan Brown 			goto err;
7587a8a68f5SJulian Pullen 		}
759bbf6f00cSJordan Brown 		if (rrname != NULL && *rrname == NULL) {
7607a8a68f5SJulian Pullen 			*rrname = strdup(namebuf);
761bbf6f00cSJordan Brown 			if (*rrname == NULL) {
762bbf6f00cSJordan Brown 				logger(LOG_ERR, "Out of memory");
763bbf6f00cSJordan Brown 				goto err;
764bbf6f00cSJordan Brown 			}
765bbf6f00cSJordan Brown 		}
7667a8a68f5SJulian Pullen 		ptr += len;
7677a8a68f5SJulian Pullen 		NS_GET16(type, ptr);
7687a8a68f5SJulian Pullen 		NS_GET16(class, ptr);
7697a8a68f5SJulian Pullen 		NS_GET32(rttl, ptr);
7707a8a68f5SJulian Pullen 		NS_GET16(size, ptr);
7717a8a68f5SJulian Pullen 		if ((end = ptr + size) > eom) {
7727a8a68f5SJulian Pullen 			logger(LOG_ERR, "DNS query invalid message format");
773bbf6f00cSJordan Brown 			goto err;
7747a8a68f5SJulian Pullen 		}
7757a8a68f5SJulian Pullen 
7767a8a68f5SJulian Pullen 		if (type != T_SRV) {
7777a8a68f5SJulian Pullen 			ptr = end;
7787a8a68f5SJulian Pullen 			continue;
7797a8a68f5SJulian Pullen 		}
7807a8a68f5SJulian Pullen 
7817a8a68f5SJulian Pullen 		NS_GET16(srv->priority, ptr);
7827a8a68f5SJulian Pullen 		NS_GET16(srv->weight, ptr);
7837a8a68f5SJulian Pullen 		NS_GET16(srv->port, ptr);
7847a8a68f5SJulian Pullen 		len = dn_expand(msg.buf, eom, ptr, srv->host,
7857a8a68f5SJulian Pullen 		    sizeof (srv->host));
7867a8a68f5SJulian Pullen 		if (len < 0) {
7877a8a68f5SJulian Pullen 			logger(LOG_ERR, "DNS query invalid SRV record");
788bbf6f00cSJordan Brown 			goto err;
7897a8a68f5SJulian Pullen 		}
7907a8a68f5SJulian Pullen 
7917a8a68f5SJulian Pullen 		if (rttl < *ttl)
7927a8a68f5SJulian Pullen 			*ttl = rttl;
7937a8a68f5SJulian Pullen 
7947a8a68f5SJulian Pullen 		logger(LOG_DEBUG, "Found %s %d IN SRV [%d][%d] %s:%d",
7957a8a68f5SJulian Pullen 		    namebuf, rttl, srv->priority, srv->weight, srv->host,
7967a8a68f5SJulian Pullen 		    srv->port);
7977a8a68f5SJulian Pullen 
7987a8a68f5SJulian Pullen 		/* 3. move ptr to the end of current record */
7997a8a68f5SJulian Pullen 
8007a8a68f5SJulian Pullen 		ptr = end;
8017a8a68f5SJulian Pullen 	}
8027a8a68f5SJulian Pullen 
8037a8a68f5SJulian Pullen 	if (ancount > 1)
8047a8a68f5SJulian Pullen 		qsort(srv_res, ancount, sizeof (*srv_res),
8057a8a68f5SJulian Pullen 		    (int (*)(const void *, const void *))srvcmp);
8067a8a68f5SJulian Pullen 
8077a8a68f5SJulian Pullen 	return (srv_res);
808bbf6f00cSJordan Brown 
809bbf6f00cSJordan Brown err:
810bbf6f00cSJordan Brown 	free(srv_res);
811bbf6f00cSJordan Brown 	if (rrname != NULL) {
812bbf6f00cSJordan Brown 		free(*rrname);
813bbf6f00cSJordan Brown 		*rrname = NULL;
814bbf6f00cSJordan Brown 	}
815bbf6f00cSJordan Brown 	return (NULL);
8167a8a68f5SJulian Pullen }
8177a8a68f5SJulian Pullen 
8187a8a68f5SJulian Pullen 
8197a8a68f5SJulian Pullen /*
8207a8a68f5SJulian Pullen  * A utility function to bind to a Directory server
8217a8a68f5SJulian Pullen  */
8227a8a68f5SJulian Pullen 
823*c5866007SKeyur Desai static
824*c5866007SKeyur Desai LDAP *
8257a8a68f5SJulian Pullen ldap_lookup_init(idmap_ad_disc_ds_t *ds)
8267a8a68f5SJulian Pullen {
8277a8a68f5SJulian Pullen 	int 	i;
8287a8a68f5SJulian Pullen 	int	rc, ldversion;
8297a8a68f5SJulian Pullen 	int	zero = 0;
8307a8a68f5SJulian Pullen 	int 	timeoutms = 5 * 1000;
8317a8a68f5SJulian Pullen 	char 	*saslmech = "GSSAPI";
8327a8a68f5SJulian Pullen 	uint32_t saslflags = LDAP_SASL_INTERACTIVE;
8337a8a68f5SJulian Pullen 	LDAP 	*ld = NULL;
8347a8a68f5SJulian Pullen 
8357a8a68f5SJulian Pullen 	for (i = 0; ds[i].host[0] != '\0'; i++) {
8367a8a68f5SJulian Pullen 		ld = ldap_init(ds[i].host, ds[i].port);
8377a8a68f5SJulian Pullen 		if (ld == NULL) {
8387a8a68f5SJulian Pullen 			logger(LOG_DEBUG, "Couldn't connect to "
8397a8a68f5SJulian Pullen 			    "AD DC %s:%d (%s)",
8407a8a68f5SJulian Pullen 			    ds[i].host, ds[i].port,
8417a8a68f5SJulian Pullen 			    strerror(errno));
8427a8a68f5SJulian Pullen 			continue;
8437a8a68f5SJulian Pullen 		}
8447a8a68f5SJulian Pullen 
8457a8a68f5SJulian Pullen 		ldversion = LDAP_VERSION3;
8467a8a68f5SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
8477a8a68f5SJulian Pullen 		    &ldversion);
8487a8a68f5SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_REFERRALS,
8497a8a68f5SJulian Pullen 		    LDAP_OPT_OFF);
8507a8a68f5SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
8517a8a68f5SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
8527a8a68f5SJulian Pullen 		/* setup TCP/IP connect timeout */
8537a8a68f5SJulian Pullen 		(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
8547a8a68f5SJulian Pullen 		    &timeoutms);
8557a8a68f5SJulian Pullen 		(void) ldap_set_option(ld, LDAP_OPT_RESTART,
8567a8a68f5SJulian Pullen 		    LDAP_OPT_ON);
8577a8a68f5SJulian Pullen 
858bd428526SJulian Pullen 		rc = adutils_set_thread_functions(ld);
859bd428526SJulian Pullen 		if (rc != LDAP_SUCCESS) {
860bd428526SJulian Pullen 			/* Error has already been logged */
861bd428526SJulian Pullen 			(void) ldap_unbind(ld);
862bd428526SJulian Pullen 			ld = NULL;
863bd428526SJulian Pullen 			continue;
864bd428526SJulian Pullen 		}
865bd428526SJulian Pullen 
8667a8a68f5SJulian Pullen 		rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */,
8677a8a68f5SJulian Pullen 		    saslmech, NULL, NULL, saslflags, &saslcallback,
8687a8a68f5SJulian Pullen 		    NULL /* defaults */);
8697a8a68f5SJulian Pullen 		if (rc == LDAP_SUCCESS)
8707a8a68f5SJulian Pullen 			break;
8717a8a68f5SJulian Pullen 
8727a8a68f5SJulian Pullen 		logger(LOG_INFO, "LDAP SASL bind to %s:%d failed (%s)",
8737a8a68f5SJulian Pullen 		    ds[i].host, ds[i].port, ldap_err2string(rc));
8747a8a68f5SJulian Pullen 		(void) ldap_unbind(ld);
8757a8a68f5SJulian Pullen 		ld = NULL;
8767a8a68f5SJulian Pullen 	}
8777a8a68f5SJulian Pullen 	return (ld);
8787a8a68f5SJulian Pullen }
8797a8a68f5SJulian Pullen 
8807a8a68f5SJulian Pullen 
8817a8a68f5SJulian Pullen 
8827a8a68f5SJulian Pullen /*
8837a8a68f5SJulian Pullen  * A utility function to get the value of some attribute of one of one
8847a8a68f5SJulian Pullen  * or more AD LDAP objects named by the dn_list; first found one wins.
8857a8a68f5SJulian Pullen  */
8867a8a68f5SJulian Pullen static char *
8877a8a68f5SJulian Pullen ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers,
8887a8a68f5SJulian Pullen 			char **dn_list, char *attr)
8897a8a68f5SJulian Pullen {
8907a8a68f5SJulian Pullen 	int 	i;
8917a8a68f5SJulian Pullen 	int	rc;
8927a8a68f5SJulian Pullen 	int	scope = LDAP_SCOPE_BASE;
8937a8a68f5SJulian Pullen 	char	*attrs[2];
8947a8a68f5SJulian Pullen 	LDAPMessage *results = NULL;
8957a8a68f5SJulian Pullen 	LDAPMessage *entry;
8967a8a68f5SJulian Pullen 	char	**values = NULL;
8977a8a68f5SJulian Pullen 	char	*val = NULL;
8987a8a68f5SJulian Pullen 
8997a8a68f5SJulian Pullen 	attrs[0] = attr;
9007a8a68f5SJulian Pullen 	attrs[1] = NULL;
9017a8a68f5SJulian Pullen 
9027a8a68f5SJulian Pullen 	if (*ld == NULL)
9037a8a68f5SJulian Pullen 		*ld = ldap_lookup_init(domainControllers);
9047a8a68f5SJulian Pullen 
9057a8a68f5SJulian Pullen 	if (*ld == NULL)
9067a8a68f5SJulian Pullen 		return (NULL);
9077a8a68f5SJulian Pullen 
9087a8a68f5SJulian Pullen 	for (i = 0; dn_list[i] != NULL; i++) {
9097a8a68f5SJulian Pullen 		rc = ldap_search_s(*ld, dn_list[i], scope,
9107a8a68f5SJulian Pullen 		    "(objectclass=*)", attrs, 0, &results);
9117a8a68f5SJulian Pullen 		if (rc == LDAP_SUCCESS) {
9127a8a68f5SJulian Pullen 			for (entry = ldap_first_entry(*ld, results);
9137a8a68f5SJulian Pullen 			    entry != NULL && values == NULL;
9147a8a68f5SJulian Pullen 			    entry = ldap_next_entry(*ld, entry)) {
9157a8a68f5SJulian Pullen 				values = ldap_get_values(
9167a8a68f5SJulian Pullen 				    *ld, entry, attr);
9177a8a68f5SJulian Pullen 			}
9187a8a68f5SJulian Pullen 
9197a8a68f5SJulian Pullen 			if (values != NULL) {
9207a8a68f5SJulian Pullen 				(void) ldap_msgfree(results);
9217a8a68f5SJulian Pullen 				val = strdup(values[0]);
9227a8a68f5SJulian Pullen 				ldap_value_free(values);
9237a8a68f5SJulian Pullen 				return (val);
9247a8a68f5SJulian Pullen 			}
9257a8a68f5SJulian Pullen 		}
9267a8a68f5SJulian Pullen 		if (results != NULL) {
9277a8a68f5SJulian Pullen 			(void) ldap_msgfree(results);
9287a8a68f5SJulian Pullen 			results = NULL;
9297a8a68f5SJulian Pullen 		}
9307a8a68f5SJulian Pullen 	}
9317a8a68f5SJulian Pullen 
9327a8a68f5SJulian Pullen 	return (NULL);
9337a8a68f5SJulian Pullen }
9347a8a68f5SJulian Pullen 
9357a8a68f5SJulian Pullen 
9367a8a68f5SJulian Pullen /*
9377a8a68f5SJulian Pullen  * Lookup the trusted domains in the global catalog.
9387a8a68f5SJulian Pullen  *
9397a8a68f5SJulian Pullen  * Returns:
9407a8a68f5SJulian Pullen  *	array of trusted domains which is terminated by
9417a8a68f5SJulian Pullen  *		an empty trusted domain.
9427a8a68f5SJulian Pullen  *	NULL an error occured
9437a8a68f5SJulian Pullen  */
9447a8a68f5SJulian Pullen ad_disc_trusteddomains_t *
9457a8a68f5SJulian Pullen ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog,
9467a8a68f5SJulian Pullen 			char *base_dn)
9477a8a68f5SJulian Pullen {
9487a8a68f5SJulian Pullen 	int		scope = LDAP_SCOPE_SUBTREE;
9497a8a68f5SJulian Pullen 	char		*attrs[3];
9507a8a68f5SJulian Pullen 	int		rc;
9517a8a68f5SJulian Pullen 	LDAPMessage	*results = NULL;
9527a8a68f5SJulian Pullen 	LDAPMessage	*entry;
9537a8a68f5SJulian Pullen 	char		*filter;
9547a8a68f5SJulian Pullen 	char		**partner = NULL;
9557a8a68f5SJulian Pullen 	char		**direction = NULL;
9567a8a68f5SJulian Pullen 	int		num = 0;
9577a8a68f5SJulian Pullen 	ad_disc_trusteddomains_t *trusted_domains = NULL;
9587a8a68f5SJulian Pullen 
9597a8a68f5SJulian Pullen 
9607a8a68f5SJulian Pullen 	if (*ld == NULL)
9617a8a68f5SJulian Pullen 		*ld = ldap_lookup_init(globalCatalog);
9627a8a68f5SJulian Pullen 
9637a8a68f5SJulian Pullen 	if (*ld == NULL)
9647a8a68f5SJulian Pullen 		return (NULL);
9657a8a68f5SJulian Pullen 
9667a8a68f5SJulian Pullen 	attrs[0] = "trustPartner";
9677a8a68f5SJulian Pullen 	attrs[1] = "trustDirection";
9687a8a68f5SJulian Pullen 	attrs[2] = NULL;
9697a8a68f5SJulian Pullen 
9707a8a68f5SJulian Pullen 	/* trustDirection values - inbound = 1 and bidirectional = 3 */
9717a8a68f5SJulian Pullen 	filter = "(&(objectclass=trustedDomain)"
9727a8a68f5SJulian Pullen 	    "(|(trustDirection=3)(trustDirection=1)))";
9737a8a68f5SJulian Pullen 
9747a8a68f5SJulian Pullen 	rc = ldap_search_s(*ld, base_dn, scope, filter, attrs, 0, &results);
9757a8a68f5SJulian Pullen 	if (rc == LDAP_SUCCESS) {
9767a8a68f5SJulian Pullen 		for (entry = ldap_first_entry(*ld, results);
9777a8a68f5SJulian Pullen 		    entry != NULL; entry = ldap_next_entry(*ld, entry)) {
9787a8a68f5SJulian Pullen 			partner = ldap_get_values(*ld, entry, "trustPartner");
9797a8a68f5SJulian Pullen 			direction = ldap_get_values(
9807a8a68f5SJulian Pullen 			    *ld, entry, "trustDirection");
9817a8a68f5SJulian Pullen 
9827a8a68f5SJulian Pullen 			if (partner != NULL && direction != NULL) {
9837a8a68f5SJulian Pullen 				num++;
984*c5866007SKeyur Desai 				void *tmp = realloc(trusted_domains,
9857a8a68f5SJulian Pullen 				    (num + 1) *
9867a8a68f5SJulian Pullen 				    sizeof (ad_disc_trusteddomains_t));
987*c5866007SKeyur Desai 				if (tmp == NULL) {
988*c5866007SKeyur Desai 					free(trusted_domains);
9897a8a68f5SJulian Pullen 					ldap_value_free(partner);
9907a8a68f5SJulian Pullen 					ldap_value_free(direction);
9917a8a68f5SJulian Pullen 					ldap_msgfree(results);
9927a8a68f5SJulian Pullen 					return (NULL);
9937a8a68f5SJulian Pullen 				}
994*c5866007SKeyur Desai 				trusted_domains = tmp;
9957a8a68f5SJulian Pullen 				/* Last element should be zero */
9967a8a68f5SJulian Pullen 				memset(&trusted_domains[num], 0,
9977a8a68f5SJulian Pullen 				    sizeof (ad_disc_trusteddomains_t));
9987a8a68f5SJulian Pullen 				strcpy(trusted_domains[num - 1].domain,
9997a8a68f5SJulian Pullen 				    partner[0]);
10007a8a68f5SJulian Pullen 				trusted_domains[num - 1].direction =
10017a8a68f5SJulian Pullen 				    atoi(direction[0]);
10027a8a68f5SJulian Pullen 			}
10037a8a68f5SJulian Pullen 			if (partner != NULL)
10047a8a68f5SJulian Pullen 				ldap_value_free(partner);
10057a8a68f5SJulian Pullen 			if (direction != NULL)
10067a8a68f5SJulian Pullen 				ldap_value_free(direction);
10077a8a68f5SJulian Pullen 		}
10087a8a68f5SJulian Pullen 	} else if (rc == LDAP_NO_RESULTS_RETURNED) {
10097a8a68f5SJulian Pullen 		/* This is not an error - return empty trusted domain */
10107a8a68f5SJulian Pullen 		trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t));
10117a8a68f5SJulian Pullen 	}
10127a8a68f5SJulian Pullen 	if (results != NULL)
10137a8a68f5SJulian Pullen 		ldap_msgfree(results);
10147a8a68f5SJulian Pullen 
10157a8a68f5SJulian Pullen 	return (trusted_domains);
10167a8a68f5SJulian Pullen }
10177a8a68f5SJulian Pullen 
10187a8a68f5SJulian Pullen 
10197a8a68f5SJulian Pullen /*
10207a8a68f5SJulian Pullen  * This functions finds all the domains in a forest.
10217a8a68f5SJulian Pullen  */
10227a8a68f5SJulian Pullen ad_disc_domainsinforest_t *
10237a8a68f5SJulian Pullen ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs)
10247a8a68f5SJulian Pullen {
1025928e1f97SJordan Brown 	static char	*attrs[] = {
1026928e1f97SJordan Brown 		"objectSid",
1027928e1f97SJordan Brown 		NULL,
1028928e1f97SJordan Brown 	};
10297a8a68f5SJulian Pullen 	int		rc;
10307a8a68f5SJulian Pullen 	LDAPMessage	*result = NULL;
10317a8a68f5SJulian Pullen 	LDAPMessage	*entry;
1032928e1f97SJordan Brown 	int		ndomains = 0;
1033928e1f97SJordan Brown 	int		nresults;
10347a8a68f5SJulian Pullen 	ad_disc_domainsinforest_t *domains = NULL;
10357a8a68f5SJulian Pullen 
10367a8a68f5SJulian Pullen 	if (*ld == NULL)
10377a8a68f5SJulian Pullen 		*ld = ldap_lookup_init(globalCatalogs);
10387a8a68f5SJulian Pullen 
10397a8a68f5SJulian Pullen 	if (*ld == NULL)
10407a8a68f5SJulian Pullen 		return (NULL);
10417a8a68f5SJulian Pullen 
1042928e1f97SJordan Brown 	logger(LOG_DEBUG, "Looking for domains in forest...");
10437a8a68f5SJulian Pullen 	/* Find domains */
1044928e1f97SJordan Brown 	rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE,
1045928e1f97SJordan Brown 	    "(objectClass=Domain)", attrs, 0, &result);
1046928e1f97SJordan Brown 	if (rc != LDAP_SUCCESS)
1047928e1f97SJordan Brown 		goto err;
1048928e1f97SJordan Brown 
1049928e1f97SJordan Brown 	nresults = ldap_count_entries(*ld, result);
1050928e1f97SJordan Brown 	domains = calloc(nresults + 1, sizeof (*domains));
1051928e1f97SJordan Brown 	if (domains == NULL)
1052928e1f97SJordan Brown 		goto err;
1053928e1f97SJordan Brown 
1054928e1f97SJordan Brown 	for (entry = ldap_first_entry(*ld, result);
1055928e1f97SJordan Brown 	    entry != NULL;
1056928e1f97SJordan Brown 	    entry = ldap_next_entry(*ld, entry)) {
1057928e1f97SJordan Brown 		struct berval	**sid_ber;
1058928e1f97SJordan Brown 		adutils_sid_t	sid;
1059928e1f97SJordan Brown 		char		*sid_str;
1060928e1f97SJordan Brown 		char 		*name;
1061bbf6f00cSJordan Brown 		char		*dn;
1062928e1f97SJordan Brown 
10637a8a68f5SJulian Pullen 		sid_ber = ldap_get_values_len(*ld, entry,
10647a8a68f5SJulian Pullen 		    "objectSid");
1065928e1f97SJordan Brown 		if (sid_ber == NULL)
1066928e1f97SJordan Brown 			continue;
10677a8a68f5SJulian Pullen 
1068928e1f97SJordan Brown 		rc = adutils_getsid(sid_ber[0], &sid);
10697a8a68f5SJulian Pullen 		ldap_value_free_len(sid_ber);
1070928e1f97SJordan Brown 		if (rc < 0)
1071928e1f97SJordan Brown 			goto err;
10727a8a68f5SJulian Pullen 
1073928e1f97SJordan Brown 		if ((sid_str = adutils_sid2txt(&sid)) == NULL)
1074928e1f97SJordan Brown 			goto err;
1075928e1f97SJordan Brown 
1076928e1f97SJordan Brown 		strcpy(domains[ndomains].sid, sid_str);
10777a8a68f5SJulian Pullen 		free(sid_str);
10787a8a68f5SJulian Pullen 
1079bbf6f00cSJordan Brown 		dn = ldap_get_dn(*ld, entry);
1080bbf6f00cSJordan Brown 		name = DN_to_DNS(dn);
1081bbf6f00cSJordan Brown 		free(dn);
1082928e1f97SJordan Brown 		if (name == NULL)
1083928e1f97SJordan Brown 			goto err;
1084928e1f97SJordan Brown 
1085928e1f97SJordan Brown 		strcpy(domains[ndomains].domain, name);
10867a8a68f5SJulian Pullen 		free(name);
1087928e1f97SJordan Brown 
1088928e1f97SJordan Brown 		logger(LOG_DEBUG, "    found %s", domains[ndomains].domain);
1089928e1f97SJordan Brown 
1090928e1f97SJordan Brown 		ndomains++;
10917a8a68f5SJulian Pullen 	}
1092928e1f97SJordan Brown 
1093928e1f97SJordan Brown 	if (ndomains == 0)
1094928e1f97SJordan Brown 		goto err;
1095928e1f97SJordan Brown 
1096928e1f97SJordan Brown 	if (ndomains < nresults) {
1097928e1f97SJordan Brown 		ad_disc_domainsinforest_t *tmp;
1098928e1f97SJordan Brown 		tmp = realloc(domains, (ndomains + 1) * sizeof (*domains));
1099928e1f97SJordan Brown 		if (tmp == NULL)
1100928e1f97SJordan Brown 			goto err;
1101928e1f97SJordan Brown 		domains = tmp;
11027a8a68f5SJulian Pullen 	}
1103928e1f97SJordan Brown 
11047a8a68f5SJulian Pullen 	if (result != NULL)
11057a8a68f5SJulian Pullen 		ldap_msgfree(result);
11067a8a68f5SJulian Pullen 
11077a8a68f5SJulian Pullen 	return (domains);
1108928e1f97SJordan Brown 
1109928e1f97SJordan Brown err:
1110928e1f97SJordan Brown 	free(domains);
1111928e1f97SJordan Brown 	if (result != NULL)
1112928e1f97SJordan Brown 		ldap_msgfree(result);
1113928e1f97SJordan Brown 	return (NULL);
11147a8a68f5SJulian Pullen }
11157a8a68f5SJulian Pullen 
11167a8a68f5SJulian Pullen 
11177a8a68f5SJulian Pullen ad_disc_t
11187a8a68f5SJulian Pullen ad_disc_init(void)
11197a8a68f5SJulian Pullen {
11207a8a68f5SJulian Pullen 	struct ad_disc *ctx;
11217a8a68f5SJulian Pullen 	ctx = calloc(1, sizeof (struct ad_disc));
11227a8a68f5SJulian Pullen 	if (ctx != NULL)
11237a8a68f5SJulian Pullen 		DO_RES_NINIT(ctx);
11247a8a68f5SJulian Pullen 
11257a8a68f5SJulian Pullen 	ctx->domain_name.type = AD_STRING;
11267a8a68f5SJulian Pullen 	ctx->domain_controller.type = AD_DIRECTORY;
11277a8a68f5SJulian Pullen 	ctx->site_name.type = AD_STRING;
11287a8a68f5SJulian Pullen 	ctx->forest_name.type = AD_STRING;
11297a8a68f5SJulian Pullen 	ctx->global_catalog.type = AD_DIRECTORY;
11307a8a68f5SJulian Pullen 	ctx->domains_in_forest.type = AD_DOMAINS_IN_FOREST;
11317a8a68f5SJulian Pullen 	ctx->trusted_domains.type = AD_TRUSTED_DOMAINS;
11327a8a68f5SJulian Pullen 	/* Site specific versions */
11337a8a68f5SJulian Pullen 	ctx->site_domain_controller.type = AD_DIRECTORY;
11347a8a68f5SJulian Pullen 	ctx->site_global_catalog.type = AD_DIRECTORY;
11357a8a68f5SJulian Pullen 	return (ctx);
11367a8a68f5SJulian Pullen }
11377a8a68f5SJulian Pullen 
11387a8a68f5SJulian Pullen 
11397a8a68f5SJulian Pullen void
11407a8a68f5SJulian Pullen ad_disc_fini(ad_disc_t ctx)
11417a8a68f5SJulian Pullen {
11427a8a68f5SJulian Pullen 	if (ctx == NULL)
11437a8a68f5SJulian Pullen 		return;
11447a8a68f5SJulian Pullen 
11457a8a68f5SJulian Pullen 	if (ctx->res_ninitted)
11467a8a68f5SJulian Pullen 		res_ndestroy(&ctx->res_state);
11477a8a68f5SJulian Pullen 
11487a8a68f5SJulian Pullen 	if (ctx->subnets != NULL)
11497a8a68f5SJulian Pullen 		free(ctx->subnets);
11507a8a68f5SJulian Pullen 
11517a8a68f5SJulian Pullen 	if (ctx->domain_name.value != NULL)
11527a8a68f5SJulian Pullen 		free(ctx->domain_name.value);
11537a8a68f5SJulian Pullen 
11547a8a68f5SJulian Pullen 	if (ctx->domain_controller.value != NULL)
11557a8a68f5SJulian Pullen 		free(ctx->domain_controller.value);
11567a8a68f5SJulian Pullen 
11577a8a68f5SJulian Pullen 	if (ctx->site_name.value != NULL)
11587a8a68f5SJulian Pullen 		free(ctx->site_name.value);
11597a8a68f5SJulian Pullen 
11607a8a68f5SJulian Pullen 	if (ctx->forest_name.value != NULL)
11617a8a68f5SJulian Pullen 		free(ctx->forest_name.value);
11627a8a68f5SJulian Pullen 
11637a8a68f5SJulian Pullen 	if (ctx->global_catalog.value != NULL)
11647a8a68f5SJulian Pullen 		free(ctx->global_catalog.value);
11657a8a68f5SJulian Pullen 
11667a8a68f5SJulian Pullen 	if (ctx->domains_in_forest.value != NULL)
11677a8a68f5SJulian Pullen 		free(ctx->domains_in_forest.value);
11687a8a68f5SJulian Pullen 
11697a8a68f5SJulian Pullen 	if (ctx->trusted_domains.value != NULL)
11707a8a68f5SJulian Pullen 		free(ctx->trusted_domains.value);
11717a8a68f5SJulian Pullen 
11727a8a68f5SJulian Pullen 	/* Site specific versions */
11737a8a68f5SJulian Pullen 	if (ctx->site_domain_controller.value != NULL)
11747a8a68f5SJulian Pullen 		free(ctx->site_domain_controller.value);
11757a8a68f5SJulian Pullen 
11767a8a68f5SJulian Pullen 	if (ctx->site_global_catalog.value != NULL)
11777a8a68f5SJulian Pullen 		free(ctx->site_global_catalog.value);
11787a8a68f5SJulian Pullen 
11797a8a68f5SJulian Pullen 	free(ctx);
11807a8a68f5SJulian Pullen }
11817a8a68f5SJulian Pullen 
11827a8a68f5SJulian Pullen void
11837a8a68f5SJulian Pullen ad_disc_refresh(ad_disc_t ctx)
11847a8a68f5SJulian Pullen {
11857a8a68f5SJulian Pullen 	if (ctx->res_ninitted)
11867a8a68f5SJulian Pullen 		res_ndestroy(&ctx->res_state);
11877a8a68f5SJulian Pullen 	(void) memset(&ctx->res_state, 0, sizeof (ctx->res_state));
11887a8a68f5SJulian Pullen 	ctx->res_ninitted = res_ninit(&ctx->res_state) != -1;
11897a8a68f5SJulian Pullen 
11907a8a68f5SJulian Pullen 	if (ctx->domain_name.state == AD_STATE_AUTO)
11917a8a68f5SJulian Pullen 		ctx->domain_name.state = AD_STATE_INVALID;
11927a8a68f5SJulian Pullen 
11937a8a68f5SJulian Pullen 	if (ctx->domain_controller.state == AD_STATE_AUTO)
11947a8a68f5SJulian Pullen 		ctx->domain_controller.state  = AD_STATE_INVALID;
11957a8a68f5SJulian Pullen 
11967a8a68f5SJulian Pullen 	if (ctx->site_name.state == AD_STATE_AUTO)
11977a8a68f5SJulian Pullen 		ctx->site_name.state = AD_STATE_INVALID;
11987a8a68f5SJulian Pullen 
11997a8a68f5SJulian Pullen 	if (ctx->forest_name.state == AD_STATE_AUTO)
12007a8a68f5SJulian Pullen 		ctx->forest_name.state = AD_STATE_INVALID;
12017a8a68f5SJulian Pullen 
12027a8a68f5SJulian Pullen 	if (ctx->global_catalog.state == AD_STATE_AUTO)
12037a8a68f5SJulian Pullen 		ctx->global_catalog.state = AD_STATE_INVALID;
12047a8a68f5SJulian Pullen 
12057a8a68f5SJulian Pullen 	if (ctx->domains_in_forest.state == AD_STATE_AUTO)
12067a8a68f5SJulian Pullen 		ctx->domains_in_forest.state  = AD_STATE_INVALID;
12077a8a68f5SJulian Pullen 
12087a8a68f5SJulian Pullen 	if (ctx->trusted_domains.state == AD_STATE_AUTO)
12097a8a68f5SJulian Pullen 		ctx->trusted_domains.state  = AD_STATE_INVALID;
12107a8a68f5SJulian Pullen 
12117a8a68f5SJulian Pullen 	if (ctx->site_domain_controller.state == AD_STATE_AUTO)
12127a8a68f5SJulian Pullen 		ctx->site_domain_controller.state  = AD_STATE_INVALID;
12137a8a68f5SJulian Pullen 
12147a8a68f5SJulian Pullen 	if (ctx->site_global_catalog.state == AD_STATE_AUTO)
12157a8a68f5SJulian Pullen 		ctx->site_global_catalog.state = AD_STATE_INVALID;
12167a8a68f5SJulian Pullen }
12177a8a68f5SJulian Pullen 
12187a8a68f5SJulian Pullen 
1219*c5866007SKeyur Desai /*
1220*c5866007SKeyur Desai  * Called when the discovery cycle is done.  Sets a master TTL
1221*c5866007SKeyur Desai  * that will avoid doing new time-based discoveries too soon after
1222*c5866007SKeyur Desai  * the last discovery cycle.  Most interesting when the discovery
1223*c5866007SKeyur Desai  * cycle failed, because then the TTLs on the individual items will
1224*c5866007SKeyur Desai  * not be updated and may go stale.
1225*c5866007SKeyur Desai  */
1226*c5866007SKeyur Desai void
1227*c5866007SKeyur Desai ad_disc_done(ad_disc_t ctx)
1228*c5866007SKeyur Desai {
1229*c5866007SKeyur Desai 	time_t now = time(NULL);
1230*c5866007SKeyur Desai 
1231*c5866007SKeyur Desai 	ctx->expires_not_before = now + MINIMUM_TTL;
1232*c5866007SKeyur Desai 	ctx->expires_not_after = now + MAXIMUM_TTL;
1233*c5866007SKeyur Desai }
1234*c5866007SKeyur Desai 
12357a8a68f5SJulian Pullen 
12367a8a68f5SJulian Pullen /* Discover joined Active Directory domainName */
12377a8a68f5SJulian Pullen static ad_item_t *
12387a8a68f5SJulian Pullen validate_DomainName(ad_disc_t ctx)
12397a8a68f5SJulian Pullen {
12407a8a68f5SJulian Pullen 	idmap_ad_disc_ds_t *domain_controller = NULL;
12417a8a68f5SJulian Pullen 	char *dname, *srvname;
12427a8a68f5SJulian Pullen 	uint32_t ttl = 0;
1243928e1f97SJordan Brown 	int len;
12447a8a68f5SJulian Pullen 
12457a8a68f5SJulian Pullen 	if (is_valid(&ctx->domain_name))
12467a8a68f5SJulian Pullen 		return (&ctx->domain_name);
12477a8a68f5SJulian Pullen 
12487a8a68f5SJulian Pullen 
12497a8a68f5SJulian Pullen 	/* Try to find our domain by searching for DCs for it */
12507a8a68f5SJulian Pullen 	DO_RES_NINIT(ctx);
12517a8a68f5SJulian Pullen 	domain_controller = srv_query(&ctx->res_state, LDAP_SRV_HEAD
12527a8a68f5SJulian Pullen 	    DC_SRV_TAIL, ctx->domain_name.value, &srvname, &ttl);
12537a8a68f5SJulian Pullen 
12547a8a68f5SJulian Pullen 	/*
12557a8a68f5SJulian Pullen 	 * If we can't find DCs by via res_nsearch() then there's no
12567a8a68f5SJulian Pullen 	 * point in trying anything else to discover the AD domain name.
12577a8a68f5SJulian Pullen 	 */
12587a8a68f5SJulian Pullen 	if (domain_controller == NULL)
12597a8a68f5SJulian Pullen 		return (NULL);
12607a8a68f5SJulian Pullen 
12617a8a68f5SJulian Pullen 	free(domain_controller);
12627a8a68f5SJulian Pullen 	/*
12637a8a68f5SJulian Pullen 	 * We have the FQDN of the SRV RR name, so now we extract the
12647a8a68f5SJulian Pullen 	 * domainname suffix from it.
12657a8a68f5SJulian Pullen 	 */
12667a8a68f5SJulian Pullen 	dname = strdup(srvname + strlen(LDAP_SRV_HEAD DC_SRV_TAIL) +
12677a8a68f5SJulian Pullen 	    1 /* for the dot between RR name and domainname */);
12687a8a68f5SJulian Pullen 
12697a8a68f5SJulian Pullen 	free(srvname);
12707a8a68f5SJulian Pullen 
12717a8a68f5SJulian Pullen 	if (dname == NULL) {
12727a8a68f5SJulian Pullen 		logger(LOG_ERR, "Out of memory");
12737a8a68f5SJulian Pullen 		return (NULL);
12747a8a68f5SJulian Pullen 	}
12757a8a68f5SJulian Pullen 
12767a8a68f5SJulian Pullen 	/* Eat any trailing dot */
1277928e1f97SJordan Brown 	len = strlen(dname);
1278928e1f97SJordan Brown 	if (len > 0 && dname[len - 1] == '.')
1279928e1f97SJordan Brown 		dname[len - 1] = '\0';
12807a8a68f5SJulian Pullen 
12817a8a68f5SJulian Pullen 	update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl);
12827a8a68f5SJulian Pullen 
12837a8a68f5SJulian Pullen 	return (&ctx->domain_name);
12847a8a68f5SJulian Pullen }
12857a8a68f5SJulian Pullen 
12867a8a68f5SJulian Pullen 
12877a8a68f5SJulian Pullen char *
12887a8a68f5SJulian Pullen ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered)
12897a8a68f5SJulian Pullen {
12907a8a68f5SJulian Pullen 	char *domain_name = NULL;
12917a8a68f5SJulian Pullen 	ad_item_t *domain_name_item;
12927a8a68f5SJulian Pullen 
12937a8a68f5SJulian Pullen 	domain_name_item = validate_DomainName(ctx);
12947a8a68f5SJulian Pullen 
12957a8a68f5SJulian Pullen 	if (domain_name_item) {
12967a8a68f5SJulian Pullen 		domain_name = strdup(domain_name_item->value);
12977a8a68f5SJulian Pullen 		if (auto_discovered != NULL)
12987a8a68f5SJulian Pullen 			*auto_discovered =
12997a8a68f5SJulian Pullen 			    (domain_name_item->state == AD_STATE_AUTO);
13007a8a68f5SJulian Pullen 	} else if (auto_discovered != NULL)
13017a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
13027a8a68f5SJulian Pullen 
13037a8a68f5SJulian Pullen 	return (domain_name);
13047a8a68f5SJulian Pullen }
13057a8a68f5SJulian Pullen 
13067a8a68f5SJulian Pullen 
13077a8a68f5SJulian Pullen /* Discover domain controllers */
13087a8a68f5SJulian Pullen static ad_item_t *
13097a8a68f5SJulian Pullen validate_DomainController(ad_disc_t ctx, enum ad_disc_req req)
13107a8a68f5SJulian Pullen {
13117a8a68f5SJulian Pullen 	uint32_t ttl = 0;
13127a8a68f5SJulian Pullen 	idmap_ad_disc_ds_t *domain_controller = NULL;
13137a8a68f5SJulian Pullen 	boolean_t validate_global = B_FALSE;
13147a8a68f5SJulian Pullen 	boolean_t validate_site = B_FALSE;
13157a8a68f5SJulian Pullen 	ad_item_t *domain_name_item;
13167a8a68f5SJulian Pullen 	ad_item_t *site_name_item = NULL;
13177a8a68f5SJulian Pullen 
13187a8a68f5SJulian Pullen 	/* If the values is fixed there will not be a site specific version */
13197a8a68f5SJulian Pullen 	if (is_fixed(&ctx->domain_controller))
13207a8a68f5SJulian Pullen 		return (&ctx->domain_controller);
13217a8a68f5SJulian Pullen 
13227a8a68f5SJulian Pullen 	domain_name_item = validate_DomainName(ctx);
13237a8a68f5SJulian Pullen 	if (domain_name_item == NULL)
13247a8a68f5SJulian Pullen 		return (NULL);
13257a8a68f5SJulian Pullen 
13267a8a68f5SJulian Pullen 	if (req == AD_DISC_GLOBAL)
13277a8a68f5SJulian Pullen 		validate_global = B_TRUE;
13287a8a68f5SJulian Pullen 	else {
13297a8a68f5SJulian Pullen 		site_name_item = validate_SiteName(ctx);
13307a8a68f5SJulian Pullen 		if (site_name_item != NULL)
13317a8a68f5SJulian Pullen 			validate_site = B_TRUE;
13327a8a68f5SJulian Pullen 		else if (req == AD_DISC_PREFER_SITE)
13337a8a68f5SJulian Pullen 			validate_global = B_TRUE;
13347a8a68f5SJulian Pullen 	}
13357a8a68f5SJulian Pullen 
13367a8a68f5SJulian Pullen 	if (validate_global) {
13377a8a68f5SJulian Pullen 		if (!is_valid(&ctx->domain_controller) ||
13387a8a68f5SJulian Pullen 		    is_changed(&ctx->domain_controller, PARAM1,
13397a8a68f5SJulian Pullen 		    domain_name_item)) {
13407a8a68f5SJulian Pullen 			/*
13417a8a68f5SJulian Pullen 			 * Lookup DNS SRV RR named
13427a8a68f5SJulian Pullen 			 * _ldap._tcp.dc._msdcs.<DomainName>
13437a8a68f5SJulian Pullen 			 */
13447a8a68f5SJulian Pullen 			DO_RES_NINIT(ctx);
13457a8a68f5SJulian Pullen 			domain_controller = srv_query(&ctx->res_state,
13467a8a68f5SJulian Pullen 			    LDAP_SRV_HEAD DC_SRV_TAIL,
13477a8a68f5SJulian Pullen 			    domain_name_item->value, NULL, &ttl);
13487a8a68f5SJulian Pullen 
13497a8a68f5SJulian Pullen 			if (domain_controller == NULL)
13507a8a68f5SJulian Pullen 				return (NULL);
13517a8a68f5SJulian Pullen 
13527a8a68f5SJulian Pullen 			update_item(&ctx->domain_controller, domain_controller,
13537a8a68f5SJulian Pullen 			    AD_STATE_AUTO, ttl);
13547a8a68f5SJulian Pullen 			update_version(&ctx->domain_controller, PARAM1,
13557a8a68f5SJulian Pullen 			    domain_name_item);
13567a8a68f5SJulian Pullen 		}
13577a8a68f5SJulian Pullen 		return (&ctx->domain_controller);
13587a8a68f5SJulian Pullen 	}
13597a8a68f5SJulian Pullen 
13607a8a68f5SJulian Pullen 	if (validate_site) {
13617a8a68f5SJulian Pullen 		if (!is_valid(&ctx->site_domain_controller) ||
13627a8a68f5SJulian Pullen 		    is_changed(&ctx->site_domain_controller, PARAM1,
13637a8a68f5SJulian Pullen 		    domain_name_item) ||
13647a8a68f5SJulian Pullen 		    is_changed(&ctx->site_domain_controller, PARAM2,
13657a8a68f5SJulian Pullen 		    site_name_item)) {
13667a8a68f5SJulian Pullen 			char rr_name[DNS_MAX_NAME];
13677a8a68f5SJulian Pullen 			/*
13687a8a68f5SJulian Pullen 			 * Lookup DNS SRV RR named
13697a8a68f5SJulian Pullen 			 * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName>
13707a8a68f5SJulian Pullen 			 */
13717a8a68f5SJulian Pullen 			(void) snprintf(rr_name, sizeof (rr_name),
13727a8a68f5SJulian Pullen 			    LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL,
13737a8a68f5SJulian Pullen 			    site_name_item->value);
13747a8a68f5SJulian Pullen 			DO_RES_NINIT(ctx);
13757a8a68f5SJulian Pullen 			domain_controller = srv_query(&ctx->res_state, rr_name,
13767a8a68f5SJulian Pullen 			    domain_name_item->value, NULL, &ttl);
13777a8a68f5SJulian Pullen 			if (domain_controller == NULL)
13787a8a68f5SJulian Pullen 				return (NULL);
13797a8a68f5SJulian Pullen 
13807a8a68f5SJulian Pullen 			update_item(&ctx->site_domain_controller,
13817a8a68f5SJulian Pullen 			    domain_controller, AD_STATE_AUTO, ttl);
13827a8a68f5SJulian Pullen 			update_version(&ctx->site_domain_controller, PARAM1,
13837a8a68f5SJulian Pullen 			    domain_name_item);
13847a8a68f5SJulian Pullen 			update_version(&ctx->site_domain_controller, PARAM2,
13857a8a68f5SJulian Pullen 			    site_name_item);
13867a8a68f5SJulian Pullen 		}
13877a8a68f5SJulian Pullen 		return (&ctx->site_domain_controller);
13887a8a68f5SJulian Pullen 	}
13897a8a68f5SJulian Pullen 	return (NULL);
13907a8a68f5SJulian Pullen }
13917a8a68f5SJulian Pullen 
13927a8a68f5SJulian Pullen idmap_ad_disc_ds_t *
13937a8a68f5SJulian Pullen ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req,
13947a8a68f5SJulian Pullen 			boolean_t *auto_discovered)
13957a8a68f5SJulian Pullen {
13967a8a68f5SJulian Pullen 	ad_item_t *domain_controller_item;
13977a8a68f5SJulian Pullen 	idmap_ad_disc_ds_t *domain_controller = NULL;
13987a8a68f5SJulian Pullen 
13997a8a68f5SJulian Pullen 	domain_controller_item = validate_DomainController(ctx, req);
14007a8a68f5SJulian Pullen 
14017a8a68f5SJulian Pullen 	if (domain_controller_item != NULL) {
14027a8a68f5SJulian Pullen 		domain_controller = ds_dup(domain_controller_item->value);
14037a8a68f5SJulian Pullen 		if (auto_discovered != NULL)
14047a8a68f5SJulian Pullen 			*auto_discovered =
14057a8a68f5SJulian Pullen 			    (domain_controller_item->state == AD_STATE_AUTO);
14067a8a68f5SJulian Pullen 	} else if (auto_discovered != NULL)
14077a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
14087a8a68f5SJulian Pullen 
14097a8a68f5SJulian Pullen 	return (domain_controller);
14107a8a68f5SJulian Pullen }
14117a8a68f5SJulian Pullen 
14127a8a68f5SJulian Pullen 
14137a8a68f5SJulian Pullen /* Discover site name (for multi-homed systems the first one found wins) */
14147a8a68f5SJulian Pullen static ad_item_t *
14157a8a68f5SJulian Pullen validate_SiteName(ad_disc_t ctx)
14167a8a68f5SJulian Pullen {
14177a8a68f5SJulian Pullen 	LDAP *ld = NULL;
14187a8a68f5SJulian Pullen 	ad_subnet_t *subnets = NULL;
14197a8a68f5SJulian Pullen 	char **dn_subnets = NULL;
14207a8a68f5SJulian Pullen 	char *dn_root[2];
14217a8a68f5SJulian Pullen 	char *config_naming_context = NULL;
14227a8a68f5SJulian Pullen 	char *site_object = NULL;
14237a8a68f5SJulian Pullen 	char *site_name = NULL;
14247a8a68f5SJulian Pullen 	char *forest_name;
14257a8a68f5SJulian Pullen 	int len;
14267a8a68f5SJulian Pullen 	int i;
14277a8a68f5SJulian Pullen 	boolean_t update_required = B_FALSE;
14287a8a68f5SJulian Pullen 	ad_item_t *domain_controller_item;
14297a8a68f5SJulian Pullen 
14307a8a68f5SJulian Pullen 	if (is_fixed(&ctx->site_name))
14317a8a68f5SJulian Pullen 		return (&ctx->site_name);
14327a8a68f5SJulian Pullen 
14337a8a68f5SJulian Pullen 	/* Can't rely on site-specific DCs */
14347a8a68f5SJulian Pullen 	domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
14357a8a68f5SJulian Pullen 	if (domain_controller_item == NULL)
14367a8a68f5SJulian Pullen 		return (NULL);
14377a8a68f5SJulian Pullen 
14387a8a68f5SJulian Pullen 	if (!is_valid(&ctx->site_name) ||
1439*c5866007SKeyur Desai 	    is_changed(&ctx->site_name, PARAM1, domain_controller_item) ||
14407a8a68f5SJulian Pullen 	    ctx->subnets == NULL || ctx->subnets_changed) {
14417a8a68f5SJulian Pullen 		subnets = find_subnets();
14427a8a68f5SJulian Pullen 		ctx->subnets_last_check = time(NULL);
14437a8a68f5SJulian Pullen 		update_required = B_TRUE;
14447a8a68f5SJulian Pullen 	} else if (ctx->subnets_last_check + 60 < time(NULL)) {
1445*c5866007SKeyur Desai 		/* NEEDSWORK magic constant 60 above */
14467a8a68f5SJulian Pullen 		subnets = find_subnets();
14477a8a68f5SJulian Pullen 		ctx->subnets_last_check = time(NULL);
14487a8a68f5SJulian Pullen 		if (cmpsubnets(ctx->subnets, subnets) != 0)
14497a8a68f5SJulian Pullen 			update_required = B_TRUE;
14507a8a68f5SJulian Pullen 	}
14517a8a68f5SJulian Pullen 
14527a8a68f5SJulian Pullen 	if (!update_required) {
14537a8a68f5SJulian Pullen 		free(subnets);
14547a8a68f5SJulian Pullen 		return (&ctx->site_name);
14557a8a68f5SJulian Pullen 	}
14567a8a68f5SJulian Pullen 
14577a8a68f5SJulian Pullen 	if (subnets == NULL)
14587a8a68f5SJulian Pullen 		return (NULL);
14597a8a68f5SJulian Pullen 
14607a8a68f5SJulian Pullen 	dn_root[0] = "";
14617a8a68f5SJulian Pullen 	dn_root[1] = NULL;
14627a8a68f5SJulian Pullen 
14637a8a68f5SJulian Pullen 	config_naming_context = ldap_lookup_entry_attr(
14647a8a68f5SJulian Pullen 	    &ld, ctx->domain_controller.value,
14657a8a68f5SJulian Pullen 	    dn_root, "configurationNamingContext");
14667a8a68f5SJulian Pullen 	if (config_naming_context == NULL)
14677a8a68f5SJulian Pullen 		goto out;
14687a8a68f5SJulian Pullen 	/*
14697a8a68f5SJulian Pullen 	 * configurationNamingContext also provides the Forest
14707a8a68f5SJulian Pullen 	 * Name.
14717a8a68f5SJulian Pullen 	 */
14727a8a68f5SJulian Pullen 	if (!is_fixed(&ctx->forest_name)) {
14737a8a68f5SJulian Pullen 		/*
14747a8a68f5SJulian Pullen 		 * The configurationNamingContext should be of
14757a8a68f5SJulian Pullen 		 * form:
14767a8a68f5SJulian Pullen 		 * CN=Configuration,<DNforestName>
14777a8a68f5SJulian Pullen 		 * Remove the first part and convert to DNS form
14787a8a68f5SJulian Pullen 		 * (replace ",DC=" with ".")
14797a8a68f5SJulian Pullen 		 */
14807a8a68f5SJulian Pullen 		char *str = "CN=Configuration,";
14817a8a68f5SJulian Pullen 		int len = strlen(str);
14827a8a68f5SJulian Pullen 		if (strncasecmp(config_naming_context, str, len) == 0) {
14837a8a68f5SJulian Pullen 			forest_name = DN_to_DNS(config_naming_context + len);
14847a8a68f5SJulian Pullen 			update_item(&ctx->forest_name, forest_name,
14857a8a68f5SJulian Pullen 			    AD_STATE_AUTO, 0);
1486*c5866007SKeyur Desai 			update_version(&ctx->forest_name, PARAM1,
1487*c5866007SKeyur Desai 			    domain_controller_item);
14887a8a68f5SJulian Pullen 		}
14897a8a68f5SJulian Pullen 	}
14907a8a68f5SJulian Pullen 
14917a8a68f5SJulian Pullen 	dn_subnets = subnets_to_DNs(subnets, config_naming_context);
14927a8a68f5SJulian Pullen 	if (dn_subnets == NULL)
14937a8a68f5SJulian Pullen 		goto out;
14947a8a68f5SJulian Pullen 
14957a8a68f5SJulian Pullen 	site_object = ldap_lookup_entry_attr(
14967a8a68f5SJulian Pullen 	    &ld, domain_controller_item->value,
14977a8a68f5SJulian Pullen 	    dn_subnets, "siteobject");
14987a8a68f5SJulian Pullen 	if (site_object != NULL) {
14997a8a68f5SJulian Pullen 		/*
15007a8a68f5SJulian Pullen 		 * The site object should be of the form
15017a8a68f5SJulian Pullen 		 * CN=<site>,CN=Sites,CN=Configuration,
15027a8a68f5SJulian Pullen 		 *		<DN Domain>
15037a8a68f5SJulian Pullen 		 */
15047a8a68f5SJulian Pullen 		if (strncasecmp(site_object, "CN=", 3) == 0) {
15057a8a68f5SJulian Pullen 			for (len = 0; site_object[len + 3] != ','; len++)
15067a8a68f5SJulian Pullen 					;
15077a8a68f5SJulian Pullen 			site_name = malloc(len + 1);
15087a8a68f5SJulian Pullen 			(void) strncpy(site_name, &site_object[3], len);
15097a8a68f5SJulian Pullen 			site_name[len] = '\0';
15107a8a68f5SJulian Pullen 			update_item(&ctx->site_name, site_name,
15117a8a68f5SJulian Pullen 			    AD_STATE_AUTO, 0);
1512*c5866007SKeyur Desai 			update_version(&ctx->site_name, PARAM1,
1513*c5866007SKeyur Desai 			    domain_controller_item);
15147a8a68f5SJulian Pullen 		}
15157a8a68f5SJulian Pullen 	}
15167a8a68f5SJulian Pullen 
15177a8a68f5SJulian Pullen 	if (ctx->subnets != NULL) {
15187a8a68f5SJulian Pullen 		free(ctx->subnets);
15197a8a68f5SJulian Pullen 		ctx->subnets = NULL;
15207a8a68f5SJulian Pullen 	}
15217a8a68f5SJulian Pullen 	ctx->subnets = subnets;
15227a8a68f5SJulian Pullen 	subnets = NULL;
15237a8a68f5SJulian Pullen 	ctx->subnets_changed = B_FALSE;
15247a8a68f5SJulian Pullen 
15257a8a68f5SJulian Pullen out:
15267a8a68f5SJulian Pullen 	if (ld != NULL)
15277a8a68f5SJulian Pullen 		(void) ldap_unbind(ld);
15287a8a68f5SJulian Pullen 
15297a8a68f5SJulian Pullen 	if (dn_subnets != NULL) {
15307a8a68f5SJulian Pullen 		for (i = 0; dn_subnets[i] != NULL; i++)
15317a8a68f5SJulian Pullen 			free(dn_subnets[i]);
15327a8a68f5SJulian Pullen 		free(dn_subnets);
15337a8a68f5SJulian Pullen 	}
15347a8a68f5SJulian Pullen 	if (config_naming_context != NULL)
15357a8a68f5SJulian Pullen 		free(config_naming_context);
15367a8a68f5SJulian Pullen 	if (site_object != NULL)
15377a8a68f5SJulian Pullen 		free(site_object);
15387a8a68f5SJulian Pullen 
15397a8a68f5SJulian Pullen 	free(subnets);
15407a8a68f5SJulian Pullen 	if (site_name == NULL)
15417a8a68f5SJulian Pullen 		return (NULL);
15427a8a68f5SJulian Pullen 	return (&ctx->site_name);
15437a8a68f5SJulian Pullen 
15447a8a68f5SJulian Pullen }
15457a8a68f5SJulian Pullen 
15467a8a68f5SJulian Pullen 
15477a8a68f5SJulian Pullen char *
15487a8a68f5SJulian Pullen ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered)
15497a8a68f5SJulian Pullen {
15507a8a68f5SJulian Pullen 	ad_item_t *site_name_item;
15517a8a68f5SJulian Pullen 	char	*site_name = NULL;
15527a8a68f5SJulian Pullen 
15537a8a68f5SJulian Pullen 	site_name_item = validate_SiteName(ctx);
15547a8a68f5SJulian Pullen 	if (site_name_item != NULL) {
15557a8a68f5SJulian Pullen 		site_name = strdup(site_name_item->value);
15567a8a68f5SJulian Pullen 		if (auto_discovered != NULL)
15577a8a68f5SJulian Pullen 			*auto_discovered =
15587a8a68f5SJulian Pullen 			    (site_name_item->state == AD_STATE_AUTO);
15597a8a68f5SJulian Pullen 	} else if (auto_discovered != NULL)
15607a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
15617a8a68f5SJulian Pullen 
15627a8a68f5SJulian Pullen 	return (site_name);
15637a8a68f5SJulian Pullen }
15647a8a68f5SJulian Pullen 
15657a8a68f5SJulian Pullen 
15667a8a68f5SJulian Pullen 
15677a8a68f5SJulian Pullen /* Discover forest name */
15687a8a68f5SJulian Pullen static ad_item_t *
15697a8a68f5SJulian Pullen validate_ForestName(ad_disc_t ctx)
15707a8a68f5SJulian Pullen {
15717a8a68f5SJulian Pullen 	LDAP	*ld = NULL;
15727a8a68f5SJulian Pullen 	char	*config_naming_context;
15737a8a68f5SJulian Pullen 	char	*forest_name = NULL;
15747a8a68f5SJulian Pullen 	char	*dn_list[2];
15757a8a68f5SJulian Pullen 	ad_item_t *domain_controller_item;
15767a8a68f5SJulian Pullen 
15777a8a68f5SJulian Pullen 	if (is_fixed(&ctx->forest_name))
15787a8a68f5SJulian Pullen 		return (&ctx->forest_name);
15797a8a68f5SJulian Pullen 	/*
15807a8a68f5SJulian Pullen 	 * We may not have a site name yet, so we won't rely on
15817a8a68f5SJulian Pullen 	 * site-specific DCs.  (But maybe we could replace
15827a8a68f5SJulian Pullen 	 * validate_ForestName() with validate_siteName()?)
15837a8a68f5SJulian Pullen 	 */
15847a8a68f5SJulian Pullen 	domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL);
15857a8a68f5SJulian Pullen 	if (domain_controller_item == NULL)
15867a8a68f5SJulian Pullen 		return (NULL);
15877a8a68f5SJulian Pullen 
15887a8a68f5SJulian Pullen 	if (!is_valid(&ctx->forest_name) ||
15897a8a68f5SJulian Pullen 	    is_changed(&ctx->forest_name, PARAM1, domain_controller_item)) {
15907a8a68f5SJulian Pullen 
15917a8a68f5SJulian Pullen 		dn_list[0] = "";
15927a8a68f5SJulian Pullen 		dn_list[1] = NULL;
15937a8a68f5SJulian Pullen 		config_naming_context = ldap_lookup_entry_attr(
15947a8a68f5SJulian Pullen 		    &ld, ctx->domain_controller.value,
15957a8a68f5SJulian Pullen 		    dn_list, "configurationNamingContext");
15967a8a68f5SJulian Pullen 		if (config_naming_context != NULL) {
15977a8a68f5SJulian Pullen 			/*
15987a8a68f5SJulian Pullen 			 * The configurationNamingContext should be of
15997a8a68f5SJulian Pullen 			 * form:
16007a8a68f5SJulian Pullen 			 * CN=Configuration,<DNforestName>
16017a8a68f5SJulian Pullen 			 * Remove the first part and convert to DNS form
16027a8a68f5SJulian Pullen 			 * (replace ",DC=" with ".")
16037a8a68f5SJulian Pullen 			 */
16047a8a68f5SJulian Pullen 			char *str = "CN=Configuration,";
16057a8a68f5SJulian Pullen 			int len = strlen(str);
16067a8a68f5SJulian Pullen 			if (strncasecmp(config_naming_context, str, len) == 0) {
16077a8a68f5SJulian Pullen 				forest_name = DN_to_DNS(
16087a8a68f5SJulian Pullen 				    config_naming_context + len);
16097a8a68f5SJulian Pullen 			}
16107a8a68f5SJulian Pullen 			free(config_naming_context);
16117a8a68f5SJulian Pullen 		}
16127a8a68f5SJulian Pullen 		if (ld != NULL)
16137a8a68f5SJulian Pullen 			(void) ldap_unbind(ld);
16147a8a68f5SJulian Pullen 
16157a8a68f5SJulian Pullen 		if (forest_name == NULL)
16167a8a68f5SJulian Pullen 			return (NULL);
16177a8a68f5SJulian Pullen 
16187a8a68f5SJulian Pullen 		update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0);
16197a8a68f5SJulian Pullen 		update_version(&ctx->forest_name, PARAM1,
16207a8a68f5SJulian Pullen 		    domain_controller_item);
16217a8a68f5SJulian Pullen 	}
16227a8a68f5SJulian Pullen 	return (&ctx->forest_name);
16237a8a68f5SJulian Pullen }
16247a8a68f5SJulian Pullen 
16257a8a68f5SJulian Pullen 
16267a8a68f5SJulian Pullen char *
16277a8a68f5SJulian Pullen ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered)
16287a8a68f5SJulian Pullen {
16297a8a68f5SJulian Pullen 	ad_item_t *forest_name_item;
16307a8a68f5SJulian Pullen 	char	*forest_name = NULL;
16317a8a68f5SJulian Pullen 
16327a8a68f5SJulian Pullen 	forest_name_item = validate_ForestName(ctx);
16337a8a68f5SJulian Pullen 
16347a8a68f5SJulian Pullen 	if (forest_name_item != NULL) {
16357a8a68f5SJulian Pullen 		forest_name = strdup(forest_name_item->value);
16367a8a68f5SJulian Pullen 		if (auto_discovered != NULL)
16377a8a68f5SJulian Pullen 			*auto_discovered =
16387a8a68f5SJulian Pullen 			    (forest_name_item->state == AD_STATE_AUTO);
16397a8a68f5SJulian Pullen 	} else if (auto_discovered != NULL)
16407a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
16417a8a68f5SJulian Pullen 
16427a8a68f5SJulian Pullen 	return (forest_name);
16437a8a68f5SJulian Pullen }
16447a8a68f5SJulian Pullen 
16457a8a68f5SJulian Pullen 
16467a8a68f5SJulian Pullen /* Discover global catalog servers */
16477a8a68f5SJulian Pullen static ad_item_t *
16487a8a68f5SJulian Pullen validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req)
16497a8a68f5SJulian Pullen {
16507a8a68f5SJulian Pullen 	idmap_ad_disc_ds_t *global_catalog = NULL;
16517a8a68f5SJulian Pullen 	uint32_t ttl = 0;
16527a8a68f5SJulian Pullen 	boolean_t validate_global = B_FALSE;
16537a8a68f5SJulian Pullen 	boolean_t validate_site = B_FALSE;
16547a8a68f5SJulian Pullen 	ad_item_t *forest_name_item;
16557a8a68f5SJulian Pullen 	ad_item_t *site_name_item;
16567a8a68f5SJulian Pullen 
16577a8a68f5SJulian Pullen 	/* If the values is fixed there will not be a site specific version */
16587a8a68f5SJulian Pullen 	if (is_fixed(&ctx->global_catalog))
16597a8a68f5SJulian Pullen 		return (&ctx->global_catalog);
16607a8a68f5SJulian Pullen 
16617a8a68f5SJulian Pullen 	forest_name_item = validate_ForestName(ctx);
16627a8a68f5SJulian Pullen 	if (forest_name_item == NULL)
16637a8a68f5SJulian Pullen 		return (NULL);
16647a8a68f5SJulian Pullen 
16657a8a68f5SJulian Pullen 	if (req == AD_DISC_GLOBAL)
16667a8a68f5SJulian Pullen 		validate_global = B_TRUE;
16677a8a68f5SJulian Pullen 	else {
16687a8a68f5SJulian Pullen 		site_name_item = validate_SiteName(ctx);
16697a8a68f5SJulian Pullen 		if (site_name_item != NULL)
16707a8a68f5SJulian Pullen 			validate_site = B_TRUE;
16717a8a68f5SJulian Pullen 		else if (req == AD_DISC_PREFER_SITE)
16727a8a68f5SJulian Pullen 			validate_global = B_TRUE;
16737a8a68f5SJulian Pullen 	}
16747a8a68f5SJulian Pullen 
16757a8a68f5SJulian Pullen 	if (validate_global) {
16767a8a68f5SJulian Pullen 		if (!is_valid(&ctx->global_catalog) ||
16777a8a68f5SJulian Pullen 		    is_changed(&ctx->global_catalog, PARAM1,
16787a8a68f5SJulian Pullen 		    forest_name_item)) {
16797a8a68f5SJulian Pullen 			/*
16807a8a68f5SJulian Pullen 			 * Lookup DNS SRV RR named
16817a8a68f5SJulian Pullen 			 * _ldap._tcp.gc._msdcs.<ForestName>
16827a8a68f5SJulian Pullen 			 */
16837a8a68f5SJulian Pullen 			DO_RES_NINIT(ctx);
16847a8a68f5SJulian Pullen 			global_catalog =
16857a8a68f5SJulian Pullen 			    srv_query(&ctx->res_state,
16867a8a68f5SJulian Pullen 			    LDAP_SRV_HEAD GC_SRV_TAIL,
16877a8a68f5SJulian Pullen 			    ctx->forest_name.value, NULL, &ttl);
16887a8a68f5SJulian Pullen 
16897a8a68f5SJulian Pullen 			if (global_catalog == NULL)
16907a8a68f5SJulian Pullen 				return (NULL);
16917a8a68f5SJulian Pullen 
16927a8a68f5SJulian Pullen 			update_item(&ctx->global_catalog, global_catalog,
16937a8a68f5SJulian Pullen 			    AD_STATE_AUTO, ttl);
16947a8a68f5SJulian Pullen 			update_version(&ctx->global_catalog, PARAM1,
16957a8a68f5SJulian Pullen 			    forest_name_item);
16967a8a68f5SJulian Pullen 		}
16977a8a68f5SJulian Pullen 		return (&ctx->global_catalog);
16987a8a68f5SJulian Pullen 	}
16997a8a68f5SJulian Pullen 
17007a8a68f5SJulian Pullen 	if (validate_site) {
17017a8a68f5SJulian Pullen 		if (!is_valid(&ctx->site_global_catalog) ||
17027a8a68f5SJulian Pullen 		    is_changed(&ctx->site_global_catalog, PARAM1,
17037a8a68f5SJulian Pullen 		    forest_name_item) ||
17047a8a68f5SJulian Pullen 		    is_changed(&ctx->site_global_catalog, PARAM2,
17057a8a68f5SJulian Pullen 		    site_name_item)) {
17067a8a68f5SJulian Pullen 			char 	rr_name[DNS_MAX_NAME];
17077a8a68f5SJulian Pullen 
17087a8a68f5SJulian Pullen 			/*
17097a8a68f5SJulian Pullen 			 * Lookup DNS SRV RR named:
17107a8a68f5SJulian Pullen 			 * _ldap._tcp.<siteName>._sites.gc.
17117a8a68f5SJulian Pullen 			 *	_msdcs.<ForestName>
17127a8a68f5SJulian Pullen 			 */
17137a8a68f5SJulian Pullen 			(void) snprintf(rr_name,
17147a8a68f5SJulian Pullen 			    sizeof (rr_name),
17157a8a68f5SJulian Pullen 			    LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL,
17167a8a68f5SJulian Pullen 			    ctx->site_name.value);
17177a8a68f5SJulian Pullen 			DO_RES_NINIT(ctx);
17187a8a68f5SJulian Pullen 			global_catalog = srv_query(&ctx->res_state, rr_name,
17197a8a68f5SJulian Pullen 			    ctx->forest_name.value, NULL, &ttl);
17207a8a68f5SJulian Pullen 
17217a8a68f5SJulian Pullen 			if (global_catalog == NULL)
17227a8a68f5SJulian Pullen 				return (NULL);
17237a8a68f5SJulian Pullen 			update_item(&ctx->site_global_catalog, global_catalog,
17247a8a68f5SJulian Pullen 			    AD_STATE_AUTO, ttl);
17257a8a68f5SJulian Pullen 			update_version(&ctx->site_global_catalog, PARAM1,
17267a8a68f5SJulian Pullen 			    forest_name_item);
17277a8a68f5SJulian Pullen 			update_version(&ctx->site_global_catalog, PARAM2,
17287a8a68f5SJulian Pullen 			    site_name_item);
17297a8a68f5SJulian Pullen 		}
17307a8a68f5SJulian Pullen 		return (&ctx->site_global_catalog);
17317a8a68f5SJulian Pullen 	}
17327a8a68f5SJulian Pullen 	return (NULL);
17337a8a68f5SJulian Pullen }
17347a8a68f5SJulian Pullen 
17357a8a68f5SJulian Pullen 
17367a8a68f5SJulian Pullen idmap_ad_disc_ds_t *
17377a8a68f5SJulian Pullen ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req,
17387a8a68f5SJulian Pullen 			boolean_t *auto_discovered)
17397a8a68f5SJulian Pullen {
17407a8a68f5SJulian Pullen 	idmap_ad_disc_ds_t *global_catalog = NULL;
17417a8a68f5SJulian Pullen 	ad_item_t *global_catalog_item;
17427a8a68f5SJulian Pullen 
17437a8a68f5SJulian Pullen 	global_catalog_item = validate_GlobalCatalog(ctx, req);
17447a8a68f5SJulian Pullen 
17457a8a68f5SJulian Pullen 	if (global_catalog_item != NULL) {
17467a8a68f5SJulian Pullen 		global_catalog = ds_dup(global_catalog_item->value);
17477a8a68f5SJulian Pullen 		if (auto_discovered != NULL)
17487a8a68f5SJulian Pullen 			*auto_discovered =
17497a8a68f5SJulian Pullen 			    (global_catalog_item->state == AD_STATE_AUTO);
17507a8a68f5SJulian Pullen 	} else if (auto_discovered != NULL)
17517a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
17527a8a68f5SJulian Pullen 
17537a8a68f5SJulian Pullen 	return (global_catalog);
17547a8a68f5SJulian Pullen }
17557a8a68f5SJulian Pullen 
17567a8a68f5SJulian Pullen 
17577a8a68f5SJulian Pullen static ad_item_t *
17587a8a68f5SJulian Pullen validate_TrustedDomains(ad_disc_t ctx)
17597a8a68f5SJulian Pullen {
17607a8a68f5SJulian Pullen 	LDAP *ld = NULL;
17617a8a68f5SJulian Pullen 	ad_item_t *global_catalog_item;
17627a8a68f5SJulian Pullen 	ad_item_t *forest_name_item;
17637a8a68f5SJulian Pullen 	ad_disc_trusteddomains_t *trusted_domains;
17647a8a68f5SJulian Pullen 	char *dn = NULL;
17657a8a68f5SJulian Pullen 	char *forest_name_dn;
17667a8a68f5SJulian Pullen 	int len;
17677a8a68f5SJulian Pullen 	int num_parts;
17687a8a68f5SJulian Pullen 
17697a8a68f5SJulian Pullen 	if (is_fixed(&ctx->trusted_domains))
17707a8a68f5SJulian Pullen 		return (&ctx->trusted_domains);
17717a8a68f5SJulian Pullen 
17727a8a68f5SJulian Pullen 	global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL);
17737a8a68f5SJulian Pullen 	if (global_catalog_item == NULL)
17747a8a68f5SJulian Pullen 		return (NULL);
17757a8a68f5SJulian Pullen 
17767a8a68f5SJulian Pullen 	forest_name_item = validate_ForestName(ctx);
17777a8a68f5SJulian Pullen 	if (forest_name_item == NULL)
17787a8a68f5SJulian Pullen 		return (NULL);
17797a8a68f5SJulian Pullen 
17807a8a68f5SJulian Pullen 	if (!is_valid(&ctx->trusted_domains) ||
17817a8a68f5SJulian Pullen 	    is_changed(&ctx->trusted_domains, PARAM1, global_catalog_item) ||
17827a8a68f5SJulian Pullen 	    is_changed(&ctx->trusted_domains, PARAM2, forest_name_item)) {
17837a8a68f5SJulian Pullen 
17847a8a68f5SJulian Pullen 		forest_name_dn = ldap_dns_to_dn(forest_name_item->value,
17857a8a68f5SJulian Pullen 		    &num_parts);
17867a8a68f5SJulian Pullen 		if (forest_name_dn == NULL)
17877a8a68f5SJulian Pullen 			return (NULL);
17887a8a68f5SJulian Pullen 
17897a8a68f5SJulian Pullen 		len = snprintf(NULL, 0, "CN=System,%s", forest_name_dn) + 1;
17907a8a68f5SJulian Pullen 		dn = malloc(len);
17917a8a68f5SJulian Pullen 		if (dn == NULL)  {
17927a8a68f5SJulian Pullen 			free(forest_name_dn);
17937a8a68f5SJulian Pullen 			return (NULL);
17947a8a68f5SJulian Pullen 		}
17957a8a68f5SJulian Pullen 		(void) snprintf(dn, len, "CN=System,%s", forest_name_dn);
17967a8a68f5SJulian Pullen 		free(forest_name_dn);
17977a8a68f5SJulian Pullen 
17987a8a68f5SJulian Pullen 		trusted_domains = ldap_lookup_trusted_domains(
17997a8a68f5SJulian Pullen 		    &ld, global_catalog_item->value, dn);
18007a8a68f5SJulian Pullen 
18017a8a68f5SJulian Pullen 		if (ld != NULL)
18027a8a68f5SJulian Pullen 			(void) ldap_unbind(ld);
18037a8a68f5SJulian Pullen 		free(dn);
18047a8a68f5SJulian Pullen 
18057a8a68f5SJulian Pullen 		if (trusted_domains == NULL)
18067a8a68f5SJulian Pullen 			return (NULL);
18077a8a68f5SJulian Pullen 
18087a8a68f5SJulian Pullen 		update_item(&ctx->trusted_domains, trusted_domains,
18097a8a68f5SJulian Pullen 		    AD_STATE_AUTO, 0);
18107a8a68f5SJulian Pullen 		update_version(&ctx->trusted_domains, PARAM1,
18117a8a68f5SJulian Pullen 		    global_catalog_item);
18127a8a68f5SJulian Pullen 		update_version(&ctx->trusted_domains, PARAM2,
18137a8a68f5SJulian Pullen 		    forest_name_item);
18147a8a68f5SJulian Pullen 	}
18157a8a68f5SJulian Pullen 
18167a8a68f5SJulian Pullen 	return (&ctx->trusted_domains);
18177a8a68f5SJulian Pullen }
18187a8a68f5SJulian Pullen 
18197a8a68f5SJulian Pullen 
18207a8a68f5SJulian Pullen ad_disc_trusteddomains_t *
18217a8a68f5SJulian Pullen ad_disc_get_TrustedDomains(ad_disc_t ctx, boolean_t *auto_discovered)
18227a8a68f5SJulian Pullen {
18237a8a68f5SJulian Pullen 	ad_disc_trusteddomains_t *trusted_domains = NULL;
18247a8a68f5SJulian Pullen 	ad_item_t *trusted_domains_item;
18257a8a68f5SJulian Pullen 
18267a8a68f5SJulian Pullen 	trusted_domains_item = validate_TrustedDomains(ctx);
18277a8a68f5SJulian Pullen 
18287a8a68f5SJulian Pullen 	if (trusted_domains_item != NULL) {
18297a8a68f5SJulian Pullen 		trusted_domains = td_dup(trusted_domains_item->value);
18307a8a68f5SJulian Pullen 		if (auto_discovered != NULL)
18317a8a68f5SJulian Pullen 			*auto_discovered =
18327a8a68f5SJulian Pullen 			    (trusted_domains_item->state == AD_STATE_AUTO);
18337a8a68f5SJulian Pullen 	} else if (auto_discovered != NULL)
18347a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
18357a8a68f5SJulian Pullen 
18367a8a68f5SJulian Pullen 	return (trusted_domains);
18377a8a68f5SJulian Pullen }
18387a8a68f5SJulian Pullen 
18397a8a68f5SJulian Pullen 
18407a8a68f5SJulian Pullen static ad_item_t *
18417a8a68f5SJulian Pullen validate_DomainsInForest(ad_disc_t ctx)
18427a8a68f5SJulian Pullen {
18437a8a68f5SJulian Pullen 	ad_item_t *global_catalog_item;
18447a8a68f5SJulian Pullen 	LDAP *ld = NULL;
18457a8a68f5SJulian Pullen 	ad_disc_domainsinforest_t *domains_in_forest;
18467a8a68f5SJulian Pullen 
18477a8a68f5SJulian Pullen 	if (is_fixed(&ctx->domains_in_forest))
18487a8a68f5SJulian Pullen 		return (&ctx->domains_in_forest);
18497a8a68f5SJulian Pullen 
18507a8a68f5SJulian Pullen 	global_catalog_item = validate_GlobalCatalog(ctx, AD_DISC_GLOBAL);
18517a8a68f5SJulian Pullen 	if (global_catalog_item == NULL)
18527a8a68f5SJulian Pullen 		return (NULL);
18537a8a68f5SJulian Pullen 
18547a8a68f5SJulian Pullen 	if (!is_valid(&ctx->domains_in_forest) ||
18557a8a68f5SJulian Pullen 	    is_changed(&ctx->domains_in_forest, PARAM1, global_catalog_item)) {
18567a8a68f5SJulian Pullen 
18577a8a68f5SJulian Pullen 		domains_in_forest = ldap_lookup_domains_in_forest(
18587a8a68f5SJulian Pullen 		    &ld, global_catalog_item->value);
18597a8a68f5SJulian Pullen 
18607a8a68f5SJulian Pullen 		if (ld != NULL)
18617a8a68f5SJulian Pullen 			(void) ldap_unbind(ld);
18627a8a68f5SJulian Pullen 
18637a8a68f5SJulian Pullen 		if (domains_in_forest == NULL)
18647a8a68f5SJulian Pullen 			return (NULL);
18657a8a68f5SJulian Pullen 
18667a8a68f5SJulian Pullen 		update_item(&ctx->domains_in_forest, domains_in_forest,
18677a8a68f5SJulian Pullen 		    AD_STATE_AUTO, 0);
18687a8a68f5SJulian Pullen 		update_version(&ctx->domains_in_forest, PARAM1,
18697a8a68f5SJulian Pullen 		    global_catalog_item);
18707a8a68f5SJulian Pullen 	}
18717a8a68f5SJulian Pullen 	return (&ctx->domains_in_forest);
18727a8a68f5SJulian Pullen }
18737a8a68f5SJulian Pullen 
18747a8a68f5SJulian Pullen 
18757a8a68f5SJulian Pullen ad_disc_domainsinforest_t *
18767a8a68f5SJulian Pullen ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered)
18777a8a68f5SJulian Pullen {
18787a8a68f5SJulian Pullen 	ad_disc_domainsinforest_t *domains_in_forest = NULL;
18797a8a68f5SJulian Pullen 	ad_item_t *domains_in_forest_item;
18807a8a68f5SJulian Pullen 
18817a8a68f5SJulian Pullen 	domains_in_forest_item = validate_DomainsInForest(ctx);
18827a8a68f5SJulian Pullen 
18837a8a68f5SJulian Pullen 	if (domains_in_forest_item != NULL) {
18847a8a68f5SJulian Pullen 		domains_in_forest = df_dup(domains_in_forest_item->value);
18857a8a68f5SJulian Pullen 		if (auto_discovered != NULL)
18867a8a68f5SJulian Pullen 			*auto_discovered =
18877a8a68f5SJulian Pullen 			    (domains_in_forest_item->state == AD_STATE_AUTO);
18887a8a68f5SJulian Pullen 	} else if (auto_discovered != NULL)
18897a8a68f5SJulian Pullen 		*auto_discovered = B_FALSE;
18907a8a68f5SJulian Pullen 
18917a8a68f5SJulian Pullen 	return (domains_in_forest);
18927a8a68f5SJulian Pullen }
18937a8a68f5SJulian Pullen 
18947a8a68f5SJulian Pullen 
18957a8a68f5SJulian Pullen 
18967a8a68f5SJulian Pullen 
18977a8a68f5SJulian Pullen int
18987a8a68f5SJulian Pullen ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName)
18997a8a68f5SJulian Pullen {
19007a8a68f5SJulian Pullen 	char *domain_name = NULL;
19017a8a68f5SJulian Pullen 	if (domainName != NULL) {
19027a8a68f5SJulian Pullen 		domain_name = strdup(domainName);
19037a8a68f5SJulian Pullen 		if (domain_name == NULL)
19047a8a68f5SJulian Pullen 			return (-1);
19057a8a68f5SJulian Pullen 		update_item(&ctx->domain_name, domain_name,
19067a8a68f5SJulian Pullen 		    AD_STATE_FIXED, 0);
19077a8a68f5SJulian Pullen 	} else if (ctx->domain_name.state == AD_STATE_FIXED)
19087a8a68f5SJulian Pullen 		ctx->domain_name.state = AD_STATE_INVALID;
19097a8a68f5SJulian Pullen 	return (0);
19107a8a68f5SJulian Pullen }
19117a8a68f5SJulian Pullen 
19127a8a68f5SJulian Pullen 
19137a8a68f5SJulian Pullen int
19147a8a68f5SJulian Pullen ad_disc_set_DomainController(ad_disc_t ctx,
19157a8a68f5SJulian Pullen 				const idmap_ad_disc_ds_t *domainController)
19167a8a68f5SJulian Pullen {
19177a8a68f5SJulian Pullen 	idmap_ad_disc_ds_t *domain_controller = NULL;
19187a8a68f5SJulian Pullen 	if (domainController != NULL) {
19197a8a68f5SJulian Pullen 		domain_controller = ds_dup(domainController);
19207a8a68f5SJulian Pullen 		if (domain_controller == NULL)
19217a8a68f5SJulian Pullen 			return (-1);
19227a8a68f5SJulian Pullen 		update_item(&ctx->domain_controller, domain_controller,
19237a8a68f5SJulian Pullen 		    AD_STATE_FIXED, 0);
19247a8a68f5SJulian Pullen 	} else if (ctx->domain_controller.state == AD_STATE_FIXED)
19257a8a68f5SJulian Pullen 		ctx->domain_controller.state = AD_STATE_INVALID;
19267a8a68f5SJulian Pullen 	return (0);
19277a8a68f5SJulian Pullen }
19287a8a68f5SJulian Pullen 
19297a8a68f5SJulian Pullen 
19307a8a68f5SJulian Pullen int
19317a8a68f5SJulian Pullen ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName)
19327a8a68f5SJulian Pullen {
19337a8a68f5SJulian Pullen 	char *site_name = NULL;
19347a8a68f5SJulian Pullen 	if (siteName != NULL) {
19357a8a68f5SJulian Pullen 		site_name = strdup(siteName);
19367a8a68f5SJulian Pullen 		if (site_name == NULL)
19377a8a68f5SJulian Pullen 			return (-1);
19387a8a68f5SJulian Pullen 		update_item(&ctx->site_name, site_name, AD_STATE_FIXED, 0);
19397a8a68f5SJulian Pullen 	} else if (ctx->site_name.state == AD_STATE_FIXED)
19407a8a68f5SJulian Pullen 		ctx->site_name.state = AD_STATE_INVALID;
19417a8a68f5SJulian Pullen 	return (0);
19427a8a68f5SJulian Pullen }
19437a8a68f5SJulian Pullen 
19447a8a68f5SJulian Pullen int
19457a8a68f5SJulian Pullen ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName)
19467a8a68f5SJulian Pullen {
19477a8a68f5SJulian Pullen 	char *forest_name = NULL;
19487a8a68f5SJulian Pullen 	if (forestName != NULL) {
19497a8a68f5SJulian Pullen 		forest_name = strdup(forestName);
19507a8a68f5SJulian Pullen 		if (forest_name == NULL)
19517a8a68f5SJulian Pullen 			return (-1);
19527a8a68f5SJulian Pullen 		update_item(&ctx->forest_name, forest_name,
19537a8a68f5SJulian Pullen 		    AD_STATE_FIXED, 0);
19547a8a68f5SJulian Pullen 	} else if (ctx->forest_name.state == AD_STATE_FIXED)
19557a8a68f5SJulian Pullen 		ctx->forest_name.state = AD_STATE_INVALID;
19567a8a68f5SJulian Pullen 	return (0);
19577a8a68f5SJulian Pullen }
19587a8a68f5SJulian Pullen 
19597a8a68f5SJulian Pullen int
19607a8a68f5SJulian Pullen ad_disc_set_GlobalCatalog(ad_disc_t ctx,
19617a8a68f5SJulian Pullen     const idmap_ad_disc_ds_t *globalCatalog)
19627a8a68f5SJulian Pullen {
19637a8a68f5SJulian Pullen 	idmap_ad_disc_ds_t *global_catalog = NULL;
19647a8a68f5SJulian Pullen 	if (globalCatalog != NULL) {
19657a8a68f5SJulian Pullen 		global_catalog = ds_dup(globalCatalog);
19667a8a68f5SJulian Pullen 		if (global_catalog == NULL)
19677a8a68f5SJulian Pullen 			return (-1);
19687a8a68f5SJulian Pullen 		update_item(&ctx->global_catalog, global_catalog,
19697a8a68f5SJulian Pullen 		    AD_STATE_FIXED, 0);
19707a8a68f5SJulian Pullen 	} else if (ctx->global_catalog.state == AD_STATE_FIXED)
19717a8a68f5SJulian Pullen 		ctx->global_catalog.state = AD_STATE_INVALID;
19727a8a68f5SJulian Pullen 	return (0);
19737a8a68f5SJulian Pullen }
19747a8a68f5SJulian Pullen 
19757a8a68f5SJulian Pullen 
19767a8a68f5SJulian Pullen int
19777a8a68f5SJulian Pullen ad_disc_unset(ad_disc_t ctx)
19787a8a68f5SJulian Pullen {
19797a8a68f5SJulian Pullen 	if (ctx->domain_name.state == AD_STATE_FIXED)
19807a8a68f5SJulian Pullen 		ctx->domain_name.state =  AD_STATE_INVALID;
19817a8a68f5SJulian Pullen 
19827a8a68f5SJulian Pullen 	if (ctx->domain_controller.state == AD_STATE_FIXED)
19837a8a68f5SJulian Pullen 		ctx->domain_controller.state =  AD_STATE_INVALID;
19847a8a68f5SJulian Pullen 
19857a8a68f5SJulian Pullen 	if (ctx->site_name.state == AD_STATE_FIXED)
19867a8a68f5SJulian Pullen 		ctx->site_name.state =  AD_STATE_INVALID;
19877a8a68f5SJulian Pullen 
19887a8a68f5SJulian Pullen 	if (ctx->forest_name.state == AD_STATE_FIXED)
19897a8a68f5SJulian Pullen 		ctx->forest_name.state =  AD_STATE_INVALID;
19907a8a68f5SJulian Pullen 
19917a8a68f5SJulian Pullen 	if (ctx->global_catalog.state == AD_STATE_FIXED)
19927a8a68f5SJulian Pullen 		ctx->global_catalog.state =  AD_STATE_INVALID;
19937a8a68f5SJulian Pullen 
19947a8a68f5SJulian Pullen 	return (0);
19957a8a68f5SJulian Pullen }
19967a8a68f5SJulian Pullen 
19977a8a68f5SJulian Pullen /*
19987a8a68f5SJulian Pullen  * ad_disc_get_TTL
19997a8a68f5SJulian Pullen  *
20007a8a68f5SJulian Pullen  * This routines the time to live for AD
20017a8a68f5SJulian Pullen  * auto discovered items.
20027a8a68f5SJulian Pullen  *
20037a8a68f5SJulian Pullen  *	Returns:
20047a8a68f5SJulian Pullen  *		-1 if there are no TTL items
20057a8a68f5SJulian Pullen  *		0  if there are expired items
20067a8a68f5SJulian Pullen  *		else the number of seconds
20077a8a68f5SJulian Pullen  *
20087a8a68f5SJulian Pullen  * The MIN_GT_ZERO(x, y) macro return the lesser of x and y, provided it
20097a8a68f5SJulian Pullen  * is positive -- min() greater than zero.
20107a8a68f5SJulian Pullen  */
20117a8a68f5SJulian Pullen #define	MIN_GT_ZERO(x, y) (((x) <= 0) ? (((y) <= 0) ? \
20127a8a68f5SJulian Pullen 		(-1) : (y)) : (((y) <= 0) ? (x) : (((x) > (y)) ? (y) : (x))))
20137a8a68f5SJulian Pullen int
20147a8a68f5SJulian Pullen ad_disc_get_TTL(ad_disc_t ctx)
20157a8a68f5SJulian Pullen {
2016*c5866007SKeyur Desai 	time_t expires;
20177a8a68f5SJulian Pullen 	int ttl;
20187a8a68f5SJulian Pullen 
2019*c5866007SKeyur Desai 	expires = MIN_GT_ZERO(ctx->domain_controller.expires,
2020*c5866007SKeyur Desai 	    ctx->global_catalog.expires);
2021*c5866007SKeyur Desai 	expires = MIN_GT_ZERO(expires, ctx->site_domain_controller.expires);
2022*c5866007SKeyur Desai 	expires = MIN_GT_ZERO(expires, ctx->site_global_catalog.expires);
20237a8a68f5SJulian Pullen 
2024*c5866007SKeyur Desai 	if (expires == -1) {
20257a8a68f5SJulian Pullen 		return (-1);
2026*c5866007SKeyur Desai 	}
2027*c5866007SKeyur Desai 
2028*c5866007SKeyur Desai 	if (ctx->expires_not_before != 0 &&
2029*c5866007SKeyur Desai 	    expires < ctx->expires_not_before) {
2030*c5866007SKeyur Desai 		expires = ctx->expires_not_before;
2031*c5866007SKeyur Desai 	}
2032*c5866007SKeyur Desai 
2033*c5866007SKeyur Desai 	if (ctx->expires_not_after != 0 &&
2034*c5866007SKeyur Desai 	    expires > ctx->expires_not_after) {
2035*c5866007SKeyur Desai 		expires = ctx->expires_not_after;
2036*c5866007SKeyur Desai 	}
2037*c5866007SKeyur Desai 
2038*c5866007SKeyur Desai 	ttl = expires - time(NULL);
2039*c5866007SKeyur Desai 
2040*c5866007SKeyur Desai 	if (ttl < 0) {
20417a8a68f5SJulian Pullen 		return (0);
2042*c5866007SKeyur Desai 	}
20437a8a68f5SJulian Pullen 	return (ttl);
20447a8a68f5SJulian Pullen }
20457a8a68f5SJulian Pullen 
20467a8a68f5SJulian Pullen boolean_t
20477a8a68f5SJulian Pullen ad_disc_SubnetChanged(ad_disc_t ctx)
20487a8a68f5SJulian Pullen {
20497a8a68f5SJulian Pullen 	ad_subnet_t *subnets;
20507a8a68f5SJulian Pullen 
20517a8a68f5SJulian Pullen 	if (ctx->subnets_changed || ctx->subnets == NULL)
20527a8a68f5SJulian Pullen 		return (B_TRUE);
20537a8a68f5SJulian Pullen 
20547a8a68f5SJulian Pullen 	if ((subnets = find_subnets()) != NULL) {
20557a8a68f5SJulian Pullen 		if (cmpsubnets(subnets, ctx->subnets) != 0)
20567a8a68f5SJulian Pullen 			ctx->subnets_changed = B_TRUE;
20577a8a68f5SJulian Pullen 		free(subnets);
20587a8a68f5SJulian Pullen 	}
20597a8a68f5SJulian Pullen 
20607a8a68f5SJulian Pullen 	return (ctx->subnets_changed);
20617a8a68f5SJulian Pullen }
2062