11fcced4cSJordan Brown /*
21fcced4cSJordan Brown  * CDDL HEADER START
31fcced4cSJordan Brown  *
41fcced4cSJordan Brown  * The contents of this file are subject to the terms of the
51fcced4cSJordan Brown  * Common Development and Distribution License (the "License").
61fcced4cSJordan Brown  * You may not use this file except in compliance with the License.
71fcced4cSJordan Brown  *
81fcced4cSJordan Brown  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91fcced4cSJordan Brown  * or http://www.opensolaris.org/os/licensing.
101fcced4cSJordan Brown  * See the License for the specific language governing permissions
111fcced4cSJordan Brown  * and limitations under the License.
121fcced4cSJordan Brown  *
131fcced4cSJordan Brown  * When distributing Covered Code, include this CDDL HEADER in each
141fcced4cSJordan Brown  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151fcced4cSJordan Brown  * If applicable, add the following below this CDDL HEADER, with the
161fcced4cSJordan Brown  * fields enclosed by brackets "[]" replaced with your own identifying
171fcced4cSJordan Brown  * information: Portions Copyright [yyyy] [name of copyright owner]
181fcced4cSJordan Brown  *
191fcced4cSJordan Brown  * CDDL HEADER END
201fcced4cSJordan Brown  */
211fcced4cSJordan Brown 
221fcced4cSJordan Brown /*
23cb174861Sjoyce mcintosh  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24*b3700b07SGordon Ross  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
251fcced4cSJordan Brown  */
261fcced4cSJordan Brown 
271fcced4cSJordan Brown /*
281fcced4cSJordan Brown  * Retrieve directory information for Active Directory users.
291fcced4cSJordan Brown  */
301fcced4cSJordan Brown 
311fcced4cSJordan Brown #include <ldap.h>
321fcced4cSJordan Brown #include <lber.h>
331fcced4cSJordan Brown #include <pwd.h>
341fcced4cSJordan Brown #include <malloc.h>
351fcced4cSJordan Brown #include <string.h>
361fcced4cSJordan Brown #include <stdlib.h>
371fcced4cSJordan Brown #include <netdb.h>
381fcced4cSJordan Brown #include <libadutils.h>
39cb174861Sjoyce mcintosh #include <libuutil.h>
401fcced4cSJordan Brown #include <note.h>
411fcced4cSJordan Brown #include <assert.h>
421fcced4cSJordan Brown #include "directory.h"
431fcced4cSJordan Brown #include "directory_private.h"
441fcced4cSJordan Brown #include "idmapd.h"
451fcced4cSJordan Brown #include <rpcsvc/idmap_prot.h>
461fcced4cSJordan Brown #include "directory_server_impl.h"
471fcced4cSJordan Brown 
481fcced4cSJordan Brown /*
491fcced4cSJordan Brown  * Information required by the function that handles the callback from LDAP
501fcced4cSJordan Brown  * when responses are received.
511fcced4cSJordan Brown  */
521fcced4cSJordan Brown struct cbinfo {
531fcced4cSJordan Brown 	const char * const *attrs;
541fcced4cSJordan Brown 	int nattrs;
551fcced4cSJordan Brown 	directory_entry_rpc *entry;
561fcced4cSJordan Brown 	const char *domain;
571fcced4cSJordan Brown };
581fcced4cSJordan Brown 
591fcced4cSJordan Brown static void directory_provider_ad_cb(LDAP *ld, LDAPMessage **ldapres, int rc,
601fcced4cSJordan Brown     int qid, void *argp);
611fcced4cSJordan Brown static void directory_provider_ad_cb1(LDAP *ld, LDAPMessage *msg,
621fcced4cSJordan Brown     struct cbinfo *cbinfo);
631fcced4cSJordan Brown static directory_error_t bv_list_dav(directory_values_rpc *lvals,
641fcced4cSJordan Brown     struct berval **bv);
651fcced4cSJordan Brown static directory_error_t directory_provider_ad_lookup(
661fcced4cSJordan Brown     directory_entry_rpc *pent, const char * const * attrs, int nattrs,
671fcced4cSJordan Brown     const char *domain, const char *filter);
681fcced4cSJordan Brown static directory_error_t get_domain(LDAP *ld, LDAPMessage *ldapres,
691fcced4cSJordan Brown     char **domain);
701fcced4cSJordan Brown static directory_error_t directory_provider_ad_utils_error(char *func, int rc);
711fcced4cSJordan Brown 
721fcced4cSJordan Brown #if	defined(DUMP_VALUES)
731fcced4cSJordan Brown static void dump_bv_list(const char *attr, struct berval **bv);
741fcced4cSJordan Brown #endif
751fcced4cSJordan Brown 
761fcced4cSJordan Brown #define	MAX_EXTRA_ATTRS	1	/* sAMAccountName */
771fcced4cSJordan Brown 
781fcced4cSJordan Brown /*
791fcced4cSJordan Brown  * Add an entry to a NULL-terminated list, if it's not already there.
801fcced4cSJordan Brown  * Assumes that the list has been allocated large enough for all additions,
811fcced4cSJordan Brown  * and prefilled with NULL.
821fcced4cSJordan Brown  */
831fcced4cSJordan Brown static
841fcced4cSJordan Brown void
maybe_add_to_list(const char ** list,const char * s)851fcced4cSJordan Brown maybe_add_to_list(const char **list, const char *s)
861fcced4cSJordan Brown {
871fcced4cSJordan Brown 	for (; *list != NULL; list++) {
88cb174861Sjoyce mcintosh 		if (uu_strcaseeq(*list, s))
891fcced4cSJordan Brown 			return;
901fcced4cSJordan Brown 	}
911fcced4cSJordan Brown 	*list = s;
921fcced4cSJordan Brown }
931fcced4cSJordan Brown 
941fcced4cSJordan Brown /*
951fcced4cSJordan Brown  * Copy a counted attribute list to a NULL-terminated one.
961fcced4cSJordan Brown  * In the process, examine the requested attributes and augment
971fcced4cSJordan Brown  * the list as required to support any synthesized attributes
981fcced4cSJordan Brown  * requested.
991fcced4cSJordan Brown  */
1001fcced4cSJordan Brown static
1011fcced4cSJordan Brown const char **
copy_and_augment_attr_list(char ** req_list,int req_list_len)1021fcced4cSJordan Brown copy_and_augment_attr_list(char **req_list, int req_list_len)
1031fcced4cSJordan Brown {
1041fcced4cSJordan Brown 	const char **new_list;
1051fcced4cSJordan Brown 	int i;
1061fcced4cSJordan Brown 
1071fcced4cSJordan Brown 	new_list =
1081fcced4cSJordan Brown 	    calloc(req_list_len + MAX_EXTRA_ATTRS + 1, sizeof (*new_list));
1091fcced4cSJordan Brown 	if (new_list == NULL)
1101fcced4cSJordan Brown 		return (NULL);
1111fcced4cSJordan Brown 
1121fcced4cSJordan Brown 	(void) memcpy(new_list, req_list, req_list_len * sizeof (char *));
1131fcced4cSJordan Brown 
1141fcced4cSJordan Brown 	for (i = 0; i < req_list_len; i++) {
1151fcced4cSJordan Brown 		const char *a = req_list[i];
1161fcced4cSJordan Brown 		/*
1171fcced4cSJordan Brown 		 * Note that you must update MAX_EXTRA_ATTRS above if you
1181fcced4cSJordan Brown 		 * add to this list.
1191fcced4cSJordan Brown 		 */
120cb174861Sjoyce mcintosh 		if (uu_strcaseeq(a, "x-sun-canonicalName")) {
1211fcced4cSJordan Brown 			maybe_add_to_list(new_list, "sAMAccountName");
1221fcced4cSJordan Brown 			continue;
1231fcced4cSJordan Brown 		}
1241fcced4cSJordan Brown 		/* None needed for x-sun-provider */
1251fcced4cSJordan Brown 	}
1261fcced4cSJordan Brown 
1271fcced4cSJordan Brown 	return (new_list);
1281fcced4cSJordan Brown }
1291fcced4cSJordan Brown 
1301fcced4cSJordan Brown /*
1311fcced4cSJordan Brown  * Retrieve information by name.
1321fcced4cSJordan Brown  * Called indirectly through the Directory_provider_static structure.
1331fcced4cSJordan Brown  */
1341fcced4cSJordan Brown static
1351fcced4cSJordan Brown directory_error_t
directory_provider_ad_get(directory_entry_rpc * del,idmap_utf8str_list * ids,char * types,idmap_utf8str_list * attrs)1361fcced4cSJordan Brown directory_provider_ad_get(
1371fcced4cSJordan Brown     directory_entry_rpc *del,
1381fcced4cSJordan Brown     idmap_utf8str_list *ids,
1391fcced4cSJordan Brown     char *types,
1401fcced4cSJordan Brown     idmap_utf8str_list *attrs)
1411fcced4cSJordan Brown {
1421fcced4cSJordan Brown 	int i;
1431fcced4cSJordan Brown 	const char **attrs2;
1441fcced4cSJordan Brown 	directory_error_t de = NULL;
1451fcced4cSJordan Brown 
1461fcced4cSJordan Brown 	/*
1471fcced4cSJordan Brown 	 * If we don't have any AD servers handy, we can't find anything.
148*b3700b07SGordon Ross 	 * XXX: this should be using our DC, not the GC.
1491fcced4cSJordan Brown 	 */
150e3f2c991SKeyur Desai 	if (_idmapdstate.num_gcs < 1) {
1511fcced4cSJordan Brown 		return (NULL);
1521fcced4cSJordan Brown 	}
1531fcced4cSJordan Brown 
1541fcced4cSJordan Brown 	RDLOCK_CONFIG()
1551fcced4cSJordan Brown 
1561fcced4cSJordan Brown 	/* 6835280 spurious lint error if the strlen is in the declaration */
1571fcced4cSJordan Brown 	int len = strlen(_idmapdstate.cfg->pgcfg.default_domain);
1581fcced4cSJordan Brown 	char default_domain[len + 1];
1591fcced4cSJordan Brown 	(void) strcpy(default_domain, _idmapdstate.cfg->pgcfg.default_domain);
1601fcced4cSJordan Brown 
1611fcced4cSJordan Brown 	UNLOCK_CONFIG();
1621fcced4cSJordan Brown 
1631fcced4cSJordan Brown 	/*
1641fcced4cSJordan Brown 	 * Turn our counted-array argument into a NULL-terminated array.
1651fcced4cSJordan Brown 	 * At the same time, add in any attributes that we need to support
1661fcced4cSJordan Brown 	 * any requested synthesized attributes.
1671fcced4cSJordan Brown 	 */
1681fcced4cSJordan Brown 	attrs2 = copy_and_augment_attr_list(attrs->idmap_utf8str_list_val,
1691fcced4cSJordan Brown 	    attrs->idmap_utf8str_list_len);
1701fcced4cSJordan Brown 	if (attrs2 == NULL)
1711fcced4cSJordan Brown 		goto nomem;
1721fcced4cSJordan Brown 
1731fcced4cSJordan Brown 	for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
1741fcced4cSJordan Brown 		char *vw[3];
1751fcced4cSJordan Brown 		int type;
1761fcced4cSJordan Brown 
1771fcced4cSJordan Brown 		/*
1781fcced4cSJordan Brown 		 * Extract the type for this particular ID.
1791fcced4cSJordan Brown 		 * Advance to the next type, if it's there, else keep
1801fcced4cSJordan Brown 		 * using this type until we run out of IDs.
1811fcced4cSJordan Brown 		 */
1821fcced4cSJordan Brown 		type = *types;
1831fcced4cSJordan Brown 		if (*(types+1) != '\0')
1841fcced4cSJordan Brown 			types++;
1851fcced4cSJordan Brown 
1861fcced4cSJordan Brown 		/*
1871fcced4cSJordan Brown 		 * If this entry has already been handled, one way or another,
1881fcced4cSJordan Brown 		 * skip it.
1891fcced4cSJordan Brown 		 */
1901fcced4cSJordan Brown 		if (del[i].status != DIRECTORY_NOT_FOUND)
1911fcced4cSJordan Brown 			continue;
1921fcced4cSJordan Brown 
1931fcced4cSJordan Brown 		char *id = ids->idmap_utf8str_list_val[i];
1941fcced4cSJordan Brown 
1951fcced4cSJordan Brown 		/*
1961fcced4cSJordan Brown 		 * Allow for expanding every character to \xx, plus some
1971fcced4cSJordan Brown 		 * space for the query syntax.
1981fcced4cSJordan Brown 		 */
1991fcced4cSJordan Brown 		int id_len = strlen(id);
2001fcced4cSJordan Brown 		char filter[1000 + id_len*3];
2011fcced4cSJordan Brown 
2021fcced4cSJordan Brown 		if (type == DIRECTORY_ID_SID[0]) {
2031fcced4cSJordan Brown 			/*
2041fcced4cSJordan Brown 			 * Mildly surprisingly, AD appears to allow searching
2051fcced4cSJordan Brown 			 * based on text SIDs.  Must be a special case on the
2061fcced4cSJordan Brown 			 * server end.
2071fcced4cSJordan Brown 			 */
2081fcced4cSJordan Brown 			ldap_build_filter(filter, sizeof (filter),
2091fcced4cSJordan Brown 			    "(objectSid=%v)", NULL, NULL, NULL, id, NULL);
2101fcced4cSJordan Brown 
2111fcced4cSJordan Brown 			de = directory_provider_ad_lookup(&del[i], attrs2,
2121fcced4cSJordan Brown 			    attrs->idmap_utf8str_list_len, NULL, filter);
2131fcced4cSJordan Brown 			if (de != NULL) {
2141fcced4cSJordan Brown 				directory_entry_set_error(&del[i], de);
2151fcced4cSJordan Brown 				de = NULL;
2161fcced4cSJordan Brown 			}
2171fcced4cSJordan Brown 		} else {
2181fcced4cSJordan Brown 			int id_len = strlen(id);
2191fcced4cSJordan Brown 			char name[id_len + 1];
2201fcced4cSJordan Brown 			char domain[id_len + 1];
2211fcced4cSJordan Brown 
2221fcced4cSJordan Brown 			split_name(name, domain, id);
2231fcced4cSJordan Brown 
2241fcced4cSJordan Brown 			vw[0] = name;
2251fcced4cSJordan Brown 
226cb174861Sjoyce mcintosh 			if (uu_streq(domain, "")) {
2271fcced4cSJordan Brown 				vw[1] = default_domain;
2281fcced4cSJordan Brown 			} else {
2291fcced4cSJordan Brown 				vw[1] = domain;
2301fcced4cSJordan Brown 			}
2311fcced4cSJordan Brown 
2321fcced4cSJordan Brown 			if (type == DIRECTORY_ID_USER[0])
2331fcced4cSJordan Brown 				vw[2] = "user";
2341fcced4cSJordan Brown 			else if (type == DIRECTORY_ID_GROUP[0])
2351fcced4cSJordan Brown 				vw[2] = "group";
2361fcced4cSJordan Brown 			else
2371fcced4cSJordan Brown 				vw[2] = "*";
2381fcced4cSJordan Brown 
2391fcced4cSJordan Brown 			/*
2401fcced4cSJordan Brown 			 * Try samAccountName.
2411fcced4cSJordan Brown 			 * Note that here we rely on checking the returned
2421fcced4cSJordan Brown 			 * distinguishedName to make sure that we found an
2431fcced4cSJordan Brown 			 * entry from the right domain, because there's no
2441fcced4cSJordan Brown 			 * attribute we can straightforwardly filter for to
2451fcced4cSJordan Brown 			 * match domain.
2461fcced4cSJordan Brown 			 *
2471fcced4cSJordan Brown 			 * Eventually we should perhaps also try
2481fcced4cSJordan Brown 			 * userPrincipalName.
2491fcced4cSJordan Brown 			 */
2501fcced4cSJordan Brown 			ldap_build_filter(filter, sizeof (filter),
2511fcced4cSJordan Brown 			    "(&(samAccountName=%v1)(objectClass=%v3))",
2521fcced4cSJordan Brown 			    NULL, NULL, NULL, NULL, vw);
2531fcced4cSJordan Brown 
2541fcced4cSJordan Brown 			de = directory_provider_ad_lookup(&del[i], attrs2,
2551fcced4cSJordan Brown 			    attrs->idmap_utf8str_list_len, vw[1], filter);
2561fcced4cSJordan Brown 			if (de != NULL) {
2571fcced4cSJordan Brown 				directory_entry_set_error(&del[i], de);
2581fcced4cSJordan Brown 				de = NULL;
2591fcced4cSJordan Brown 			}
2601fcced4cSJordan Brown 		}
2611fcced4cSJordan Brown 	}
2621fcced4cSJordan Brown 
2631fcced4cSJordan Brown 	de = NULL;
2641fcced4cSJordan Brown 
2651fcced4cSJordan Brown 	goto out;
2661fcced4cSJordan Brown 
2671fcced4cSJordan Brown nomem:
2681fcced4cSJordan Brown 	de = directory_error("ENOMEM.AD",
2691fcced4cSJordan Brown 	    "Out of memory during AD lookup", NULL);
2701fcced4cSJordan Brown out:
2711fcced4cSJordan Brown 	free(attrs2);
2721fcced4cSJordan Brown 	return (de);
2731fcced4cSJordan Brown }
2741fcced4cSJordan Brown 
2751fcced4cSJordan Brown /*
2761fcced4cSJordan Brown  * Note that attrs is NULL terminated, and that nattrs is the number
2771fcced4cSJordan Brown  * of attributes requested by the user... which might be fewer than are
2781fcced4cSJordan Brown  * in attrs because of attributes that we need for our own processing.
2791fcced4cSJordan Brown  */
2801fcced4cSJordan Brown static
2811fcced4cSJordan Brown directory_error_t
directory_provider_ad_lookup(directory_entry_rpc * pent,const char * const * attrs,int nattrs,const char * domain,const char * filter)2821fcced4cSJordan Brown directory_provider_ad_lookup(
2831fcced4cSJordan Brown     directory_entry_rpc *pent,
2841fcced4cSJordan Brown     const char * const * attrs,
2851fcced4cSJordan Brown     int nattrs,
2861fcced4cSJordan Brown     const char *domain,
2871fcced4cSJordan Brown     const char *filter)
2881fcced4cSJordan Brown {
2891fcced4cSJordan Brown 	adutils_ad_t *ad;
2901fcced4cSJordan Brown 	adutils_rc batchrc;
2911fcced4cSJordan Brown 	struct cbinfo cbinfo;
2921fcced4cSJordan Brown 	adutils_query_state_t *qs;
2931fcced4cSJordan Brown 	int rc;
2941fcced4cSJordan Brown 
2951fcced4cSJordan Brown 	/*
2961fcced4cSJordan Brown 	 * NEEDSWORK:  Should eventually handle other forests.
2971fcced4cSJordan Brown 	 * NEEDSWORK:  Should eventually handle non-GC attributes.
2981fcced4cSJordan Brown 	 */
299e3f2c991SKeyur Desai 	ad = _idmapdstate.gcs[0];
3001fcced4cSJordan Brown 
3011fcced4cSJordan Brown 	/* Stash away information for the callback function. */
3021fcced4cSJordan Brown 	cbinfo.attrs = attrs;
3031fcced4cSJordan Brown 	cbinfo.nattrs = nattrs;
3041fcced4cSJordan Brown 	cbinfo.entry = pent;
3051fcced4cSJordan Brown 	cbinfo.domain = domain;
3061fcced4cSJordan Brown 
3071fcced4cSJordan Brown 	rc = adutils_lookup_batch_start(ad, 1, directory_provider_ad_cb,
3081fcced4cSJordan Brown 	    &cbinfo, &qs);
3091fcced4cSJordan Brown 	if (rc != ADUTILS_SUCCESS) {
3101fcced4cSJordan Brown 		return (directory_provider_ad_utils_error(
3111fcced4cSJordan Brown 		    "adutils_lookup_batch_start", rc));
3121fcced4cSJordan Brown 	}
3131fcced4cSJordan Brown 
3141fcced4cSJordan Brown 	rc = adutils_lookup_batch_add(qs, filter, attrs, domain,
3151fcced4cSJordan Brown 	    NULL, &batchrc);
3161fcced4cSJordan Brown 	if (rc != ADUTILS_SUCCESS) {
3171fcced4cSJordan Brown 		adutils_lookup_batch_release(&qs);
3181fcced4cSJordan Brown 		return (directory_provider_ad_utils_error(
3191fcced4cSJordan Brown 		    "adutils_lookup_batch_add", rc));
3201fcced4cSJordan Brown 	}
3211fcced4cSJordan Brown 
3221fcced4cSJordan Brown 	rc = adutils_lookup_batch_end(&qs);
3231fcced4cSJordan Brown 	if (rc != ADUTILS_SUCCESS) {
3241fcced4cSJordan Brown 		return (directory_provider_ad_utils_error(
3251fcced4cSJordan Brown 		    "adutils_lookup_batch_end", rc));
3261fcced4cSJordan Brown 	}
3271fcced4cSJordan Brown 
3281fcced4cSJordan Brown 	if (batchrc != ADUTILS_SUCCESS) {
3291fcced4cSJordan Brown 		/*
3301fcced4cSJordan Brown 		 * NEEDSWORK:  We're consistently getting -9997 here.
3311fcced4cSJordan Brown 		 * What does it mean?
3321fcced4cSJordan Brown 		 */
3331fcced4cSJordan Brown 		return (NULL);
3341fcced4cSJordan Brown 	}
3351fcced4cSJordan Brown 
3361fcced4cSJordan Brown 	return (NULL);
3371fcced4cSJordan Brown }
3381fcced4cSJordan Brown 
3391fcced4cSJordan Brown /*
3401fcced4cSJordan Brown  * Callback from the LDAP functions when they get responses.
3411fcced4cSJordan Brown  * We don't really need (nor want) asynchronous handling, but it's
3421fcced4cSJordan Brown  * what libadutils gives us.
3431fcced4cSJordan Brown  */
3441fcced4cSJordan Brown static
3451fcced4cSJordan Brown void
directory_provider_ad_cb(LDAP * ld,LDAPMessage ** ldapres,int rc,int qid,void * argp)3461fcced4cSJordan Brown directory_provider_ad_cb(
3471fcced4cSJordan Brown     LDAP *ld,
3481fcced4cSJordan Brown     LDAPMessage **ldapres,
3491fcced4cSJordan Brown     int rc,
3501fcced4cSJordan Brown     int qid,
3511fcced4cSJordan Brown     void *argp)
3521fcced4cSJordan Brown {
3531fcced4cSJordan Brown 	NOTE(ARGUNUSED(rc, qid))
3541fcced4cSJordan Brown 	struct cbinfo *cbinfo = (struct cbinfo *)argp;
3551fcced4cSJordan Brown 	LDAPMessage *msg = *ldapres;
3561fcced4cSJordan Brown 
3571fcced4cSJordan Brown 	for (msg = ldap_first_entry(ld, msg);
3581fcced4cSJordan Brown 	    msg != NULL;
3591fcced4cSJordan Brown 	    msg = ldap_next_entry(ld, msg)) {
3601fcced4cSJordan Brown 		directory_provider_ad_cb1(ld, msg, cbinfo);
3611fcced4cSJordan Brown 	}
3621fcced4cSJordan Brown }
3631fcced4cSJordan Brown 
3641fcced4cSJordan Brown /*
3651fcced4cSJordan Brown  * Process a single entry returned by an LDAP callback.
3661fcced4cSJordan Brown  * Note that this performs a function roughly equivalent to the
3671fcced4cSJordan Brown  * directory*Populate() functions in the other providers.
3681fcced4cSJordan Brown  * Given an LDAP response, populate the directory entry for return to
3691fcced4cSJordan Brown  * the caller.  This one differs primarily in that we're working directly
3701fcced4cSJordan Brown  * with LDAP, so we don't have to do any attribute translation.
3711fcced4cSJordan Brown  */
3721fcced4cSJordan Brown static
3731fcced4cSJordan Brown void
directory_provider_ad_cb1(LDAP * ld,LDAPMessage * msg,struct cbinfo * cbinfo)3741fcced4cSJordan Brown directory_provider_ad_cb1(
3751fcced4cSJordan Brown     LDAP *ld,
3761fcced4cSJordan Brown     LDAPMessage *msg,
3771fcced4cSJordan Brown     struct cbinfo *cbinfo)
3781fcced4cSJordan Brown {
3791fcced4cSJordan Brown 	int nattrs = cbinfo->nattrs;
3801fcced4cSJordan Brown 	const char * const *attrs = cbinfo->attrs;
3811fcced4cSJordan Brown 	directory_entry_rpc *pent = cbinfo->entry;
3821fcced4cSJordan Brown 
3831fcced4cSJordan Brown 	int i;
3841fcced4cSJordan Brown 	directory_values_rpc *llvals;
3851fcced4cSJordan Brown 	directory_error_t de;
3861fcced4cSJordan Brown 	char *domain = NULL;
3871fcced4cSJordan Brown 
3881fcced4cSJordan Brown 	/*
3891fcced4cSJordan Brown 	 * We don't have a way to filter for entries from the right domain
3901fcced4cSJordan Brown 	 * in the LDAP query, so we check for it here.  Searches based on
3911fcced4cSJordan Brown 	 * samAccountName might yield results from the wrong domain.
3921fcced4cSJordan Brown 	 */
3931fcced4cSJordan Brown 	de = get_domain(ld, msg, &domain);
3941fcced4cSJordan Brown 	if (de != NULL)
3951fcced4cSJordan Brown 		goto err;
3961fcced4cSJordan Brown 
3971fcced4cSJordan Brown 	if (cbinfo->domain != NULL && !domain_eq(cbinfo->domain, domain))
3981fcced4cSJordan Brown 		goto out;
3991fcced4cSJordan Brown 
4001fcced4cSJordan Brown 	/*
4011fcced4cSJordan Brown 	 * If we've already found a match, error.
4021fcced4cSJordan Brown 	 */
4031fcced4cSJordan Brown 	if (pent->status != DIRECTORY_NOT_FOUND) {
4041fcced4cSJordan Brown 		de = directory_error("Duplicate.AD",
4051fcced4cSJordan Brown 		    "Multiple matching entries found", NULL);
4061fcced4cSJordan Brown 		goto err;
4071fcced4cSJordan Brown 	}
4081fcced4cSJordan Brown 
4091fcced4cSJordan Brown 	llvals = calloc(nattrs, sizeof (directory_values_rpc));
4101fcced4cSJordan Brown 	if (llvals == NULL)
4111fcced4cSJordan Brown 		goto nomem;
4121fcced4cSJordan Brown 
4131fcced4cSJordan Brown 	pent->directory_entry_rpc_u.attrs.attrs_val = llvals;
4141fcced4cSJordan Brown 	pent->directory_entry_rpc_u.attrs.attrs_len = nattrs;
4151fcced4cSJordan Brown 	pent->status = DIRECTORY_FOUND;
4161fcced4cSJordan Brown 
4171fcced4cSJordan Brown 	for (i = 0; i < nattrs; i++) {
4181fcced4cSJordan Brown 		struct berval **bv;
4191fcced4cSJordan Brown 		const char *a = attrs[i];
4201fcced4cSJordan Brown 		directory_values_rpc *val = &llvals[i];
4211fcced4cSJordan Brown 
4221fcced4cSJordan Brown 		bv = ldap_get_values_len(ld, msg, a);
4231fcced4cSJordan Brown #if	defined(DUMP_VALUES)
4241fcced4cSJordan Brown 		dump_bv_list(attrs[i], bv);
4251fcced4cSJordan Brown #endif
4261fcced4cSJordan Brown 		if (bv != NULL) {
4271fcced4cSJordan Brown 			de = bv_list_dav(val, bv);
4281fcced4cSJordan Brown 			ldap_value_free_len(bv);
4291fcced4cSJordan Brown 			if (de != NULL)
4301fcced4cSJordan Brown 				goto err;
431cb174861Sjoyce mcintosh 		} else if (uu_strcaseeq(a, "x-sun-canonicalName")) {
4321fcced4cSJordan Brown 			bv = ldap_get_values_len(ld, msg, "sAMAccountName");
4331fcced4cSJordan Brown 			if (bv != NULL) {
4341fcced4cSJordan Brown 				int n = ldap_count_values_len(bv);
4351fcced4cSJordan Brown 				if (n > 0) {
4361fcced4cSJordan Brown 					char *tmp;
4371fcced4cSJordan Brown 					(void) asprintf(&tmp, "%.*s@%s",
4381fcced4cSJordan Brown 					    bv[0]->bv_len, bv[0]->bv_val,
4391fcced4cSJordan Brown 					    domain);
4401fcced4cSJordan Brown 					if (tmp == NULL)
4411fcced4cSJordan Brown 						goto nomem;
4421fcced4cSJordan Brown 					const char *ctmp = tmp;
4431fcced4cSJordan Brown 					de = str_list_dav(val, &ctmp, 1);
4441fcced4cSJordan Brown 					free(tmp);
4451fcced4cSJordan Brown 					if (de != NULL)
4461fcced4cSJordan Brown 						goto err;
4471fcced4cSJordan Brown 				}
4481fcced4cSJordan Brown 			}
449cb174861Sjoyce mcintosh 		} else if (uu_strcaseeq(a, "x-sun-provider")) {
4501fcced4cSJordan Brown 			const char *provider = "LDAP-AD";
4511fcced4cSJordan Brown 			de = str_list_dav(val, &provider, 1);
4521fcced4cSJordan Brown 		}
4531fcced4cSJordan Brown 	}
4541fcced4cSJordan Brown 
4551fcced4cSJordan Brown 	goto out;
4561fcced4cSJordan Brown 
4571fcced4cSJordan Brown nomem:
4581fcced4cSJordan Brown 	de = directory_error("ENOMEM.users",
4591fcced4cSJordan Brown 	    "No memory allocating return value for user lookup", NULL);
4601fcced4cSJordan Brown 
4611fcced4cSJordan Brown err:
4621fcced4cSJordan Brown 	directory_entry_set_error(pent, de);
4631fcced4cSJordan Brown 	de = NULL;
4641fcced4cSJordan Brown 
4651fcced4cSJordan Brown out:
4661fcced4cSJordan Brown 	free(domain);
4671fcced4cSJordan Brown }
4681fcced4cSJordan Brown 
4691fcced4cSJordan Brown /*
4701fcced4cSJordan Brown  * Given a struct berval, populate a directory attribute value (which is a
4711fcced4cSJordan Brown  * list of values).
4721fcced4cSJordan Brown  * Note that here we populate the DAV with the exact bytes that LDAP returns.
4731fcced4cSJordan Brown  * Back over in the client it appends a \0 so that strings are null
4741fcced4cSJordan Brown  * terminated.
4751fcced4cSJordan Brown  */
4761fcced4cSJordan Brown static
4771fcced4cSJordan Brown directory_error_t
bv_list_dav(directory_values_rpc * lvals,struct berval ** bv)4781fcced4cSJordan Brown bv_list_dav(directory_values_rpc *lvals, struct berval **bv)
4791fcced4cSJordan Brown {
4801fcced4cSJordan Brown 	directory_value_rpc *dav;
4811fcced4cSJordan Brown 	int n;
4821fcced4cSJordan Brown 	int i;
4831fcced4cSJordan Brown 
4841fcced4cSJordan Brown 	n = ldap_count_values_len(bv);
4851fcced4cSJordan Brown 
4861fcced4cSJordan Brown 	dav = calloc(n, sizeof (directory_value_rpc));
4871fcced4cSJordan Brown 	if (dav == NULL)
4881fcced4cSJordan Brown 		goto nomem;
4891fcced4cSJordan Brown 
4901fcced4cSJordan Brown 	lvals->directory_values_rpc_u.values.values_val = dav;
4911fcced4cSJordan Brown 	lvals->directory_values_rpc_u.values.values_len = n;
4921fcced4cSJordan Brown 	lvals->found = TRUE;
4931fcced4cSJordan Brown 
4941fcced4cSJordan Brown 	for (i = 0; i < n; i++) {
4951fcced4cSJordan Brown 		dav[i].directory_value_rpc_val =
496cb174861Sjoyce mcintosh 		    uu_memdup(bv[i]->bv_val, bv[i]->bv_len);
4971fcced4cSJordan Brown 		if (dav[i].directory_value_rpc_val == NULL)
4981fcced4cSJordan Brown 			goto nomem;
4991fcced4cSJordan Brown 		dav[i].directory_value_rpc_len = bv[i]->bv_len;
5001fcced4cSJordan Brown 	}
5011fcced4cSJordan Brown 
5021fcced4cSJordan Brown 	return (NULL);
5031fcced4cSJordan Brown 
5041fcced4cSJordan Brown nomem:
5051fcced4cSJordan Brown 	return (directory_error("ENOMEM.bv_list_dav",
5061fcced4cSJordan Brown 	    "Insufficient memory copying values"));
5071fcced4cSJordan Brown }
5081fcced4cSJordan Brown 
5091fcced4cSJordan Brown #if	defined(DUMP_VALUES)
5101fcced4cSJordan Brown static
5111fcced4cSJordan Brown void
dump_bv_list(const char * attr,struct berval ** bv)5121fcced4cSJordan Brown dump_bv_list(const char *attr, struct berval **bv)
5131fcced4cSJordan Brown {
5141fcced4cSJordan Brown 	int i;
5151fcced4cSJordan Brown 
5161fcced4cSJordan Brown 	if (bv == NULL) {
5171fcced4cSJordan Brown 		(void) fprintf(stderr, "%s:  (empty)\n", attr);
5181fcced4cSJordan Brown 		return;
5191fcced4cSJordan Brown 	}
5201fcced4cSJordan Brown 	for (i = 0; bv[i] != NULL; i++) {
5211fcced4cSJordan Brown 		(void) fprintf(stderr, "%s[%d] =\n", attr, i);
5221fcced4cSJordan Brown 		dump(stderr, "    ", bv[i]->bv_val, bv[i]->bv_len);
5231fcced4cSJordan Brown 	}
5241fcced4cSJordan Brown }
5251fcced4cSJordan Brown #endif	/* DUMP_VALUES */
5261fcced4cSJordan Brown 
5271fcced4cSJordan Brown /*
5281fcced4cSJordan Brown  * Return the domain associated with the specified entry.
5291fcced4cSJordan Brown  */
5301fcced4cSJordan Brown static
5311fcced4cSJordan Brown directory_error_t
get_domain(LDAP * ld,LDAPMessage * msg,char ** domain)5321fcced4cSJordan Brown get_domain(
5331fcced4cSJordan Brown     LDAP *ld,
5341fcced4cSJordan Brown     LDAPMessage *msg,
5351fcced4cSJordan Brown     char **domain)
5361fcced4cSJordan Brown {
5371fcced4cSJordan Brown 	*domain = NULL;
5381fcced4cSJordan Brown 
5391fcced4cSJordan Brown 	char *dn = ldap_get_dn(ld, msg);
5401fcced4cSJordan Brown 	if (dn == NULL) {
5411fcced4cSJordan Brown 		char buf[100];	/* big enough for any int */
5421fcced4cSJordan Brown 		char *m;
5431fcced4cSJordan Brown 		char *s;
5441fcced4cSJordan Brown 		int err = ldap_get_lderrno(ld, &m, &s);
5451fcced4cSJordan Brown 		(void) snprintf(buf, sizeof (buf), "%d", err);
5461fcced4cSJordan Brown 
5471fcced4cSJordan Brown 		return directory_error("AD.get_domain.ldap_get_dn",
5481fcced4cSJordan Brown 		    "ldap_get_dn: %1 (%2)\n"
5491fcced4cSJordan Brown 		    "matched: %3\n"
5501fcced4cSJordan Brown 		    "error:   %4",
5511fcced4cSJordan Brown 		    ldap_err2string(err), buf,
5521fcced4cSJordan Brown 		    m == NULL ? "(null)" : m,
5531fcced4cSJordan Brown 		    s == NULL ? "(null)" : s,
5541fcced4cSJordan Brown 		    NULL);
5551fcced4cSJordan Brown 	}
5561fcced4cSJordan Brown 
5571fcced4cSJordan Brown 	*domain = adutils_dn2dns(dn);
5581fcced4cSJordan Brown 	if (*domain == NULL) {
5591fcced4cSJordan Brown 		directory_error_t de;
5601fcced4cSJordan Brown 
5611fcced4cSJordan Brown 		de = directory_error("Unknown.get_domain.adutils_dn2dns",
5621fcced4cSJordan Brown 		    "get_domain:  Unexpected error from adutils_dn2dns(%1)",
5631fcced4cSJordan Brown 		    dn, NULL);
5641fcced4cSJordan Brown 		free(dn);
5651fcced4cSJordan Brown 		return (de);
5661fcced4cSJordan Brown 	}
5671fcced4cSJordan Brown 	free(dn);
5681fcced4cSJordan Brown 
5691fcced4cSJordan Brown 	return (NULL);
5701fcced4cSJordan Brown }
5711fcced4cSJordan Brown 
5721fcced4cSJordan Brown /*
5731fcced4cSJordan Brown  * Given an error report from libadutils, generate a directory_error_t.
5741fcced4cSJordan Brown  */
5751fcced4cSJordan Brown static
5761fcced4cSJordan Brown directory_error_t
directory_provider_ad_utils_error(char * func,int rc)5771fcced4cSJordan Brown directory_provider_ad_utils_error(char *func, int rc)
5781fcced4cSJordan Brown {
5791fcced4cSJordan Brown 	char rcstr[100];	/* plenty for any int */
5801fcced4cSJordan Brown 	char code[100];		/* plenty for any int */
5811fcced4cSJordan Brown 	(void) snprintf(rcstr, sizeof (rcstr), "%d", rc);
5821fcced4cSJordan Brown 	(void) snprintf(code, sizeof (code), "ADUTILS.%d", rc);
5831fcced4cSJordan Brown 
5841fcced4cSJordan Brown 	return (directory_error(code,
5851fcced4cSJordan Brown 	    "Error %2 from adutils function %1", func, rcstr, NULL));
5861fcced4cSJordan Brown }
5871fcced4cSJordan Brown 
5881fcced4cSJordan Brown struct directory_provider_static directory_provider_ad = {
5891fcced4cSJordan Brown 	"AD",
5901fcced4cSJordan Brown 	directory_provider_ad_get,
5911fcced4cSJordan Brown };