xref: /onnv-gate/usr/src/cmd/idmap/idmapd/adutils.c (revision 10122)
14520Snw141292 /*
24520Snw141292  * CDDL HEADER START
34520Snw141292  *
44520Snw141292  * The contents of this file are subject to the terms of the
54520Snw141292  * Common Development and Distribution License (the "License").
64520Snw141292  * You may not use this file except in compliance with the License.
74520Snw141292  *
84520Snw141292  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94520Snw141292  * or http://www.opensolaris.org/os/licensing.
104520Snw141292  * See the License for the specific language governing permissions
114520Snw141292  * and limitations under the License.
124520Snw141292  *
134520Snw141292  * When distributing Covered Code, include this CDDL HEADER in each
144520Snw141292  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154520Snw141292  * If applicable, add the following below this CDDL HEADER, with the
164520Snw141292  * fields enclosed by brackets "[]" replaced with your own identifying
174520Snw141292  * information: Portions Copyright [yyyy] [name of copyright owner]
184520Snw141292  *
194520Snw141292  * CDDL HEADER END
204520Snw141292  */
214520Snw141292 
224520Snw141292 /*
23*10122SJordan.Brown@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
244520Snw141292  * Use is subject to license terms.
254520Snw141292  */
264520Snw141292 
274520Snw141292 /*
284520Snw141292  * Processes name2sid & sid2name batched lookups for a given user or
294520Snw141292  * computer from an AD Directory server using GSSAPI authentication
304520Snw141292  */
314520Snw141292 
324520Snw141292 #include <stdio.h>
334520Snw141292 #include <stdlib.h>
344520Snw141292 #include <alloca.h>
354520Snw141292 #include <string.h>
364520Snw141292 #include <strings.h>
374520Snw141292 #include <lber.h>
384520Snw141292 #include <ldap.h>
394520Snw141292 #include <sasl/sasl.h>
404520Snw141292 #include <string.h>
414520Snw141292 #include <ctype.h>
424520Snw141292 #include <pthread.h>
434520Snw141292 #include <synch.h>
444520Snw141292 #include <atomic.h>
454520Snw141292 #include <errno.h>
464520Snw141292 #include <assert.h>
474520Snw141292 #include <limits.h>
488040SBaban.Kenkre@Sun.COM #include <time.h>
495696Snw141292 #include <sys/u8_textprep.h>
508040SBaban.Kenkre@Sun.COM #include "libadutils.h"
516616Sdm199847 #include "nldaputils.h"
524520Snw141292 #include "idmapd.h"
534520Snw141292 
544520Snw141292 /* Attribute names and filter format strings */
555447Snw141292 #define	SAN		"sAMAccountName"
565447Snw141292 #define	OBJSID		"objectSid"
575447Snw141292 #define	OBJCLASS	"objectClass"
584520Snw141292 #define	SANFILTER	"(sAMAccountName=%.*s)"
595447Snw141292 #define	OBJSIDFILTER	"(objectSid=%s)"
604520Snw141292 
618040SBaban.Kenkre@Sun.COM void	idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc,
628040SBaban.Kenkre@Sun.COM 		int qid, void *argp);
634520Snw141292 
644520Snw141292 /*
654520Snw141292  * A place to put the results of a batched (async) query
664520Snw141292  *
674520Snw141292  * There is one of these for every query added to a batch object
684520Snw141292  * (idmap_query_state, see below).
694520Snw141292  */
704520Snw141292 typedef struct idmap_q {
715447Snw141292 	/*
725447Snw141292 	 * data used for validating search result entries for name->SID
735731Sbaban 	 * lookups
745447Snw141292 	 */
755696Snw141292 	char			*ecanonname;	/* expected canon name */
765696Snw141292 	char			*edomain;	/* expected domain name */
775731Sbaban 	int			eunixtype;	/* expected unix type */
785447Snw141292 	/* results */
795696Snw141292 	char			**canonname;	/* actual canon name */
804520Snw141292 	char			**domain;	/* name of domain of object */
815731Sbaban 	char			**sid;		/* stringified SID */
825731Sbaban 	rid_t			*rid;		/* RID */
835731Sbaban 	int			*sid_type;	/* user or group SID? */
845731Sbaban 	char			**unixname;	/* unixname for name mapping */
856386Sjp151216 	char			**dn;		/* DN of entry */
866386Sjp151216 	char			**attr;		/* Attr for name mapping */
876386Sjp151216 	char			**value;	/* value for name mapping */
884520Snw141292 	idmap_retcode		*rc;
898040SBaban.Kenkre@Sun.COM 	adutils_rc		ad_rc;
908040SBaban.Kenkre@Sun.COM 	adutils_result_t	*result;
915447Snw141292 
926531Sjp151216 	/*
936531Sjp151216 	 * The LDAP search entry result is placed here to be processed
946531Sjp151216 	 * when the search done result is received.
956531Sjp151216 	 */
966531Sjp151216 	LDAPMessage		*search_res;	/* The LDAP search result */
974520Snw141292 } idmap_q_t;
984520Snw141292 
994520Snw141292 /* Batch context structure; typedef is in header file */
1004520Snw141292 struct idmap_query_state {
1018040SBaban.Kenkre@Sun.COM 	adutils_query_state_t	*qs;
1028361SJulian.Pullen@Sun.COM 	int			qsize;		/* Queue size */
1038361SJulian.Pullen@Sun.COM 	uint32_t		qcount;		/* Number of queued requests */
1045731Sbaban 	const char		*ad_unixuser_attr;
1055731Sbaban 	const char		*ad_unixgroup_attr;
1064520Snw141292 	idmap_q_t		queries[1];	/* array of query results */
1074520Snw141292 };
1084520Snw141292 
1094644Sbaban static pthread_t	reaperid = 0;
1104520Snw141292 
1114520Snw141292 /*
1124520Snw141292  * Keep connection management simple for now, extend or replace later
1134520Snw141292  * with updated libsldap code.
1144520Snw141292  */
1154520Snw141292 #define	ADREAPERSLEEP	60
1164520Snw141292 
1174520Snw141292 /*
1184520Snw141292  * Idle connection reaping side of connection management
1194520Snw141292  *
1204520Snw141292  * Every minute wake up and look for connections that have been idle for
1214520Snw141292  * five minutes or more and close them.
1224520Snw141292  */
1234520Snw141292 /*ARGSUSED*/
1244520Snw141292 static
1254520Snw141292 void
1264520Snw141292 adreaper(void *arg)
1274520Snw141292 {
1284520Snw141292 	timespec_t	ts;
1294520Snw141292 
1304520Snw141292 	ts.tv_sec = ADREAPERSLEEP;
1314520Snw141292 	ts.tv_nsec = 0;
1324520Snw141292 
1334520Snw141292 	for (;;) {
1344520Snw141292 		/*
1354520Snw141292 		 * nanosleep(3RT) is thead-safe (no SIGALRM) and more
1364520Snw141292 		 * portable than usleep(3C)
1374520Snw141292 		 */
1384520Snw141292 		(void) nanosleep(&ts, NULL);
1398040SBaban.Kenkre@Sun.COM 		adutils_reap_idle_connections();
1404520Snw141292 	}
1414520Snw141292 }
1424520Snw141292 
1434520Snw141292 /*
1444520Snw141292  * Take ad_host_config_t information, create a ad_host_t,
1454520Snw141292  * populate it and add it to the list of hosts.
1464520Snw141292  */
1474520Snw141292 
1484520Snw141292 int
1498040SBaban.Kenkre@Sun.COM idmap_add_ds(adutils_ad_t *ad, const char *host, int port)
1504520Snw141292 {
1518040SBaban.Kenkre@Sun.COM 	int	ret = -1;
1524520Snw141292 
1538040SBaban.Kenkre@Sun.COM 	if (adutils_add_ds(ad, host, port) == ADUTILS_SUCCESS)
1548040SBaban.Kenkre@Sun.COM 		ret = 0;
1554520Snw141292 
1564520Snw141292 	/* Start reaper if it doesn't exist */
1578040SBaban.Kenkre@Sun.COM 	if (ret == 0 && reaperid == 0)
1584520Snw141292 		(void) pthread_create(&reaperid, NULL,
1594520Snw141292 		    (void *(*)(void *))adreaper, (void *)NULL);
1604520Snw141292 	return (ret);
1614520Snw141292 }
1624520Snw141292 
1634520Snw141292 static
1648040SBaban.Kenkre@Sun.COM idmap_retcode
1658040SBaban.Kenkre@Sun.COM map_adrc2idmaprc(adutils_rc adrc)
1664520Snw141292 {
1678040SBaban.Kenkre@Sun.COM 	switch (adrc) {
1688040SBaban.Kenkre@Sun.COM 	case ADUTILS_SUCCESS:
1698040SBaban.Kenkre@Sun.COM 		return (IDMAP_SUCCESS);
1708040SBaban.Kenkre@Sun.COM 	case ADUTILS_ERR_NOTFOUND:
1718040SBaban.Kenkre@Sun.COM 		return (IDMAP_ERR_NOTFOUND);
1728040SBaban.Kenkre@Sun.COM 	case ADUTILS_ERR_MEMORY:
1738040SBaban.Kenkre@Sun.COM 		return (IDMAP_ERR_MEMORY);
1748040SBaban.Kenkre@Sun.COM 	case ADUTILS_ERR_DOMAIN:
1758040SBaban.Kenkre@Sun.COM 		return (IDMAP_ERR_DOMAIN);
1768040SBaban.Kenkre@Sun.COM 	case ADUTILS_ERR_OTHER:
1778040SBaban.Kenkre@Sun.COM 		return (IDMAP_ERR_OTHER);
1788040SBaban.Kenkre@Sun.COM 	case ADUTILS_ERR_RETRIABLE_NET_ERR:
1798040SBaban.Kenkre@Sun.COM 		return (IDMAP_ERR_RETRIABLE_NET_ERR);
1808040SBaban.Kenkre@Sun.COM 	default:
1818040SBaban.Kenkre@Sun.COM 		return (IDMAP_ERR_INTERNAL);
1824520Snw141292 	}
1838040SBaban.Kenkre@Sun.COM 	/* NOTREACHED */
1844520Snw141292 }
1854520Snw141292 
1868040SBaban.Kenkre@Sun.COM idmap_retcode
1878040SBaban.Kenkre@Sun.COM idmap_lookup_batch_start(adutils_ad_t *ad, int nqueries,
1888040SBaban.Kenkre@Sun.COM 	idmap_query_state_t **state)
1894520Snw141292 {
1908040SBaban.Kenkre@Sun.COM 	idmap_query_state_t	*new_state;
1918040SBaban.Kenkre@Sun.COM 	adutils_rc		rc;
1924520Snw141292 
1934520Snw141292 	*state = NULL;
1944520Snw141292 
1958361SJulian.Pullen@Sun.COM 	assert(ad != NULL);
1964520Snw141292 
1974520Snw141292 	new_state = calloc(1, sizeof (idmap_query_state_t) +
1984520Snw141292 	    (nqueries - 1) * sizeof (idmap_q_t));
1994520Snw141292 	if (new_state == NULL)
2004520Snw141292 		return (IDMAP_ERR_MEMORY);
2014520Snw141292 
2028040SBaban.Kenkre@Sun.COM 	if ((rc = adutils_lookup_batch_start(ad, nqueries,
2038040SBaban.Kenkre@Sun.COM 	    idmap_ldap_res_search_cb, new_state, &new_state->qs))
2048040SBaban.Kenkre@Sun.COM 	    != ADUTILS_SUCCESS) {
2058040SBaban.Kenkre@Sun.COM 		free(new_state);
2068040SBaban.Kenkre@Sun.COM 		return (map_adrc2idmaprc(rc));
2078040SBaban.Kenkre@Sun.COM 	}
2084520Snw141292 
2098361SJulian.Pullen@Sun.COM 	new_state->qsize = nqueries;
2104520Snw141292 	*state = new_state;
2114520Snw141292 	return (IDMAP_SUCCESS);
2124520Snw141292 }
2134520Snw141292 
2144520Snw141292 /*
2155731Sbaban  * Set unixuser_attr and unixgroup_attr for AD-based name mapping
2165731Sbaban  */
2175731Sbaban void
2185731Sbaban idmap_lookup_batch_set_unixattr(idmap_query_state_t *state,
2195908Sjp151216 		const char *unixuser_attr, const char *unixgroup_attr)
2205908Sjp151216 {
2215731Sbaban 	state->ad_unixuser_attr = unixuser_attr;
2225731Sbaban 	state->ad_unixgroup_attr = unixgroup_attr;
2235731Sbaban }
2245731Sbaban 
2255731Sbaban /*
2265696Snw141292  * Take parsed attribute values from a search result entry and check if
2275696Snw141292  * it is the result that was desired and, if so, set the result fields
2285696Snw141292  * of the given idmap_q_t.
2295696Snw141292  *
2305731Sbaban  * Frees the unused char * values.
2314520Snw141292  */
2324520Snw141292 static
2335696Snw141292 void
2346386Sjp151216 idmap_setqresults(idmap_q_t *q, char *san, char *dn, const char *attr,
2356386Sjp151216 	char *sid, rid_t rid, int sid_type, char *unixname)
2364520Snw141292 {
2375696Snw141292 	char *domain;
238*10122SJordan.Brown@Sun.COM 	int err1;
2395696Snw141292 
2405696Snw141292 	assert(dn != NULL);
2415696Snw141292 
2428040SBaban.Kenkre@Sun.COM 	if ((domain = adutils_dn2dns(dn)) == NULL)
2435696Snw141292 		goto out;
2445696Snw141292 
2455731Sbaban 	if (q->ecanonname != NULL && san != NULL) {
2465731Sbaban 		/* Check that this is the canonname that we were looking for */
2475696Snw141292 		if (u8_strcmp(q->ecanonname, san, 0,
2485696Snw141292 		    U8_STRCMP_CI_LOWER, /* no normalization, for now */
2495731Sbaban 		    U8_UNICODE_LATEST, &err1) != 0 || err1 != 0)
2505731Sbaban 			goto out;
2515731Sbaban 	}
2525731Sbaban 
2535731Sbaban 	if (q->edomain != NULL) {
2545731Sbaban 		/* Check that this is the domain that we were looking for */
255*10122SJordan.Brown@Sun.COM 		if (!domain_eq(q->edomain, domain))
2565696Snw141292 			goto out;
2575696Snw141292 	}
2585696Snw141292 
2596386Sjp151216 	/* Copy the DN and attr and value */
2606386Sjp151216 	if (q->dn != NULL)
2616386Sjp151216 		*q->dn = strdup(dn);
2626386Sjp151216 
2636386Sjp151216 	if (q->attr != NULL && attr != NULL)
2646386Sjp151216 		*q->attr = strdup(attr);
2656386Sjp151216 
2666386Sjp151216 	if (q->value != NULL && unixname != NULL)
2676386Sjp151216 		*q->value = strdup(unixname);
2686386Sjp151216 
2695731Sbaban 	/* Set results */
2705731Sbaban 	if (q->sid) {
2715731Sbaban 		*q->sid = sid;
2725731Sbaban 		sid = NULL;
2735731Sbaban 	}
2745731Sbaban 	if (q->rid)
2755731Sbaban 		*q->rid = rid;
2765731Sbaban 	if (q->sid_type)
2775731Sbaban 		*q->sid_type = sid_type;
2785731Sbaban 	if (q->unixname) {
2795731Sbaban 		*q->unixname = unixname;
2805731Sbaban 		unixname = NULL;
2815731Sbaban 	}
2825731Sbaban 	if (q->domain != NULL) {
2835731Sbaban 		*q->domain = domain;
2845731Sbaban 		domain = NULL;
2855731Sbaban 	}
2865731Sbaban 	if (q->canonname != NULL) {
2876616Sdm199847 		/*
2886616Sdm199847 		 * The caller may be replacing the given winname by its
2896616Sdm199847 		 * canonical name and therefore free any old name before
2906616Sdm199847 		 * overwriting the field by the canonical name.
2916616Sdm199847 		 */
2926616Sdm199847 		free(*q->canonname);
2935731Sbaban 		*q->canonname = san;
2945731Sbaban 		san = NULL;
2955731Sbaban 	}
2965731Sbaban 
2978040SBaban.Kenkre@Sun.COM 	q->ad_rc = ADUTILS_SUCCESS;
2985696Snw141292 
2995696Snw141292 out:
3005696Snw141292 	/* Free unused attribute values */
3015696Snw141292 	free(san);
3025696Snw141292 	free(sid);
3035696Snw141292 	free(domain);
3045731Sbaban 	free(unixname);
3054520Snw141292 }
3064520Snw141292 
3074520Snw141292 #define	BVAL_CASEEQ(bv, str) \
3084520Snw141292 		(((*(bv))->bv_len == (sizeof (str) - 1)) && \
3094520Snw141292 		    strncasecmp((*(bv))->bv_val, str, (*(bv))->bv_len) == 0)
3104520Snw141292 
3114520Snw141292 /*
3125696Snw141292  * Extract the class of the result entry.  Returns 1 on success, 0 on
3135696Snw141292  * failure.
3144520Snw141292  */
3154520Snw141292 static
3165447Snw141292 int
3175696Snw141292 idmap_bv_objclass2sidtype(BerValue **bvalues, int *sid_type)
3184520Snw141292 {
3194520Snw141292 	BerValue	**cbval;
3204520Snw141292 
3215696Snw141292 	*sid_type = _IDMAP_T_OTHER;
3224520Snw141292 	if (bvalues == NULL)
3235447Snw141292 		return (0);
3244520Snw141292 
3255696Snw141292 	/*
3265696Snw141292 	 * We iterate over all the values because computer is a
3275696Snw141292 	 * sub-class of user.
3285696Snw141292 	 */
3294520Snw141292 	for (cbval = bvalues; *cbval != NULL; cbval++) {
3304520Snw141292 		if (BVAL_CASEEQ(cbval, "Computer")) {
3315696Snw141292 			*sid_type = _IDMAP_T_COMPUTER;
3325696Snw141292 			break;
3334520Snw141292 		} else if (BVAL_CASEEQ(cbval, "Group")) {
3345696Snw141292 			*sid_type = _IDMAP_T_GROUP;
3355696Snw141292 			break;
3364520Snw141292 		} else if (BVAL_CASEEQ(cbval, "USER")) {
3375696Snw141292 			*sid_type = _IDMAP_T_USER;
3385696Snw141292 			/* Continue looping -- this may be a computer yet */
3395696Snw141292 		}
3405696Snw141292 		/*
3415696Snw141292 		 * "else if (*sid_type = _IDMAP_T_USER)" then this is a
3425696Snw141292 		 * new sub-class of user -- what to do with it??
3435696Snw141292 		 */
3444520Snw141292 	}
3455447Snw141292 
3465447Snw141292 	return (1);
3474520Snw141292 }
3484520Snw141292 
3494520Snw141292 /*
3504520Snw141292  * Handle a given search result entry
3514520Snw141292  */
3524520Snw141292 static
3534520Snw141292 void
3548040SBaban.Kenkre@Sun.COM idmap_extract_object(idmap_query_state_t *state, idmap_q_t *q,
3558040SBaban.Kenkre@Sun.COM 	LDAPMessage *res, LDAP *ld)
3564520Snw141292 {
3574520Snw141292 	BerElement		*ber = NULL;
3584520Snw141292 	BerValue		**bvalues;
3595696Snw141292 	char			*attr;
3605731Sbaban 	const char		*unixuser_attr = NULL;
3615731Sbaban 	const char		*unixgroup_attr = NULL;
3625731Sbaban 	char			*unixuser = NULL;
3635731Sbaban 	char			*unixgroup = NULL;
3645696Snw141292 	char			*dn = NULL;
3655696Snw141292 	char			*san = NULL;
3665696Snw141292 	char			*sid = NULL;
3675696Snw141292 	rid_t			rid = 0;
3685731Sbaban 	int			sid_type = _IDMAP_T_UNDEF;
3695447Snw141292 	int			has_class, has_san, has_sid;
3705731Sbaban 	int			has_unixuser, has_unixgroup;
3715447Snw141292 
3725731Sbaban 	assert(q->rc != NULL);
3735731Sbaban 
3748040SBaban.Kenkre@Sun.COM 	if ((dn = ldap_get_dn(ld, res)) == NULL)
3754520Snw141292 		return;
3764520Snw141292 
3775447Snw141292 	assert(q->domain == NULL || *q->domain == NULL);
3784520Snw141292 
3795731Sbaban 	/*
3805731Sbaban 	 * If the caller has requested unixname then determine the
3815731Sbaban 	 * AD attribute name that will have the unixname.
3825731Sbaban 	 */
3835731Sbaban 	if (q->unixname != NULL) {
3845731Sbaban 		if (q->eunixtype == _IDMAP_T_USER)
3855731Sbaban 			unixuser_attr = state->ad_unixuser_attr;
3865731Sbaban 		else if (q->eunixtype == _IDMAP_T_GROUP)
3875731Sbaban 			unixgroup_attr = state->ad_unixgroup_attr;
3885731Sbaban 		else if (q->eunixtype == _IDMAP_T_UNDEF) {
3895731Sbaban 			/*
3905731Sbaban 			 * This is the case where we don't know
3915731Sbaban 			 * before hand whether we need unixuser
3925731Sbaban 			 * or unixgroup. This will be determined
3935731Sbaban 			 * by the "sid_type" (i.e whether the given
3945731Sbaban 			 * winname is user or group). If sid_type
3955731Sbaban 			 * turns out to be user we will return
3965731Sbaban 			 * unixuser (if found) and if it is a group
3975731Sbaban 			 * we will return unixgroup (if found). We
3985731Sbaban 			 * lookup for both ad_unixuser_attr and
3995731Sbaban 			 * ad_unixgroup_attr and discard one of them
4005731Sbaban 			 * after we know the "sidtype". This
4015731Sbaban 			 * supports the following type of lookups.
4025731Sbaban 			 *
4035731Sbaban 			 * Example:
4045731Sbaban 			 *   $idmap show -c winname:foo
4055731Sbaban 			 * In the above example, idmap will
4065731Sbaban 			 * return uid if winname is winuser
4075731Sbaban 			 * and gid if winname is wingroup.
4085731Sbaban 			 */
4095731Sbaban 			unixuser_attr = state->ad_unixuser_attr;
4105731Sbaban 			unixgroup_attr = state->ad_unixgroup_attr;
4115731Sbaban 		}
4125731Sbaban 	}
4135731Sbaban 
4145731Sbaban 	has_class = has_san = has_sid = has_unixuser = has_unixgroup = 0;
4158040SBaban.Kenkre@Sun.COM 	for (attr = ldap_first_attribute(ld, res, &ber); attr != NULL;
4168040SBaban.Kenkre@Sun.COM 	    attr = ldap_next_attribute(ld, res, ber)) {
4174520Snw141292 		bvalues = NULL;	/* for memory management below */
4184520Snw141292 
4194520Snw141292 		/*
4204520Snw141292 		 * If this is an attribute we are looking for and
4214520Snw141292 		 * haven't seen it yet, parse it
4224520Snw141292 		 */
4235731Sbaban 		if (q->sid != NULL && !has_sid &&
4245447Snw141292 		    strcasecmp(attr, OBJSID) == 0) {
4258040SBaban.Kenkre@Sun.COM 			bvalues = ldap_get_values_len(ld, res, attr);
4268040SBaban.Kenkre@Sun.COM 			if (bvalues != NULL) {
4278040SBaban.Kenkre@Sun.COM 				sid = adutils_bv_objsid2sidstr(
4288040SBaban.Kenkre@Sun.COM 				    bvalues[0], &rid);
4298040SBaban.Kenkre@Sun.COM 				has_sid = (sid != NULL);
4308040SBaban.Kenkre@Sun.COM 			}
4315447Snw141292 		} else if (!has_san && strcasecmp(attr, SAN) == 0) {
4328040SBaban.Kenkre@Sun.COM 			bvalues = ldap_get_values_len(ld, res, attr);
4338040SBaban.Kenkre@Sun.COM 			if (bvalues != NULL) {
4348040SBaban.Kenkre@Sun.COM 				san = adutils_bv_name2str(bvalues[0]);
4358040SBaban.Kenkre@Sun.COM 				has_san = (san != NULL);
4368040SBaban.Kenkre@Sun.COM 			}
4375447Snw141292 		} else if (!has_class && strcasecmp(attr, OBJCLASS) == 0) {
4388040SBaban.Kenkre@Sun.COM 			bvalues = ldap_get_values_len(ld, res, attr);
4395696Snw141292 			has_class = idmap_bv_objclass2sidtype(bvalues,
4405696Snw141292 			    &sid_type);
4415731Sbaban 			if (has_class && q->unixname != NULL &&
4425731Sbaban 			    q->eunixtype == _IDMAP_T_UNDEF) {
4435731Sbaban 				/*
4445731Sbaban 				 * This is the case where we didn't
4455731Sbaban 				 * know whether we wanted unixuser or
4465731Sbaban 				 * unixgroup as described above.
4475731Sbaban 				 * Now since we know the "sid_type"
4485731Sbaban 				 * we discard the unwanted value
4495731Sbaban 				 * if it was retrieved before we
4505731Sbaban 				 * got here.
4515731Sbaban 				 */
4525731Sbaban 				if (sid_type == _IDMAP_T_USER) {
4535731Sbaban 					free(unixgroup);
4545731Sbaban 					unixgroup_attr = unixgroup = NULL;
4555731Sbaban 				} else if (sid_type == _IDMAP_T_GROUP) {
4565731Sbaban 					free(unixuser);
4575731Sbaban 					unixuser_attr = unixuser = NULL;
4585731Sbaban 				} else {
4595731Sbaban 					free(unixuser);
4605731Sbaban 					free(unixgroup);
4615731Sbaban 					unixuser_attr = unixuser = NULL;
4625731Sbaban 					unixgroup_attr = unixgroup = NULL;
4635731Sbaban 				}
4645731Sbaban 			}
4655731Sbaban 		} else if (!has_unixuser && unixuser_attr != NULL &&
4665731Sbaban 		    strcasecmp(attr, unixuser_attr) == 0) {
4678040SBaban.Kenkre@Sun.COM 			bvalues = ldap_get_values_len(ld, res, attr);
4688040SBaban.Kenkre@Sun.COM 			if (bvalues != NULL) {
4698040SBaban.Kenkre@Sun.COM 				unixuser = adutils_bv_name2str(bvalues[0]);
4708040SBaban.Kenkre@Sun.COM 				has_unixuser = (unixuser != NULL);
4718040SBaban.Kenkre@Sun.COM 			}
4726386Sjp151216 
4735731Sbaban 		} else if (!has_unixgroup && unixgroup_attr != NULL &&
4745731Sbaban 		    strcasecmp(attr, unixgroup_attr) == 0) {
4758040SBaban.Kenkre@Sun.COM 			bvalues = ldap_get_values_len(ld, res, attr);
4768040SBaban.Kenkre@Sun.COM 			if (bvalues != NULL) {
4778040SBaban.Kenkre@Sun.COM 				unixgroup = adutils_bv_name2str(bvalues[0]);
4788040SBaban.Kenkre@Sun.COM 				has_unixgroup = (unixgroup != NULL);
4798040SBaban.Kenkre@Sun.COM 			}
4804520Snw141292 		}
4814520Snw141292 
4824520Snw141292 		if (bvalues != NULL)
4834520Snw141292 			ldap_value_free_len(bvalues);
4844520Snw141292 		ldap_memfree(attr);
4854520Snw141292 
4865696Snw141292 		if (has_class && has_san &&
4875731Sbaban 		    (q->sid == NULL || has_sid) &&
4885731Sbaban 		    (unixuser_attr == NULL || has_unixuser) &&
4895731Sbaban 		    (unixgroup_attr == NULL || has_unixgroup)) {
4905731Sbaban 			/* Got what we need */
4915447Snw141292 			break;
4925696Snw141292 		}
4934520Snw141292 	}
4944520Snw141292 
4955731Sbaban 	if (!has_class) {
4965731Sbaban 		/*
4975731Sbaban 		 * Didn't find objectclass. Something's wrong with our
4985731Sbaban 		 * AD data.
4995731Sbaban 		 */
5005731Sbaban 		free(san);
5015731Sbaban 		free(sid);
5025731Sbaban 		free(unixuser);
5035731Sbaban 		free(unixgroup);
5045731Sbaban 	} else {
5055731Sbaban 		/*
5065731Sbaban 		 * Either we got what we needed and came out of the loop
5075731Sbaban 		 * early OR we completed the loop in which case we didn't
5085731Sbaban 		 * find some attributes that we were looking for. In either
5095731Sbaban 		 * case set the result with what we got.
5105731Sbaban 		 */
5116386Sjp151216 		idmap_setqresults(q, san, dn,
5126386Sjp151216 		    (unixuser != NULL) ? unixuser_attr : unixgroup_attr,
5136386Sjp151216 		    sid, rid, sid_type,
5145731Sbaban 		    (unixuser != NULL) ? unixuser : unixgroup);
5155731Sbaban 	}
5165731Sbaban 
5174520Snw141292 	if (ber != NULL)
5184520Snw141292 		ber_free(ber, 0);
5194520Snw141292 
5204520Snw141292 	ldap_memfree(dn);
5214520Snw141292 }
5224520Snw141292 
5238040SBaban.Kenkre@Sun.COM void
5248040SBaban.Kenkre@Sun.COM idmap_ldap_res_search_cb(LDAP *ld, LDAPMessage **res, int rc, int qid,
5258040SBaban.Kenkre@Sun.COM 		void *argp)
5264520Snw141292 {
5278040SBaban.Kenkre@Sun.COM 	idmap_query_state_t	*state = (idmap_query_state_t *)argp;
5288040SBaban.Kenkre@Sun.COM 	idmap_q_t		*q = &(state->queries[qid]);
5294520Snw141292 
5304520Snw141292 	switch (rc) {
5314520Snw141292 	case LDAP_RES_SEARCH_RESULT:
5328040SBaban.Kenkre@Sun.COM 		if (q->search_res != NULL) {
5338040SBaban.Kenkre@Sun.COM 			idmap_extract_object(state, q, q->search_res, ld);
5348040SBaban.Kenkre@Sun.COM 			(void) ldap_msgfree(q->search_res);
5358040SBaban.Kenkre@Sun.COM 			q->search_res = NULL;
5368040SBaban.Kenkre@Sun.COM 		} else
5378040SBaban.Kenkre@Sun.COM 			q->ad_rc = ADUTILS_ERR_NOTFOUND;
5384520Snw141292 		break;
5398040SBaban.Kenkre@Sun.COM 	case LDAP_RES_SEARCH_ENTRY:
5408040SBaban.Kenkre@Sun.COM 		if (q->search_res == NULL) {
5418040SBaban.Kenkre@Sun.COM 			q->search_res = *res;
5428040SBaban.Kenkre@Sun.COM 			*res = NULL;
5438040SBaban.Kenkre@Sun.COM 		}
5444520Snw141292 		break;
5454520Snw141292 	default:
5464520Snw141292 		break;
5474520Snw141292 	}
5484884Sjp151216 }
5494884Sjp151216 
5505447Snw141292 static
5515447Snw141292 void
5525447Snw141292 idmap_cleanup_batch(idmap_query_state_t *batch)
5535447Snw141292 {
5545447Snw141292 	int i;
5555447Snw141292 
5565447Snw141292 	for (i = 0; i < batch->qcount; i++) {
5575696Snw141292 		if (batch->queries[i].ecanonname != NULL)
5585696Snw141292 			free(batch->queries[i].ecanonname);
5595696Snw141292 		batch->queries[i].ecanonname = NULL;
5605696Snw141292 		if (batch->queries[i].edomain != NULL)
5615696Snw141292 			free(batch->queries[i].edomain);
5625696Snw141292 		batch->queries[i].edomain = NULL;
5635447Snw141292 	}
5645447Snw141292 }
5655447Snw141292 
5664884Sjp151216 /*
5674884Sjp151216  * This routine frees the idmap_query_state_t structure
5684884Sjp151216  */
5694520Snw141292 void
5704884Sjp151216 idmap_lookup_release_batch(idmap_query_state_t **state)
5714520Snw141292 {
5728040SBaban.Kenkre@Sun.COM 	if (state == NULL || *state == NULL)
5738040SBaban.Kenkre@Sun.COM 		return;
5748040SBaban.Kenkre@Sun.COM 	adutils_lookup_batch_release(&(*state)->qs);
5755447Snw141292 	idmap_cleanup_batch(*state);
5764520Snw141292 	free(*state);
5774520Snw141292 	*state = NULL;
5784520Snw141292 }
5794520Snw141292 
5804520Snw141292 idmap_retcode
5815968Snw141292 idmap_lookup_batch_end(idmap_query_state_t **state)
5824520Snw141292 {
5838040SBaban.Kenkre@Sun.COM 	adutils_rc		ad_rc;
5848040SBaban.Kenkre@Sun.COM 	int			i;
5858040SBaban.Kenkre@Sun.COM 	idmap_query_state_t	*id_qs = *state;
5864520Snw141292 
5878040SBaban.Kenkre@Sun.COM 	ad_rc = adutils_lookup_batch_end(&id_qs->qs);
5884520Snw141292 
5898040SBaban.Kenkre@Sun.COM 	/*
5908040SBaban.Kenkre@Sun.COM 	 * Map adutils rc to idmap_retcode in each
5918040SBaban.Kenkre@Sun.COM 	 * query because consumers in dbutils.c
5928040SBaban.Kenkre@Sun.COM 	 * expects idmap_retcode.
5938040SBaban.Kenkre@Sun.COM 	 */
5948040SBaban.Kenkre@Sun.COM 	for (i = 0; i < id_qs->qcount; i++) {
5958040SBaban.Kenkre@Sun.COM 		*id_qs->queries[i].rc =
5968040SBaban.Kenkre@Sun.COM 		    map_adrc2idmaprc(id_qs->queries[i].ad_rc);
5974520Snw141292 	}
5984884Sjp151216 	idmap_lookup_release_batch(state);
5998040SBaban.Kenkre@Sun.COM 	return (map_adrc2idmaprc(ad_rc));
6004520Snw141292 }
6014520Snw141292 
6024520Snw141292 /*
6034520Snw141292  * Send one prepared search, queue up msgid, process what results are
6044520Snw141292  * available
6054520Snw141292  */
6064520Snw141292 static
6074520Snw141292 idmap_retcode
6086386Sjp151216 idmap_batch_add1(idmap_query_state_t *state, const char *filter,
6096386Sjp151216 	char *ecanonname, char *edomain, int eunixtype,
6106386Sjp151216 	char **dn, char **attr, char **value,
6116386Sjp151216 	char **canonname, char **dname,
6126386Sjp151216 	char **sid, rid_t *rid, int *sid_type, char **unixname,
6136386Sjp151216 	idmap_retcode *rc)
6144520Snw141292 {
6158040SBaban.Kenkre@Sun.COM 	adutils_rc	ad_rc;
6168040SBaban.Kenkre@Sun.COM 	int		qid, i;
6174520Snw141292 	idmap_q_t	*q;
6185696Snw141292 	static char	*attrs[] = {
6195696Snw141292 		SAN,
6205696Snw141292 		OBJSID,
6215696Snw141292 		OBJCLASS,
6225731Sbaban 		NULL,	/* placeholder for unixname attr */
6235731Sbaban 		NULL,	/* placeholder for unixname attr */
6245696Snw141292 		NULL
6255696Snw141292 	};
6264520Snw141292 
6278361SJulian.Pullen@Sun.COM 	qid = atomic_inc_32_nv(&state->qcount) - 1;
6284520Snw141292 	q = &(state->queries[qid]);
6294520Snw141292 
6308361SJulian.Pullen@Sun.COM 	assert(qid < state->qsize);
6318361SJulian.Pullen@Sun.COM 
6325696Snw141292 	/*
6338040SBaban.Kenkre@Sun.COM 	 * Remember the expected canonname, domainname and unix type
6348040SBaban.Kenkre@Sun.COM 	 * so we can check the results * against it
6355696Snw141292 	 */
6365696Snw141292 	q->ecanonname = ecanonname;
6375696Snw141292 	q->edomain = edomain;
6385731Sbaban 	q->eunixtype = eunixtype;
6395447Snw141292 
6404520Snw141292 	/* Remember where to put the results */
6415696Snw141292 	q->canonname = canonname;
6425731Sbaban 	q->sid = sid;
6434520Snw141292 	q->domain = dname;
6444520Snw141292 	q->rid = rid;
6454520Snw141292 	q->sid_type = sid_type;
6464520Snw141292 	q->rc = rc;
6475731Sbaban 	q->unixname = unixname;
6486386Sjp151216 	q->dn = dn;
6496386Sjp151216 	q->attr = attr;
6506386Sjp151216 	q->value = value;
6515731Sbaban 
6525731Sbaban 	/* Add unixuser/unixgroup attribute names to the attrs list */
6535731Sbaban 	if (unixname != NULL) {
6545731Sbaban 		i = 3;
6555731Sbaban 		if (eunixtype != _IDMAP_T_GROUP &&
6565731Sbaban 		    state->ad_unixuser_attr != NULL)
6575731Sbaban 			attrs[i++] = (char *)state->ad_unixuser_attr;
6585731Sbaban 		if (eunixtype != _IDMAP_T_USER &&
6595731Sbaban 		    state->ad_unixgroup_attr != NULL)
6605731Sbaban 			attrs[i] = (char *)state->ad_unixgroup_attr;
6615731Sbaban 	}
6624520Snw141292 
6634520Snw141292 	/*
6644520Snw141292 	 * Provide sane defaults for the results in case we never hear
6654520Snw141292 	 * back from the DS before closing the connection.
6665447Snw141292 	 *
6675447Snw141292 	 * In particular we default the result to indicate a retriable
6685447Snw141292 	 * error.  The first complete matching result entry will cause
6695447Snw141292 	 * this to be set to IDMAP_SUCCESS, and the end of the results
6705447Snw141292 	 * for this search will cause this to indicate "not found" if no
6715447Snw141292 	 * result entries arrived or no complete ones matched the lookup
6725447Snw141292 	 * we were doing.
6734520Snw141292 	 */
6744520Snw141292 	*rc = IDMAP_ERR_RETRIABLE_NET_ERR;
6755731Sbaban 	if (sid_type != NULL)
6765731Sbaban 		*sid_type = _IDMAP_T_OTHER;
6775731Sbaban 	if (sid != NULL)
6785731Sbaban 		*sid = NULL;
6794520Snw141292 	if (dname != NULL)
6804520Snw141292 		*dname = NULL;
6814520Snw141292 	if (rid != NULL)
6824520Snw141292 		*rid = 0;
6836386Sjp151216 	if (dn != NULL)
6846386Sjp151216 		*dn = NULL;
6856386Sjp151216 	if (attr != NULL)
6866386Sjp151216 		*attr = NULL;
6876386Sjp151216 	if (value != NULL)
6886386Sjp151216 		*value = NULL;
6894520Snw141292 
6906616Sdm199847 	/*
6916616Sdm199847 	 * Don't set *canonname to NULL because it may be pointing to the
6926616Sdm199847 	 * given winname. Later on if we get a canonical name from AD the
6936616Sdm199847 	 * old name if any will be freed before assigning the new name.
6946616Sdm199847 	 */
6956616Sdm199847 
6964520Snw141292 	/*
6978040SBaban.Kenkre@Sun.COM 	 * Invoke the mother of all APIs i.e. the adutils API
6984520Snw141292 	 */
6998040SBaban.Kenkre@Sun.COM 	ad_rc = adutils_lookup_batch_add(state->qs, filter,
7008040SBaban.Kenkre@Sun.COM 	    (const char **)attrs,
7018040SBaban.Kenkre@Sun.COM 	    edomain, &q->result, &q->ad_rc);
7028040SBaban.Kenkre@Sun.COM 	return (map_adrc2idmaprc(ad_rc));
7034520Snw141292 }
7044520Snw141292 
7054520Snw141292 idmap_retcode
7064520Snw141292 idmap_name2sid_batch_add1(idmap_query_state_t *state,
7075731Sbaban 	const char *name, const char *dname, int eunixtype,
7086386Sjp151216 	char **dn, char **attr, char **value,
7096386Sjp151216 	char **canonname, char **sid, rid_t *rid,
7106386Sjp151216 	int *sid_type, char **unixname, idmap_retcode *rc)
7114520Snw141292 {
7124520Snw141292 	idmap_retcode	retcode;
7135447Snw141292 	int		len, samAcctNameLen;
7146616Sdm199847 	char		*filter = NULL, *s_name;
7155696Snw141292 	char		*ecanonname, *edomain; /* expected canonname */
7164520Snw141292 
7174520Snw141292 	/*
7185447Snw141292 	 * Strategy: search the global catalog for user/group by
7195447Snw141292 	 * sAMAccountName = user/groupname with "" as the base DN and by
7205447Snw141292 	 * userPrincipalName = user/groupname@domain.  The result
7215447Snw141292 	 * entries will be checked to conform to the name and domain
7225447Snw141292 	 * name given here.  The DN, sAMAccountName, userPrincipalName,
7235447Snw141292 	 * objectSid and objectClass of the result entries are all we
7245447Snw141292 	 * need to figure out which entries match the lookup, the SID of
7255447Snw141292 	 * the user/group and whether it is a user or a group.
7264520Snw141292 	 */
7274520Snw141292 
7284520Snw141292 	/*
7295447Snw141292 	 * We need the name and the domain name separately and as
7305447Snw141292 	 * name@domain.  We also allow the domain to be provided
7315447Snw141292 	 * separately.
7324520Snw141292 	 */
7335232Snw141292 	samAcctNameLen = strlen(name);
7345447Snw141292 
7355696Snw141292 	if ((ecanonname = strdup(name)) == NULL)
7365696Snw141292 		return (IDMAP_ERR_MEMORY);
7374520Snw141292 
7385447Snw141292 	if (dname == NULL || *dname == '\0') {
7395696Snw141292 		if ((dname = strchr(name, '@')) != NULL) {
7405696Snw141292 			/* 'name' is qualified with a domain name */
7415696Snw141292 			if ((edomain = strdup(dname + 1)) == NULL) {
7425696Snw141292 				free(ecanonname);
7435696Snw141292 				return (IDMAP_ERR_MEMORY);
7445696Snw141292 			}
7455696Snw141292 			*strchr(ecanonname, '@') = '\0';
7465696Snw141292 		} else {
7478040SBaban.Kenkre@Sun.COM 			/* 'name' not qualified and dname not given */
7488361SJulian.Pullen@Sun.COM 			dname = adutils_lookup_batch_getdefdomain(state->qs);
7498040SBaban.Kenkre@Sun.COM 			assert(dname != NULL);
7508040SBaban.Kenkre@Sun.COM 			if (*dname == '\0') {
7515696Snw141292 				free(ecanonname);
7525696Snw141292 				return (IDMAP_ERR_DOMAIN);
7535696Snw141292 			}
7548040SBaban.Kenkre@Sun.COM 			edomain = strdup(dname);
7555696Snw141292 			if (edomain == NULL) {
7565696Snw141292 				free(ecanonname);
7575696Snw141292 				return (IDMAP_ERR_MEMORY);
7585696Snw141292 			}
7595696Snw141292 		}
7605696Snw141292 	} else {
7615696Snw141292 		if ((edomain = strdup(dname)) == NULL) {
7625696Snw141292 			free(ecanonname);
7635447Snw141292 			return (IDMAP_ERR_MEMORY);
7645696Snw141292 		}
7655447Snw141292 	}
7664520Snw141292 
7678361SJulian.Pullen@Sun.COM 	if (!adutils_lookup_check_domain(state->qs, dname)) {
7688361SJulian.Pullen@Sun.COM 		free(ecanonname);
7698361SJulian.Pullen@Sun.COM 		free(edomain);
7708361SJulian.Pullen@Sun.COM 		return (IDMAP_ERR_DOMAIN_NOTFOUND);
7718361SJulian.Pullen@Sun.COM 	}
7728361SJulian.Pullen@Sun.COM 
7736616Sdm199847 	s_name = sanitize_for_ldap_filter(name);
7746616Sdm199847 	if (s_name == NULL) {
7756616Sdm199847 		free(ecanonname);
7766616Sdm199847 		free(edomain);
7776616Sdm199847 		return (IDMAP_ERR_MEMORY);
7786616Sdm199847 	}
7796616Sdm199847 
7804520Snw141292 	/* Assemble filter */
7816616Sdm199847 	len = snprintf(NULL, 0, SANFILTER, samAcctNameLen, s_name) + 1;
7825447Snw141292 	if ((filter = (char *)malloc(len)) == NULL) {
7835696Snw141292 		free(ecanonname);
7846616Sdm199847 		free(edomain);
7856616Sdm199847 		if (s_name != name)
7866616Sdm199847 			free(s_name);
7874520Snw141292 		return (IDMAP_ERR_MEMORY);
7884520Snw141292 	}
7896616Sdm199847 	(void) snprintf(filter, len, SANFILTER, samAcctNameLen, s_name);
7906616Sdm199847 	if (s_name != name)
7916616Sdm199847 		free(s_name);
7924520Snw141292 
7935696Snw141292 	retcode = idmap_batch_add1(state, filter, ecanonname, edomain,
7946386Sjp151216 	    eunixtype, dn, attr, value, canonname, NULL, sid, rid, sid_type,
7956386Sjp151216 	    unixname, rc);
7964520Snw141292 
7974520Snw141292 	free(filter);
7984520Snw141292 
7994520Snw141292 	return (retcode);
8004520Snw141292 }
8014520Snw141292 
8024520Snw141292 idmap_retcode
8034520Snw141292 idmap_sid2name_batch_add1(idmap_query_state_t *state,
8045731Sbaban 	const char *sid, const rid_t *rid, int eunixtype,
8056386Sjp151216 	char **dn, char **attr, char **value,
8066386Sjp151216 	char **name, char **dname, int *sid_type,
8076386Sjp151216 	char **unixname, idmap_retcode *rc)
8084520Snw141292 {
8094520Snw141292 	idmap_retcode	retcode;
8104520Snw141292 	int		flen, ret;
8114520Snw141292 	char		*filter = NULL;
8128040SBaban.Kenkre@Sun.COM 	char		cbinsid[ADUTILS_MAXHEXBINSID + 1];
8134520Snw141292 
8144520Snw141292 	/*
8154520Snw141292 	 * Strategy: search [the global catalog] for user/group by
8164520Snw141292 	 * objectSid = SID with empty base DN.  The DN, sAMAccountName
8174520Snw141292 	 * and objectClass of the result are all we need to figure out
8184520Snw141292 	 * the name of the SID and whether it is a user, a group or a
8194520Snw141292 	 * computer.
8204520Snw141292 	 */
8214520Snw141292 
8228361SJulian.Pullen@Sun.COM 	if (!adutils_lookup_check_sid_prefix(state->qs, sid))
8238361SJulian.Pullen@Sun.COM 		return (IDMAP_ERR_DOMAIN_NOTFOUND);
8248361SJulian.Pullen@Sun.COM 
8258040SBaban.Kenkre@Sun.COM 	ret = adutils_txtsid2hexbinsid(sid, rid, &cbinsid[0], sizeof (cbinsid));
8264520Snw141292 	if (ret != 0)
8274520Snw141292 		return (IDMAP_ERR_SID);
8284520Snw141292 
8294520Snw141292 	/* Assemble filter */
8305447Snw141292 	flen = snprintf(NULL, 0, OBJSIDFILTER, cbinsid) + 1;
8314520Snw141292 	if ((filter = (char *)malloc(flen)) == NULL)
8324520Snw141292 		return (IDMAP_ERR_MEMORY);
8335447Snw141292 	(void) snprintf(filter, flen, OBJSIDFILTER, cbinsid);
8344520Snw141292 
8355731Sbaban 	retcode = idmap_batch_add1(state, filter, NULL, NULL, eunixtype,
8366386Sjp151216 	    dn, attr, value, name, dname, NULL, NULL, sid_type, unixname, rc);
8374520Snw141292 
8384520Snw141292 	free(filter);
8394520Snw141292 
8404520Snw141292 	return (retcode);
8414520Snw141292 }
8425731Sbaban 
8435731Sbaban idmap_retcode
8445731Sbaban idmap_unixname2sid_batch_add1(idmap_query_state_t *state,
8455731Sbaban 	const char *unixname, int is_user, int is_wuser,
8466386Sjp151216 	char **dn, char **attr, char **value,
8476386Sjp151216 	char **sid, rid_t *rid, char **name,
8486386Sjp151216 	char **dname, int *sid_type, idmap_retcode *rc)
8495731Sbaban {
8505731Sbaban 	idmap_retcode	retcode;
8515731Sbaban 	int		len, ulen;
8526616Sdm199847 	char		*filter = NULL, *s_unixname;
8535731Sbaban 	const char	*attrname = NULL;
8545731Sbaban 
8555731Sbaban 	/* Get unixuser or unixgroup AD attribute name */
8565731Sbaban 	attrname = (is_user) ?
8575731Sbaban 	    state->ad_unixuser_attr : state->ad_unixgroup_attr;
8585731Sbaban 	if (attrname == NULL)
8595731Sbaban 		return (IDMAP_ERR_NOTFOUND);
8605731Sbaban 
8616616Sdm199847 	s_unixname = sanitize_for_ldap_filter(unixname);
8626616Sdm199847 	if (s_unixname == NULL)
8636616Sdm199847 		return (IDMAP_ERR_MEMORY);
8646616Sdm199847 
8655731Sbaban 	/*  Assemble filter */
8665731Sbaban 	ulen = strlen(unixname);
8675731Sbaban 	len = snprintf(NULL, 0, "(&(objectclass=%s)(%s=%.*s))",
8686616Sdm199847 	    is_wuser ? "user" : "group", attrname, ulen, s_unixname) + 1;
8696616Sdm199847 	if ((filter = (char *)malloc(len)) == NULL) {
8706616Sdm199847 		if (s_unixname != unixname)
8716616Sdm199847 			free(s_unixname);
8725731Sbaban 		return (IDMAP_ERR_MEMORY);
8736616Sdm199847 	}
8745731Sbaban 	(void) snprintf(filter, len, "(&(objectclass=%s)(%s=%.*s))",
8756616Sdm199847 	    is_wuser ? "user" : "group", attrname, ulen, s_unixname);
8766616Sdm199847 	if (s_unixname != unixname)
8776616Sdm199847 		free(s_unixname);
8785731Sbaban 
8795731Sbaban 	retcode = idmap_batch_add1(state, filter, NULL, NULL,
8806386Sjp151216 	    _IDMAP_T_UNDEF, dn, NULL, NULL, name, dname, sid, rid, sid_type,
8816386Sjp151216 	    NULL, rc);
8826386Sjp151216 
8836386Sjp151216 	if (retcode == IDMAP_SUCCESS && attr != NULL) {
8846386Sjp151216 		if ((*attr = strdup(attrname)) == NULL)
8856386Sjp151216 			retcode = IDMAP_ERR_MEMORY;
8866386Sjp151216 	}
8876386Sjp151216 
8886386Sjp151216 	if (retcode == IDMAP_SUCCESS && value != NULL) {
8896386Sjp151216 		if (ulen > 0) {
8906386Sjp151216 			if ((*value = strdup(unixname)) == NULL)
8916386Sjp151216 				retcode = IDMAP_ERR_MEMORY;
8926386Sjp151216 		}
8936386Sjp151216 		else
8946386Sjp151216 			*value = NULL;
8956386Sjp151216 	}
8965731Sbaban 
8975731Sbaban 	free(filter);
8985731Sbaban 
8995731Sbaban 	return (retcode);
9005731Sbaban }
901