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 /*
231fcced4cSJordan Brown  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241fcced4cSJordan Brown  * Use is subject to license terms.
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>
391fcced4cSJordan Brown #include <note.h>
401fcced4cSJordan Brown #include <assert.h>
411fcced4cSJordan Brown #include "directory.h"
421fcced4cSJordan Brown #include "directory_private.h"
431fcced4cSJordan Brown #include "idmapd.h"
441fcced4cSJordan Brown #include <rpcsvc/idmap_prot.h>
451fcced4cSJordan Brown #include "directory_server_impl.h"
461fcced4cSJordan Brown #include "miscutils.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
851fcced4cSJordan Brown maybe_add_to_list(const char **list, const char *s)
861fcced4cSJordan Brown {
871fcced4cSJordan Brown 	for (; *list != NULL; list++) {
881fcced4cSJordan Brown 		if (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 **
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 		 */
1201fcced4cSJordan Brown 		if (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
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.
1481fcced4cSJordan Brown 	 */
149*e3f2c991SKeyur Desai 	if (_idmapdstate.num_gcs < 1) {
1501fcced4cSJordan Brown 		return (NULL);
1511fcced4cSJordan Brown 	}
1521fcced4cSJordan Brown 
1531fcced4cSJordan Brown 	RDLOCK_CONFIG()
1541fcced4cSJordan Brown 
1551fcced4cSJordan Brown 	/* 6835280 spurious lint error if the strlen is in the declaration */
1561fcced4cSJordan Brown 	int len = strlen(_idmapdstate.cfg->pgcfg.default_domain);
1571fcced4cSJordan Brown 	char default_domain[len + 1];
1581fcced4cSJordan Brown 	(void) strcpy(default_domain, _idmapdstate.cfg->pgcfg.default_domain);
1591fcced4cSJordan Brown 
1601fcced4cSJordan Brown 	UNLOCK_CONFIG();
1611fcced4cSJordan Brown 
1621fcced4cSJordan Brown 	/*
1631fcced4cSJordan Brown 	 * Turn our counted-array argument into a NULL-terminated array.
1641fcced4cSJordan Brown 	 * At the same time, add in any attributes that we need to support
1651fcced4cSJordan Brown 	 * any requested synthesized attributes.
1661fcced4cSJordan Brown 	 */
1671fcced4cSJordan Brown 	attrs2 = copy_and_augment_attr_list(attrs->idmap_utf8str_list_val,
1681fcced4cSJordan Brown 	    attrs->idmap_utf8str_list_len);
1691fcced4cSJordan Brown 	if (attrs2 == NULL)
1701fcced4cSJordan Brown 		goto nomem;
1711fcced4cSJordan Brown 
1721fcced4cSJordan Brown 	for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
1731fcced4cSJordan Brown 		char *vw[3];
1741fcced4cSJordan Brown 		int type;
1751fcced4cSJordan Brown 
1761fcced4cSJordan Brown 		/*
1771fcced4cSJordan Brown 		 * Extract the type for this particular ID.
1781fcced4cSJordan Brown 		 * Advance to the next type, if it's there, else keep
1791fcced4cSJordan Brown 		 * using this type until we run out of IDs.
1801fcced4cSJordan Brown 		 */
1811fcced4cSJordan Brown 		type = *types;
1821fcced4cSJordan Brown 		if (*(types+1) != '\0')
1831fcced4cSJordan Brown 			types++;
1841fcced4cSJordan Brown 
1851fcced4cSJordan Brown 		/*
1861fcced4cSJordan Brown 		 * If this entry has already been handled, one way or another,
1871fcced4cSJordan Brown 		 * skip it.
1881fcced4cSJordan Brown 		 */
1891fcced4cSJordan Brown 		if (del[i].status != DIRECTORY_NOT_FOUND)
1901fcced4cSJordan Brown 			continue;
1911fcced4cSJordan Brown 
1921fcced4cSJordan Brown 		char *id = ids->idmap_utf8str_list_val[i];
1931fcced4cSJordan Brown 
1941fcced4cSJordan Brown 		/*
1951fcced4cSJordan Brown 		 * Allow for expanding every character to \xx, plus some
1961fcced4cSJordan Brown 		 * space for the query syntax.
1971fcced4cSJordan Brown 		 */
1981fcced4cSJordan Brown 		int id_len = strlen(id);
1991fcced4cSJordan Brown 		char filter[1000 + id_len*3];
2001fcced4cSJordan Brown 
2011fcced4cSJordan Brown 		if (type == DIRECTORY_ID_SID[0]) {
2021fcced4cSJordan Brown 			/*
2031fcced4cSJordan Brown 			 * Mildly surprisingly, AD appears to allow searching
2041fcced4cSJordan Brown 			 * based on text SIDs.  Must be a special case on the
2051fcced4cSJordan Brown 			 * server end.
2061fcced4cSJordan Brown 			 */
2071fcced4cSJordan Brown 			ldap_build_filter(filter, sizeof (filter),
2081fcced4cSJordan Brown 			    "(objectSid=%v)", NULL, NULL, NULL, id, NULL);
2091fcced4cSJordan Brown 
2101fcced4cSJordan Brown 			de = directory_provider_ad_lookup(&del[i], attrs2,
2111fcced4cSJordan Brown 			    attrs->idmap_utf8str_list_len, NULL, filter);
2121fcced4cSJordan Brown 			if (de != NULL) {
2131fcced4cSJordan Brown 				directory_entry_set_error(&del[i], de);
2141fcced4cSJordan Brown 				de = NULL;
2151fcced4cSJordan Brown 			}
2161fcced4cSJordan Brown 		} else {
2171fcced4cSJordan Brown 			int id_len = strlen(id);
2181fcced4cSJordan Brown 			char name[id_len + 1];
2191fcced4cSJordan Brown 			char domain[id_len + 1];
2201fcced4cSJordan Brown 
2211fcced4cSJordan Brown 			split_name(name, domain, id);
2221fcced4cSJordan Brown 
2231fcced4cSJordan Brown 			vw[0] = name;
2241fcced4cSJordan Brown 
2251fcced4cSJordan Brown 			if (streq(domain, "")) {
2261fcced4cSJordan Brown 				vw[1] = default_domain;
2271fcced4cSJordan Brown 			} else {
2281fcced4cSJordan Brown 				vw[1] = domain;
2291fcced4cSJordan Brown 			}
2301fcced4cSJordan Brown 
2311fcced4cSJordan Brown 			if (type == DIRECTORY_ID_USER[0])
2321fcced4cSJordan Brown 				vw[2] = "user";
2331fcced4cSJordan Brown 			else if (type == DIRECTORY_ID_GROUP[0])
2341fcced4cSJordan Brown 				vw[2] = "group";
2351fcced4cSJordan Brown 			else
2361fcced4cSJordan Brown 				vw[2] = "*";
2371fcced4cSJordan Brown 
2381fcced4cSJordan Brown 			/*
2391fcced4cSJordan Brown 			 * Try samAccountName.
2401fcced4cSJordan Brown 			 * Note that here we rely on checking the returned
2411fcced4cSJordan Brown 			 * distinguishedName to make sure that we found an
2421fcced4cSJordan Brown 			 * entry from the right domain, because there's no
2431fcced4cSJordan Brown 			 * attribute we can straightforwardly filter for to
2441fcced4cSJordan Brown 			 * match domain.
2451fcced4cSJordan Brown 			 *
2461fcced4cSJordan Brown 			 * Eventually we should perhaps also try
2471fcced4cSJordan Brown 			 * userPrincipalName.
2481fcced4cSJordan Brown 			 */
2491fcced4cSJordan Brown 			ldap_build_filter(filter, sizeof (filter),
2501fcced4cSJordan Brown 			    "(&(samAccountName=%v1)(objectClass=%v3))",
2511fcced4cSJordan Brown 			    NULL, NULL, NULL, NULL, vw);
2521fcced4cSJordan Brown 
2531fcced4cSJordan Brown 			de = directory_provider_ad_lookup(&del[i], attrs2,
2541fcced4cSJordan Brown 			    attrs->idmap_utf8str_list_len, vw[1], filter);
2551fcced4cSJordan Brown 			if (de != NULL) {
2561fcced4cSJordan Brown 				directory_entry_set_error(&del[i], de);
2571fcced4cSJordan Brown 				de = NULL;
2581fcced4cSJordan Brown 			}
2591fcced4cSJordan Brown 		}
2601fcced4cSJordan Brown 	}
2611fcced4cSJordan Brown 
2621fcced4cSJordan Brown 	de = NULL;
2631fcced4cSJordan Brown 
2641fcced4cSJordan Brown 	goto out;
2651fcced4cSJordan Brown 
2661fcced4cSJordan Brown nomem:
2671fcced4cSJordan Brown 	de = directory_error("ENOMEM.AD",
2681fcced4cSJordan Brown 	    "Out of memory during AD lookup", NULL);
2691fcced4cSJordan Brown out:
2701fcced4cSJordan Brown 	free(attrs2);
2711fcced4cSJordan Brown 	return (de);
2721fcced4cSJordan Brown }
2731fcced4cSJordan Brown 
2741fcced4cSJordan Brown /*
2751fcced4cSJordan Brown  * Note that attrs is NULL terminated, and that nattrs is the number
2761fcced4cSJordan Brown  * of attributes requested by the user... which might be fewer than are
2771fcced4cSJordan Brown  * in attrs because of attributes that we need for our own processing.
2781fcced4cSJordan Brown  */
2791fcced4cSJordan Brown static
2801fcced4cSJordan Brown directory_error_t
2811fcced4cSJordan Brown directory_provider_ad_lookup(
2821fcced4cSJordan Brown     directory_entry_rpc *pent,
2831fcced4cSJordan Brown     const char * const * attrs,
2841fcced4cSJordan Brown     int nattrs,
2851fcced4cSJordan Brown     const char *domain,
2861fcced4cSJordan Brown     const char *filter)
2871fcced4cSJordan Brown {
2881fcced4cSJordan Brown 	adutils_ad_t *ad;
2891fcced4cSJordan Brown 	adutils_rc batchrc;
2901fcced4cSJordan Brown 	struct cbinfo cbinfo;
2911fcced4cSJordan Brown 	adutils_query_state_t *qs;
2921fcced4cSJordan Brown 	int rc;
2931fcced4cSJordan Brown 
2941fcced4cSJordan Brown 	/*
2951fcced4cSJordan Brown 	 * NEEDSWORK:  Should eventually handle other forests.
2961fcced4cSJordan Brown 	 * NEEDSWORK:  Should eventually handle non-GC attributes.
2971fcced4cSJordan Brown 	 */
298*e3f2c991SKeyur Desai 	ad = _idmapdstate.gcs[0];
2991fcced4cSJordan Brown 
3001fcced4cSJordan Brown 	/* Stash away information for the callback function. */
3011fcced4cSJordan Brown 	cbinfo.attrs = attrs;
3021fcced4cSJordan Brown 	cbinfo.nattrs = nattrs;
3031fcced4cSJordan Brown 	cbinfo.entry = pent;
3041fcced4cSJordan Brown 	cbinfo.domain = domain;
3051fcced4cSJordan Brown 
3061fcced4cSJordan Brown 	rc = adutils_lookup_batch_start(ad, 1, directory_provider_ad_cb,
3071fcced4cSJordan Brown 	    &cbinfo, &qs);
3081fcced4cSJordan Brown 	if (rc != ADUTILS_SUCCESS) {
3091fcced4cSJordan Brown 		return (directory_provider_ad_utils_error(
3101fcced4cSJordan Brown 		    "adutils_lookup_batch_start", rc));
3111fcced4cSJordan Brown 	}
3121fcced4cSJordan Brown 
3131fcced4cSJordan Brown 	rc = adutils_lookup_batch_add(qs, filter, attrs, domain,
3141fcced4cSJordan Brown 	    NULL, &batchrc);
3151fcced4cSJordan Brown 	if (rc != ADUTILS_SUCCESS) {
3161fcced4cSJordan Brown 		adutils_lookup_batch_release(&qs);
3171fcced4cSJordan Brown 		return (directory_provider_ad_utils_error(
3181fcced4cSJordan Brown 		    "adutils_lookup_batch_add", rc));
3191fcced4cSJordan Brown 	}
3201fcced4cSJordan Brown 
3211fcced4cSJordan Brown 	rc = adutils_lookup_batch_end(&qs);
3221fcced4cSJordan Brown 	if (rc != ADUTILS_SUCCESS) {
3231fcced4cSJordan Brown 		return (directory_provider_ad_utils_error(
3241fcced4cSJordan Brown 		    "adutils_lookup_batch_end", rc));
3251fcced4cSJordan Brown 	}
3261fcced4cSJordan Brown 
3271fcced4cSJordan Brown 	if (batchrc != ADUTILS_SUCCESS) {
3281fcced4cSJordan Brown 		/*
3291fcced4cSJordan Brown 		 * NEEDSWORK:  We're consistently getting -9997 here.
3301fcced4cSJordan Brown 		 * What does it mean?
3311fcced4cSJordan Brown 		 */
3321fcced4cSJordan Brown 		return (NULL);
3331fcced4cSJordan Brown 	}
3341fcced4cSJordan Brown 
3351fcced4cSJordan Brown 	return (NULL);
3361fcced4cSJordan Brown }
3371fcced4cSJordan Brown 
3381fcced4cSJordan Brown /*
3391fcced4cSJordan Brown  * Callback from the LDAP functions when they get responses.
3401fcced4cSJordan Brown  * We don't really need (nor want) asynchronous handling, but it's
3411fcced4cSJordan Brown  * what libadutils gives us.
3421fcced4cSJordan Brown  */
3431fcced4cSJordan Brown static
3441fcced4cSJordan Brown void
3451fcced4cSJordan Brown directory_provider_ad_cb(
3461fcced4cSJordan Brown     LDAP *ld,
3471fcced4cSJordan Brown     LDAPMessage **ldapres,
3481fcced4cSJordan Brown     int rc,
3491fcced4cSJordan Brown     int qid,
3501fcced4cSJordan Brown     void *argp)
3511fcced4cSJordan Brown {
3521fcced4cSJordan Brown 	NOTE(ARGUNUSED(rc, qid))
3531fcced4cSJordan Brown 	struct cbinfo *cbinfo = (struct cbinfo *)argp;
3541fcced4cSJordan Brown 	LDAPMessage *msg = *ldapres;
3551fcced4cSJordan Brown 
3561fcced4cSJordan Brown 	for (msg = ldap_first_entry(ld, msg);
3571fcced4cSJordan Brown 	    msg != NULL;
3581fcced4cSJordan Brown 	    msg = ldap_next_entry(ld, msg)) {
3591fcced4cSJordan Brown 		directory_provider_ad_cb1(ld, msg, cbinfo);
3601fcced4cSJordan Brown 	}
3611fcced4cSJordan Brown }
3621fcced4cSJordan Brown 
3631fcced4cSJordan Brown /*
3641fcced4cSJordan Brown  * Process a single entry returned by an LDAP callback.
3651fcced4cSJordan Brown  * Note that this performs a function roughly equivalent to the
3661fcced4cSJordan Brown  * directory*Populate() functions in the other providers.
3671fcced4cSJordan Brown  * Given an LDAP response, populate the directory entry for return to
3681fcced4cSJordan Brown  * the caller.  This one differs primarily in that we're working directly
3691fcced4cSJordan Brown  * with LDAP, so we don't have to do any attribute translation.
3701fcced4cSJordan Brown  */
3711fcced4cSJordan Brown static
3721fcced4cSJordan Brown void
3731fcced4cSJordan Brown directory_provider_ad_cb1(
3741fcced4cSJordan Brown     LDAP *ld,
3751fcced4cSJordan Brown     LDAPMessage *msg,
3761fcced4cSJordan Brown     struct cbinfo *cbinfo)
3771fcced4cSJordan Brown {
3781fcced4cSJordan Brown 	int nattrs = cbinfo->nattrs;
3791fcced4cSJordan Brown 	const char * const *attrs = cbinfo->attrs;
3801fcced4cSJordan Brown 	directory_entry_rpc *pent = cbinfo->entry;
3811fcced4cSJordan Brown 
3821fcced4cSJordan Brown 	int i;
3831fcced4cSJordan Brown 	directory_values_rpc *llvals;
3841fcced4cSJordan Brown 	directory_error_t de;
3851fcced4cSJordan Brown 	char *domain = NULL;
3861fcced4cSJordan Brown 
3871fcced4cSJordan Brown 	/*
3881fcced4cSJordan Brown 	 * We don't have a way to filter for entries from the right domain
3891fcced4cSJordan Brown 	 * in the LDAP query, so we check for it here.  Searches based on
3901fcced4cSJordan Brown 	 * samAccountName might yield results from the wrong domain.
3911fcced4cSJordan Brown 	 */
3921fcced4cSJordan Brown 	de = get_domain(ld, msg, &domain);
3931fcced4cSJordan Brown 	if (de != NULL)
3941fcced4cSJordan Brown 		goto err;
3951fcced4cSJordan Brown 
3961fcced4cSJordan Brown 	if (cbinfo->domain != NULL && !domain_eq(cbinfo->domain, domain))
3971fcced4cSJordan Brown 		goto out;
3981fcced4cSJordan Brown 
3991fcced4cSJordan Brown 	/*
4001fcced4cSJordan Brown 	 * If we've already found a match, error.
4011fcced4cSJordan Brown 	 */
4021fcced4cSJordan Brown 	if (pent->status != DIRECTORY_NOT_FOUND) {
4031fcced4cSJordan Brown 		de = directory_error("Duplicate.AD",
4041fcced4cSJordan Brown 		    "Multiple matching entries found", NULL);
4051fcced4cSJordan Brown 		goto err;
4061fcced4cSJordan Brown 	}
4071fcced4cSJordan Brown 
4081fcced4cSJordan Brown 	llvals = calloc(nattrs, sizeof (directory_values_rpc));
4091fcced4cSJordan Brown 	if (llvals == NULL)
4101fcced4cSJordan Brown 		goto nomem;
4111fcced4cSJordan Brown 
4121fcced4cSJordan Brown 	pent->directory_entry_rpc_u.attrs.attrs_val = llvals;
4131fcced4cSJordan Brown 	pent->directory_entry_rpc_u.attrs.attrs_len = nattrs;
4141fcced4cSJordan Brown 	pent->status = DIRECTORY_FOUND;
4151fcced4cSJordan Brown 
4161fcced4cSJordan Brown 	for (i = 0; i < nattrs; i++) {
4171fcced4cSJordan Brown 		struct berval **bv;
4181fcced4cSJordan Brown 		const char *a = attrs[i];
4191fcced4cSJordan Brown 		directory_values_rpc *val = &llvals[i];
4201fcced4cSJordan Brown 
4211fcced4cSJordan Brown 		bv = ldap_get_values_len(ld, msg, a);
4221fcced4cSJordan Brown #if	defined(DUMP_VALUES)
4231fcced4cSJordan Brown 		dump_bv_list(attrs[i], bv);
4241fcced4cSJordan Brown #endif
4251fcced4cSJordan Brown 		if (bv != NULL) {
4261fcced4cSJordan Brown 			de = bv_list_dav(val, bv);
4271fcced4cSJordan Brown 			ldap_value_free_len(bv);
4281fcced4cSJordan Brown 			if (de != NULL)
4291fcced4cSJordan Brown 				goto err;
4301fcced4cSJordan Brown 		} else if (strcaseeq(a, "x-sun-canonicalName")) {
4311fcced4cSJordan Brown 			bv = ldap_get_values_len(ld, msg, "sAMAccountName");
4321fcced4cSJordan Brown 			if (bv != NULL) {
4331fcced4cSJordan Brown 				int n = ldap_count_values_len(bv);
4341fcced4cSJordan Brown 				if (n > 0) {
4351fcced4cSJordan Brown 					char *tmp;
4361fcced4cSJordan Brown 					(void) asprintf(&tmp, "%.*s@%s",
4371fcced4cSJordan Brown 					    bv[0]->bv_len, bv[0]->bv_val,
4381fcced4cSJordan Brown 					    domain);
4391fcced4cSJordan Brown 					if (tmp == NULL)
4401fcced4cSJordan Brown 						goto nomem;
4411fcced4cSJordan Brown 					const char *ctmp = tmp;
4421fcced4cSJordan Brown 					de = str_list_dav(val, &ctmp, 1);
4431fcced4cSJordan Brown 					free(tmp);
4441fcced4cSJordan Brown 					if (de != NULL)
4451fcced4cSJordan Brown 						goto err;
4461fcced4cSJordan Brown 				}
4471fcced4cSJordan Brown 			}
4481fcced4cSJordan Brown 		} else if (strcaseeq(a, "x-sun-provider")) {
4491fcced4cSJordan Brown 			const char *provider = "LDAP-AD";
4501fcced4cSJordan Brown 			de = str_list_dav(val, &provider, 1);
4511fcced4cSJordan Brown 		}
4521fcced4cSJordan Brown 	}
4531fcced4cSJordan Brown 
4541fcced4cSJordan Brown 	goto out;
4551fcced4cSJordan Brown 
4561fcced4cSJordan Brown nomem:
4571fcced4cSJordan Brown 	de = directory_error("ENOMEM.users",
4581fcced4cSJordan Brown 	    "No memory allocating return value for user lookup", NULL);
4591fcced4cSJordan Brown 
4601fcced4cSJordan Brown err:
4611fcced4cSJordan Brown 	directory_entry_set_error(pent, de);
4621fcced4cSJordan Brown 	de = NULL;
4631fcced4cSJordan Brown 
4641fcced4cSJordan Brown out:
4651fcced4cSJordan Brown 	free(domain);
4661fcced4cSJordan Brown }
4671fcced4cSJordan Brown 
4681fcced4cSJordan Brown /*
4691fcced4cSJordan Brown  * Given a struct berval, populate a directory attribute value (which is a
4701fcced4cSJordan Brown  * list of values).
4711fcced4cSJordan Brown  * Note that here we populate the DAV with the exact bytes that LDAP returns.
4721fcced4cSJordan Brown  * Back over in the client it appends a \0 so that strings are null
4731fcced4cSJordan Brown  * terminated.
4741fcced4cSJordan Brown  */
4751fcced4cSJordan Brown static
4761fcced4cSJordan Brown directory_error_t
4771fcced4cSJordan Brown bv_list_dav(directory_values_rpc *lvals, struct berval **bv)
4781fcced4cSJordan Brown {
4791fcced4cSJordan Brown 	directory_value_rpc *dav;
4801fcced4cSJordan Brown 	int n;
4811fcced4cSJordan Brown 	int i;
4821fcced4cSJordan Brown 
4831fcced4cSJordan Brown 	n = ldap_count_values_len(bv);
4841fcced4cSJordan Brown 
4851fcced4cSJordan Brown 	dav = calloc(n, sizeof (directory_value_rpc));
4861fcced4cSJordan Brown 	if (dav == NULL)
4871fcced4cSJordan Brown 		goto nomem;
4881fcced4cSJordan Brown 
4891fcced4cSJordan Brown 	lvals->directory_values_rpc_u.values.values_val = dav;
4901fcced4cSJordan Brown 	lvals->directory_values_rpc_u.values.values_len = n;
4911fcced4cSJordan Brown 	lvals->found = TRUE;
4921fcced4cSJordan Brown 
4931fcced4cSJordan Brown 	for (i = 0; i < n; i++) {
4941fcced4cSJordan Brown 		dav[i].directory_value_rpc_val =
4951fcced4cSJordan Brown 		    memdup(bv[i]->bv_val, bv[i]->bv_len);
4961fcced4cSJordan Brown 		if (dav[i].directory_value_rpc_val == NULL)
4971fcced4cSJordan Brown 			goto nomem;
4981fcced4cSJordan Brown 		dav[i].directory_value_rpc_len = bv[i]->bv_len;
4991fcced4cSJordan Brown 	}
5001fcced4cSJordan Brown 
5011fcced4cSJordan Brown 	return (NULL);
5021fcced4cSJordan Brown 
5031fcced4cSJordan Brown nomem:
5041fcced4cSJordan Brown 	return (directory_error("ENOMEM.bv_list_dav",
5051fcced4cSJordan Brown 	    "Insufficient memory copying values"));
5061fcced4cSJordan Brown }
5071fcced4cSJordan Brown 
5081fcced4cSJordan Brown #if	defined(DUMP_VALUES)
5091fcced4cSJordan Brown static
5101fcced4cSJordan Brown void
5111fcced4cSJordan Brown dump_bv_list(const char *attr, struct berval **bv)
5121fcced4cSJordan Brown {
5131fcced4cSJordan Brown 	int i;
5141fcced4cSJordan Brown 
5151fcced4cSJordan Brown 	if (bv == NULL) {
5161fcced4cSJordan Brown 		(void) fprintf(stderr, "%s:  (empty)\n", attr);
5171fcced4cSJordan Brown 		return;
5181fcced4cSJordan Brown 	}
5191fcced4cSJordan Brown 	for (i = 0; bv[i] != NULL; i++) {
5201fcced4cSJordan Brown 		(void) fprintf(stderr, "%s[%d] =\n", attr, i);
5211fcced4cSJordan Brown 		dump(stderr, "    ", bv[i]->bv_val, bv[i]->bv_len);
5221fcced4cSJordan Brown 	}
5231fcced4cSJordan Brown }
5241fcced4cSJordan Brown #endif	/* DUMP_VALUES */
5251fcced4cSJordan Brown 
5261fcced4cSJordan Brown /*
5271fcced4cSJordan Brown  * Return the domain associated with the specified entry.
5281fcced4cSJordan Brown  */
5291fcced4cSJordan Brown static
5301fcced4cSJordan Brown directory_error_t
5311fcced4cSJordan Brown get_domain(
5321fcced4cSJordan Brown     LDAP *ld,
5331fcced4cSJordan Brown     LDAPMessage *msg,
5341fcced4cSJordan Brown     char **domain)
5351fcced4cSJordan Brown {
5361fcced4cSJordan Brown 	*domain = NULL;
5371fcced4cSJordan Brown 
5381fcced4cSJordan Brown 	char *dn = ldap_get_dn(ld, msg);
5391fcced4cSJordan Brown 	if (dn == NULL) {
5401fcced4cSJordan Brown 		char buf[100];	/* big enough for any int */
5411fcced4cSJordan Brown 		char *m;
5421fcced4cSJordan Brown 		char *s;
5431fcced4cSJordan Brown 		int err = ldap_get_lderrno(ld, &m, &s);
5441fcced4cSJordan Brown 		(void) snprintf(buf, sizeof (buf), "%d", err);
5451fcced4cSJordan Brown 
5461fcced4cSJordan Brown 		return directory_error("AD.get_domain.ldap_get_dn",
5471fcced4cSJordan Brown 		    "ldap_get_dn: %1 (%2)\n"
5481fcced4cSJordan Brown 		    "matched: %3\n"
5491fcced4cSJordan Brown 		    "error:   %4",
5501fcced4cSJordan Brown 		    ldap_err2string(err), buf,
5511fcced4cSJordan Brown 		    m == NULL ? "(null)" : m,
5521fcced4cSJordan Brown 		    s == NULL ? "(null)" : s,
5531fcced4cSJordan Brown 		    NULL);
5541fcced4cSJordan Brown 	}
5551fcced4cSJordan Brown 
5561fcced4cSJordan Brown 	*domain = adutils_dn2dns(dn);
5571fcced4cSJordan Brown 	if (*domain == NULL) {
5581fcced4cSJordan Brown 		directory_error_t de;
5591fcced4cSJordan Brown 
5601fcced4cSJordan Brown 		de = directory_error("Unknown.get_domain.adutils_dn2dns",
5611fcced4cSJordan Brown 		    "get_domain:  Unexpected error from adutils_dn2dns(%1)",
5621fcced4cSJordan Brown 		    dn, NULL);
5631fcced4cSJordan Brown 		free(dn);
5641fcced4cSJordan Brown 		return (de);
5651fcced4cSJordan Brown 	}
5661fcced4cSJordan Brown 	free(dn);
5671fcced4cSJordan Brown 
5681fcced4cSJordan Brown 	return (NULL);
5691fcced4cSJordan Brown }
5701fcced4cSJordan Brown 
5711fcced4cSJordan Brown /*
5721fcced4cSJordan Brown  * Given an error report from libadutils, generate a directory_error_t.
5731fcced4cSJordan Brown  */
5741fcced4cSJordan Brown static
5751fcced4cSJordan Brown directory_error_t
5761fcced4cSJordan Brown directory_provider_ad_utils_error(char *func, int rc)
5771fcced4cSJordan Brown {
5781fcced4cSJordan Brown 	char rcstr[100];	/* plenty for any int */
5791fcced4cSJordan Brown 	char code[100];		/* plenty for any int */
5801fcced4cSJordan Brown 	(void) snprintf(rcstr, sizeof (rcstr), "%d", rc);
5811fcced4cSJordan Brown 	(void) snprintf(code, sizeof (code), "ADUTILS.%d", rc);
5821fcced4cSJordan Brown 
5831fcced4cSJordan Brown 	return (directory_error(code,
5841fcced4cSJordan Brown 	    "Error %2 from adutils function %1", func, rcstr, NULL));
5851fcced4cSJordan Brown }
5861fcced4cSJordan Brown 
5871fcced4cSJordan Brown struct directory_provider_static directory_provider_ad = {
5881fcced4cSJordan Brown 	"AD",
5891fcced4cSJordan Brown 	directory_provider_ad_get,
5901fcced4cSJordan Brown };
591