Home | History | Annotate | Download | only in idmapd
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * native LDAP related utility routines
     29  */
     30 
     31 #include "idmapd.h"
     32 #include "idmap_priv.h"
     33 #include "ns_sldap.h"
     34 #include "nldaputils.h"
     35 #include <assert.h>
     36 
     37 /*
     38  * The following are format strings used to construct LDAP search filters
     39  * when looking up Native LDAP directory service. The _F_XXX_SSD format
     40  * is used by the libsldap API if a corresponding SSD is defined in
     41  * Native LDAP configuration. The SSD contains a string that replaces
     42  * the first %s in _F_XXX_SSD. If no SSD is defined then the regular
     43  * _F_XXX format is used.
     44  *
     45  * Note that '\\' needs to be represented as "\\5c" in LDAP filters.
     46  */
     47 
     48 /* Native LDAP lookup using UNIX username */
     49 #define	_F_GETPWNAM		"(&(objectClass=posixAccount)(uid=%s))"
     50 #define	_F_GETPWNAM_SSD		"(&(%%s)(uid=%s))"
     51 
     52 /*
     53  * Native LDAP user lookup using names of well-known SIDs
     54  * Note the use of 1$, 2$ in the format string which basically
     55  * allows snprintf to re-use its first two arguments.
     56  */
     57 #define	_F_GETPWWNAMWK \
     58 		"(&(objectClass=posixAccount)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
     59 #define	_F_GETPWWNAMWK_SSD	"(&(%%s)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
     60 
     61 /* Native LDAP user lookup using winname@windomain OR windomain\winname */
     62 #define	_F_GETPWWNAMDOM \
     63 	"(&(objectClass=posixAccount)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
     64 #define	_F_GETPWWNAMDOM_SSD	"(&(%%s)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
     65 
     66 /* Native LDAP lookup using UID */
     67 #define	_F_GETPWUID		"(&(objectClass=posixAccount)(uidNumber=%u))"
     68 #define	_F_GETPWUID_SSD		"(&(%%s)(uidNumber=%u))"
     69 
     70 /* Native LDAP lookup using UNIX groupname */
     71 #define	_F_GETGRNAM		"(&(objectClass=posixGroup)(cn=%s))"
     72 #define	_F_GETGRNAM_SSD		"(&(%%s)(cn=%s))"
     73 
     74 /* Native LDAP group lookup using names of well-known SIDs */
     75 #define	_F_GETGRWNAMWK \
     76 		"(&(objectClass=posixGroup)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
     77 #define	_F_GETGRWNAMWK_SSD	"(&(%%s)(|(%s=%s)(%1$s=BUILTIN\\5c%2$s)))"
     78 
     79 /* Native LDAP group lookup using winname@windomain OR windomain\winname */
     80 #define	_F_GETGRWNAMDOM \
     81 		"(&(objectClass=posixGroup)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
     82 #define	_F_GETGRWNAMDOM_SSD	"(&(%%s)(|(%s=%s@%s)(%1$s=%3$s\\5c%2$s)))"
     83 
     84 /* Native LDAP lookup using GID */
     85 #define	_F_GETGRGID		"(&(objectClass=posixGroup)(gidNumber=%u))"
     86 #define	_F_GETGRGID_SSD		"(&(%%s)(gidNumber=%u))"
     87 
     88 /* Native LDAP attribute names */
     89 #define	UID			"uid"
     90 #define	CN			"cn"
     91 #define	UIDNUMBER		"uidnumber"
     92 #define	GIDNUMBER		"gidnumber"
     93 #define	DN			"dn"
     94 
     95 #define	IS_NLDAP_RC_FATAL(x)	((x == NS_LDAP_MEMORY) ? 1 : 0)
     96 
     97 typedef struct idmap_nldap_q {
     98 	char			**winname;
     99 	char			**windomain;
    100 	char			**unixname;
    101 	uid_t			*pid;
    102 	char			**dn;
    103 	char			**attr;
    104 	char			**value;
    105 	int			is_user;
    106 	idmap_retcode		*rc;
    107 	int			lrc;
    108 	ns_ldap_result_t	*result;
    109 	ns_ldap_error_t		*errorp;
    110 	char			*filter;
    111 	char			*udata;
    112 } idmap_nldap_q_t;
    113 
    114 typedef struct idmap_nldap_query_state {
    115 	const char		*nldap_winname_attr;
    116 	const char		*defdom;
    117 	int			nqueries;
    118 	int			qid;
    119 	int			flag;
    120 	ns_ldap_list_batch_t	*batch;
    121 	idmap_nldap_q_t		queries[1];
    122 } idmap_nldap_query_state_t;
    123 
    124 /*
    125  * This routine has been copied from lib/nsswitch/ldap/common/ldap_utils.c
    126  * after removing the debug statements.
    127  *
    128  * This is a generic filter callback function for merging the filter
    129  * from service search descriptor with an existing search filter. This
    130  * routine expects userdata to contain a format string with a single %s
    131  * in it, and will use the format string with sprintf() to insert the
    132  * SSD filter.
    133  *
    134  * This routine and userdata are passed to the __ns_ldap_list_batch_add()
    135  * API.
    136  *
    137  * Consider an example that uses __ns_ldap_list_batch_add() to lookup
    138  * native LDAP directory using a given userid 'xy12345'. In this
    139  * example the userdata will contain the filter "(&(%s)(cn=xy1234))".
    140  * If a SSD is defined to replace the rfc2307bis specified filter
    141  * i.e. (objectClass=posixAccount) by a site-specific filter
    142  * say (department=sds) then this routine when called will produce
    143  * "(&(department=sds)(uid=xy1234))" as the real search filter.
    144  */
    145 static
    146 int
    147 merge_SSD_filter(const ns_ldap_search_desc_t *desc,
    148 	char **realfilter, const void *userdata)
    149 {
    150 	int	len;
    151 	if (realfilter == NULL)
    152 		return (NS_LDAP_INVALID_PARAM);
    153 	*realfilter = NULL;
    154 	if (desc == NULL || desc->filter == NULL || userdata == NULL)
    155 		return (NS_LDAP_INVALID_PARAM);
    156 	len = strlen(userdata) + strlen(desc->filter) + 1;
    157 	*realfilter = (char *)malloc(len);
    158 	if (*realfilter == NULL)
    159 		return (NS_LDAP_MEMORY);
    160 	(void) sprintf(*realfilter, (char *)userdata, desc->filter);
    161 	return (NS_LDAP_SUCCESS);
    162 }
    163 
    164 static
    165 char
    166 hex_char(int n)
    167 {
    168 	return ("0123456789abcdef"[n & 0xf]);
    169 }
    170 
    171 /*
    172  * If the input string contains special characters that needs to be
    173  * escaped before the string can be used in a LDAP filter then this
    174  * function will return a new sanitized string. Otherwise this function
    175  * returns the input string (This saves us un-necessary memory allocations
    176  * especially when processing a batch of requests). The caller must free
    177  * the returned string if it isn't the input string.
    178  *
    179  * The escape mechanism for LDAP filter is described in RFC2254 basically
    180  * it's \hh where hh are the two hexadecimal digits representing the ASCII
    181  * value of the encoded character (case of hh is not significant).
    182  * Example: * -> \2a, ( -> \28, ) -> \29, \ -> \5c,
    183  *
    184  * outstring = sanitize_for_ldap_filter(instring);
    185  * if (outstring == NULL)
    186  *	Out of memory
    187  * else
    188  *	Use outstring
    189  *	if (outstring != instring)
    190  *		free(outstring);
    191  * done
    192  */
    193 char *
    194 sanitize_for_ldap_filter(const char *str)
    195 {
    196 	const char	*p;
    197 	char		*q, *s_str = NULL;
    198 	int		n;
    199 
    200 	/* Get a count of special characters */
    201 	for (p = str, n = 0; *p; p++)
    202 		if (*p == '*' || *p == '(' || *p == ')' ||
    203 		    *p == '\\' || *p == '%')
    204 			n++;
    205 	/* If count is zero then no need to sanitize */
    206 	if (n == 0)
    207 		return ((char *)str);
    208 	/* Create output buffer that will contain the sanitized value */
    209 	s_str = calloc(1, n * 2 + strlen(str) + 1);
    210 	if (s_str == NULL)
    211 		return (NULL);
    212 	for (p = str, q = s_str; *p; p++) {
    213 		if (*p == '*' || *p == '(' || *p == ')' ||
    214 		    *p == '\\' || *p == '%') {
    215 			*q++ = '\\';
    216 			*q++ = hex_char(*p >> 4);
    217 			*q++ = hex_char(*p & 0xf);
    218 		} else
    219 			*q++ = *p;
    220 	}
    221 	return (s_str);
    222 }
    223 
    224 /*
    225  * Map libsldap status to idmap  status
    226  */
    227 static
    228 idmap_retcode
    229 nldaprc2retcode(int rc)
    230 {
    231 	switch (rc) {
    232 	case NS_LDAP_SUCCESS:
    233 	case NS_LDAP_SUCCESS_WITH_INFO:
    234 		return (IDMAP_SUCCESS);
    235 	case NS_LDAP_NOTFOUND:
    236 		return (IDMAP_ERR_NOTFOUND);
    237 	case NS_LDAP_MEMORY:
    238 		return (IDMAP_ERR_MEMORY);
    239 	case NS_LDAP_CONFIG:
    240 		return (IDMAP_ERR_NS_LDAP_CFG);
    241 	case NS_LDAP_OP_FAILED:
    242 		return (IDMAP_ERR_NS_LDAP_OP_FAILED);
    243 	case NS_LDAP_PARTIAL:
    244 		return (IDMAP_ERR_NS_LDAP_PARTIAL);
    245 	case NS_LDAP_INTERNAL:
    246 		return (IDMAP_ERR_INTERNAL);
    247 	case NS_LDAP_INVALID_PARAM:
    248 		return (IDMAP_ERR_ARG);
    249 	default:
    250 		return (IDMAP_ERR_OTHER);
    251 	}
    252 	/*NOTREACHED*/
    253 }
    254 
    255 /*
    256  * Create a batch for native LDAP lookup.
    257  */
    258 static
    259 idmap_retcode
    260 idmap_nldap_lookup_batch_start(int nqueries, idmap_nldap_query_state_t **qs)
    261 {
    262 	idmap_nldap_query_state_t	*s;
    263 
    264 	s = calloc(1, sizeof (*s) +
    265 	    (nqueries - 1) * sizeof (idmap_nldap_q_t));
    266 	if (s == NULL)
    267 		return (IDMAP_ERR_MEMORY);
    268 	if (__ns_ldap_list_batch_start(&s->batch) != NS_LDAP_SUCCESS) {
    269 		free(s);
    270 		return (IDMAP_ERR_MEMORY);
    271 	}
    272 	s->nqueries = nqueries;
    273 	s->flag = NS_LDAP_KEEP_CONN;
    274 	*qs = s;
    275 	return (IDMAP_SUCCESS);
    276 }
    277 
    278 /*
    279  * Add a lookup by winname request to the batch.
    280  */
    281 static
    282 idmap_retcode
    283 idmap_nldap_bywinname_batch_add(idmap_nldap_query_state_t *qs,
    284 	const char *winname, const char *windomain, int is_user,
    285 	char **dn, char **attr, char **value,
    286 	char **unixname, uid_t *pid, idmap_retcode *rc)
    287 {
    288 	idmap_nldap_q_t		*q;
    289 	const char		*db, *filter, *udata;
    290 	int			flen, ulen, wksid = 0;
    291 	char			*s_winname, *s_windomain;
    292 	const char		**attrs;
    293 	const char		*pwd_attrs[] = {UID, UIDNUMBER, NULL, NULL};
    294 	const char		*grp_attrs[] = {CN, GIDNUMBER, NULL, NULL};
    295 
    296 	s_winname = s_windomain = NULL;
    297 	q = &(qs->queries[qs->qid++]);
    298 	q->unixname = unixname;
    299 	q->pid = pid;
    300 	q->rc = rc;
    301 	q->is_user = is_user;
    302 	q->dn = dn;
    303 	q->attr = attr;
    304 	q->value = value;
    305 
    306 	if (is_user) {
    307 		db = "passwd";
    308 		if (lookup_wksids_name2sid(winname, NULL, NULL, NULL, NULL,
    309 		    NULL, NULL) == IDMAP_SUCCESS) {
    310 			filter = _F_GETPWWNAMWK;
    311 			udata = _F_GETPWWNAMWK_SSD;
    312 			wksid = 1;
    313 		} else if (windomain != NULL) {
    314 			filter = _F_GETPWWNAMDOM;
    315 			udata = _F_GETPWWNAMDOM_SSD;
    316 		} else {
    317 			*q->rc = IDMAP_ERR_DOMAIN_NOTFOUND;
    318 			goto errout;
    319 		}
    320 		pwd_attrs[2] = qs->nldap_winname_attr;
    321 		attrs = pwd_attrs;
    322 	} else {
    323 		db = "group";
    324 		if (lookup_wksids_name2sid(winname, NULL, NULL, NULL, NULL,
    325 		    NULL, NULL) == IDMAP_SUCCESS) {
    326 			filter = _F_GETGRWNAMWK;
    327 			udata = _F_GETGRWNAMWK_SSD;
    328 			wksid = 1;
    329 		} else if (windomain != NULL) {
    330 			filter = _F_GETGRWNAMDOM;
    331 			udata = _F_GETGRWNAMDOM_SSD;
    332 		} else {
    333 			*q->rc = IDMAP_ERR_DOMAIN_NOTFOUND;
    334 			goto errout;
    335 		}
    336 		grp_attrs[2] = qs->nldap_winname_attr;
    337 		attrs = grp_attrs;
    338 	}
    339 
    340 	/*
    341 	 * Sanitize names. No need to sanitize qs->nldap_winname_attr
    342 	 * because if it contained any of the special characters then
    343 	 * it would have been rejected by the function that reads it
    344 	 * from the SMF config. LDAP attribute names can only contain
    345 	 * letters, digits or hyphens.
    346 	 */
    347 	s_winname = sanitize_for_ldap_filter(winname);
    348 	if (s_winname == NULL) {
    349 		*q->rc = IDMAP_ERR_MEMORY;
    350 		goto errout;
    351 	}
    352 	/* windomain could be NULL for names of well-known SIDs */
    353 	if (windomain != NULL) {
    354 		s_windomain = sanitize_for_ldap_filter(windomain);
    355 		if (s_windomain == NULL) {
    356 			*q->rc = IDMAP_ERR_MEMORY;
    357 			goto errout;
    358 		}
    359 	}
    360 
    361 	/* Construct the filter and udata using snprintf. */
    362 	if (wksid) {
    363 		flen = snprintf(NULL, 0, filter, qs->nldap_winname_attr,
    364 		    s_winname) + 1;
    365 		ulen = snprintf(NULL, 0, udata, qs->nldap_winname_attr,
    366 		    s_winname) + 1;
    367 	} else {
    368 		flen = snprintf(NULL, 0, filter, qs->nldap_winname_attr,
    369 		    s_winname, s_windomain) + 1;
    370 		ulen = snprintf(NULL, 0, udata, qs->nldap_winname_attr,
    371 		    s_winname, s_windomain) + 1;
    372 	}
    373 
    374 	q->filter = malloc(flen);
    375 	if (q->filter == NULL) {
    376 		*q->rc = IDMAP_ERR_MEMORY;
    377 		goto errout;
    378 	}
    379 	q->udata = malloc(ulen);
    380 	if (q->udata == NULL) {
    381 		*q->rc = IDMAP_ERR_MEMORY;
    382 		goto errout;
    383 	}
    384 
    385 	if (wksid) {
    386 		(void) snprintf(q->filter, flen, filter,
    387 		    qs->nldap_winname_attr, s_winname);
    388 		(void) snprintf(q->udata, ulen, udata,
    389 		    qs->nldap_winname_attr, s_winname);
    390 	} else {
    391 		(void) snprintf(q->filter, flen, filter,
    392 		    qs->nldap_winname_attr, s_winname, s_windomain);
    393 		(void) snprintf(q->udata, ulen, udata,
    394 		    qs->nldap_winname_attr, s_winname, s_windomain);
    395 	}
    396 
    397 	if (s_winname != winname)
    398 		free(s_winname);
    399 	if (s_windomain != windomain)
    400 		free(s_windomain);
    401 
    402 	q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
    403 	    merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
    404 	    &q->errorp, &q->lrc, NULL, q->udata);
    405 
    406 	if (IS_NLDAP_RC_FATAL(q->lrc))
    407 		return (nldaprc2retcode(q->lrc));
    408 	return (IDMAP_SUCCESS);
    409 
    410 errout:
    411 	/* query q and its content will be freed by batch_release */
    412 	if (s_winname != winname)
    413 		free(s_winname);
    414 	if (s_windomain != windomain)
    415 		free(s_windomain);
    416 	return (*q->rc);
    417 }
    418 
    419 /*
    420  * Add a lookup by uid/gid request to the batch.
    421  */
    422 static
    423 idmap_retcode
    424 idmap_nldap_bypid_batch_add(idmap_nldap_query_state_t *qs,
    425 	uid_t pid, int is_user, char **dn, char **attr, char **value,
    426 	char **winname, char **windomain,
    427 	char **unixname, idmap_retcode *rc)
    428 {
    429 	idmap_nldap_q_t		*q;
    430 	const char		*db, *filter, *udata;
    431 	int			len;
    432 	const char		**attrs;
    433 	const char		*pwd_attrs[] = {UID, NULL, NULL};
    434 	const char		*grp_attrs[] = {CN, NULL, NULL};
    435 
    436 	q = &(qs->queries[qs->qid++]);
    437 	q->winname = winname;
    438 	q->windomain = windomain;
    439 	q->unixname = unixname;
    440 	q->rc = rc;
    441 	q->is_user = is_user;
    442 	q->dn = dn;
    443 	q->attr = attr;
    444 	q->value = value;
    445 
    446 	if (is_user) {
    447 		db = "passwd";
    448 		filter = _F_GETPWUID;
    449 		udata = _F_GETPWUID_SSD;
    450 		pwd_attrs[1] = qs->nldap_winname_attr;
    451 		attrs = pwd_attrs;
    452 	} else {
    453 		db = "group";
    454 		filter = _F_GETGRGID;
    455 		udata = _F_GETGRGID_SSD;
    456 		grp_attrs[1] = qs->nldap_winname_attr;
    457 		attrs = grp_attrs;
    458 	}
    459 
    460 	len = snprintf(NULL, 0, filter, pid) + 1;
    461 	q->filter = malloc(len);
    462 	if (q->filter == NULL) {
    463 		*q->rc = IDMAP_ERR_MEMORY;
    464 		return (IDMAP_ERR_MEMORY);
    465 	}
    466 	(void) snprintf(q->filter, len, filter, pid);
    467 
    468 	len = snprintf(NULL, 0, udata, pid) + 1;
    469 	q->udata = malloc(len);
    470 	if (q->udata == NULL) {
    471 		*q->rc = IDMAP_ERR_MEMORY;
    472 		return (IDMAP_ERR_MEMORY);
    473 	}
    474 	(void) snprintf(q->udata, len, udata, pid);
    475 
    476 	q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
    477 	    merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
    478 	    &q->errorp, &q->lrc, NULL, q->udata);
    479 
    480 	if (IS_NLDAP_RC_FATAL(q->lrc))
    481 		return (nldaprc2retcode(q->lrc));
    482 	return (IDMAP_SUCCESS);
    483 }
    484 
    485 /*
    486  * Add a lookup by user/group name request to the batch.
    487  */
    488 static
    489 idmap_retcode
    490 idmap_nldap_byunixname_batch_add(idmap_nldap_query_state_t *qs,
    491 	const char *unixname, int is_user,
    492 	char **dn, char **attr, char **value,
    493 	char **winname, char **windomain, uid_t *pid, idmap_retcode *rc)
    494 {
    495 	idmap_nldap_q_t		*q;
    496 	const char		*db, *filter, *udata;
    497 	int			len;
    498 	char			*s_unixname = NULL;
    499 	const char		**attrs;
    500 	const char		*pwd_attrs[] = {UIDNUMBER, NULL, NULL};
    501 	const char		*grp_attrs[] = {GIDNUMBER, NULL, NULL};
    502 
    503 	q = &(qs->queries[qs->qid++]);
    504 	q->winname = winname;
    505 	q->windomain = windomain;
    506 	q->pid = pid;
    507 	q->rc = rc;
    508 	q->is_user = is_user;
    509 	q->dn = dn;
    510 	q->attr = attr;
    511 	q->value = value;
    512 
    513 	if (is_user) {
    514 		db = "passwd";
    515 		filter = _F_GETPWNAM;
    516 		udata = _F_GETPWNAM_SSD;
    517 		pwd_attrs[1] = qs->nldap_winname_attr;
    518 		attrs = pwd_attrs;
    519 	} else {
    520 		db = "group";
    521 		filter = _F_GETGRNAM;
    522 		udata = _F_GETGRNAM_SSD;
    523 		grp_attrs[1] = qs->nldap_winname_attr;
    524 		attrs = grp_attrs;
    525 	}
    526 
    527 	s_unixname = sanitize_for_ldap_filter(unixname);
    528 	if (s_unixname == NULL) {
    529 		*q->rc = IDMAP_ERR_MEMORY;
    530 		return (IDMAP_ERR_MEMORY);
    531 	}
    532 
    533 	len = snprintf(NULL, 0, filter, s_unixname) + 1;
    534 	q->filter = malloc(len);
    535 	if (q->filter == NULL) {
    536 		if (s_unixname != unixname)
    537 			free(s_unixname);
    538 		*q->rc = IDMAP_ERR_MEMORY;
    539 		return (IDMAP_ERR_MEMORY);
    540 	}
    541 	(void) snprintf(q->filter, len, filter, s_unixname);
    542 
    543 	len = snprintf(NULL, 0, udata, s_unixname) + 1;
    544 	q->udata = malloc(len);
    545 	if (q->udata == NULL) {
    546 		if (s_unixname != unixname)
    547 			free(s_unixname);
    548 		*q->rc = IDMAP_ERR_MEMORY;
    549 		return (IDMAP_ERR_MEMORY);
    550 	}
    551 	(void) snprintf(q->udata, len, udata, s_unixname);
    552 
    553 	if (s_unixname != unixname)
    554 		free(s_unixname);
    555 
    556 	q->lrc = __ns_ldap_list_batch_add(qs->batch, db, q->filter,
    557 	    merge_SSD_filter, attrs, NULL, qs->flag, &q->result,
    558 	    &q->errorp, &q->lrc, NULL, q->udata);
    559 
    560 	if (IS_NLDAP_RC_FATAL(q->lrc))
    561 		return (nldaprc2retcode(q->lrc));
    562 	return (IDMAP_SUCCESS);
    563 }
    564 
    565 /*
    566  * Free the batch
    567  */
    568 static
    569 void
    570 idmap_nldap_lookup_batch_release(idmap_nldap_query_state_t *qs)
    571 {
    572 	idmap_nldap_q_t		*q;
    573 	int			i;
    574 
    575 	if (qs->batch != NULL)
    576 		(void) __ns_ldap_list_batch_release(qs->batch);
    577 	for (i = 0; i < qs->qid; i++) {
    578 		q = &(qs->queries[i]);
    579 		free(q->filter);
    580 		free(q->udata);
    581 		if (q->errorp != NULL)
    582 			(void) __ns_ldap_freeError(&q->errorp);
    583 		if (q->result != NULL)
    584 			(void) __ns_ldap_freeResult(&q->result);
    585 	}
    586 	free(qs);
    587 }
    588 
    589 /*
    590  * Process all requests added to the batch and then free the batch.
    591  * The results for individual requests will be accessible using the
    592  * pointers passed during idmap_nldap_lookup_batch_end.
    593  */
    594 static
    595 idmap_retcode
    596 idmap_nldap_lookup_batch_end(idmap_nldap_query_state_t *qs)
    597 {
    598 	idmap_nldap_q_t		*q;
    599 	int			i;
    600 	ns_ldap_entry_t		*entry;
    601 	char			**val, *end, *str, *name, *dom;
    602 	idmap_retcode		rc = IDMAP_SUCCESS;
    603 
    604 	(void) __ns_ldap_list_batch_end(qs->batch);
    605 	qs->batch = NULL;
    606 	for (i = 0; i < qs->qid; i++) {
    607 		q = &(qs->queries[i]);
    608 		*q->rc = nldaprc2retcode(q->lrc);
    609 		if (*q->rc != IDMAP_SUCCESS)
    610 			continue;
    611 		if (q->result == NULL ||
    612 		    !q->result->entries_count ||
    613 		    (entry = q->result->entry) == NULL ||
    614 		    !entry->attr_count) {
    615 			*q->rc = IDMAP_ERR_NOTFOUND;
    616 			continue;
    617 		}
    618 		/* Get uid/gid */
    619 		if (q->pid != NULL) {
    620 			val = __ns_ldap_getAttr(entry,
    621 			    (q->is_user) ? UIDNUMBER : GIDNUMBER);
    622 			if (val != NULL && *val != NULL)
    623 				*q->pid = strtoul(*val, &end, 10);
    624 		}
    625 		/* Get unixname */
    626 		if (q->unixname != NULL) {
    627 			val = __ns_ldap_getAttr(entry,
    628 			    (q->is_user) ? UID : CN);
    629 			if (val != NULL && *val != NULL) {
    630 				*q->unixname = strdup(*val);
    631 				if (*q->unixname == NULL) {
    632 					rc = *q->rc = IDMAP_ERR_MEMORY;
    633 					goto out;
    634 				}
    635 			}
    636 		}
    637 		/* Get DN for how info */
    638 		if (q->dn != NULL) {
    639 			val = __ns_ldap_getAttr(entry, DN);
    640 			if (val != NULL && *val != NULL) {
    641 				*q->dn = strdup(*val);
    642 				if (*q->dn == NULL) {
    643 					rc = *q->rc = IDMAP_ERR_MEMORY;
    644 					goto out;
    645 				}
    646 			}
    647 		}
    648 		/* Get nldap name mapping attr name for how info */
    649 		if (q->attr != NULL) {
    650 			*q->attr = strdup(qs->nldap_winname_attr);
    651 			if (*q->attr == NULL) {
    652 				rc = *q->rc = IDMAP_ERR_MEMORY;
    653 				goto out;
    654 			}
    655 		}
    656 		/* Get nldap name mapping attr value for how info */
    657 		val =  __ns_ldap_getAttr(entry, qs->nldap_winname_attr);
    658 		if (val == NULL || *val == NULL)
    659 			continue;
    660 		if (q->value != NULL) {
    661 			*q->value = strdup(*val);
    662 			if (*q->value == NULL) {
    663 				rc = *q->rc = IDMAP_ERR_MEMORY;
    664 				goto out;
    665 			}
    666 		}
    667 
    668 		/* Get winname and windomain */
    669 		if (q->winname == NULL && q->windomain == NULL)
    670 			continue;
    671 		/*
    672 		 * We need to split the value into winname and
    673 		 * windomain. The value could be either in NT4
    674 		 * style (i.e. dom\name) or AD-style (i.e. name@dom).
    675 		 * We choose the first '\\' if it's in NT4 style and
    676 		 * the last '@' if it's in AD-style for the split.
    677 		 */
    678 		name = dom = NULL;
    679 		if (lookup_wksids_name2sid(*val, NULL, NULL, NULL, NULL, NULL,
    680 		    NULL) == IDMAP_SUCCESS) {
    681 			name = *val;
    682 			dom = NULL;
    683 		} else if ((str = strchr(*val, '\\')) != NULL) {
    684 			*str = '\0';
    685 			name = str + 1;
    686 			dom = *val;
    687 		} else if ((str = strrchr(*val, '@')) != NULL) {
    688 			*str = '\0';
    689 			name = *val;
    690 			dom = str + 1;
    691 		} else {
    692 			idmapdlog(LOG_INFO, "Domain-less "
    693 			    "winname (%s) found in Native LDAP", *val);
    694 			*q->rc = IDMAP_ERR_NS_LDAP_BAD_WINNAME;
    695 			continue;
    696 		}
    697 		if (q->winname != NULL) {
    698 			*q->winname = strdup(name);
    699 			if (*q->winname == NULL) {
    700 				rc = *q->rc = IDMAP_ERR_MEMORY;
    701 				goto out;
    702 			}
    703 		}
    704 		if (q->windomain != NULL && dom != NULL) {
    705 			*q->windomain = strdup(dom);
    706 			if (*q->windomain == NULL) {
    707 				rc = *q->rc = IDMAP_ERR_MEMORY;
    708 				goto out;
    709 			}
    710 		}
    711 	}
    712 
    713 out:
    714 	(void) idmap_nldap_lookup_batch_release(qs);
    715 	return (rc);
    716 }
    717 
    718 /* ARGSUSED */
    719 idmap_retcode
    720 nldap_lookup_one(lookup_state_t *state, idmap_mapping *req, idmap_id_res *res)
    721 {
    722 	idmap_mapping_batch	batch;
    723 	idmap_ids_res		result;
    724 
    725 	/* Using nldap_lookup_batch() */
    726 
    727 	batch.idmap_mapping_batch_len = 1;
    728 	batch.idmap_mapping_batch_val = req;
    729 	result.ids.ids_len = 1;
    730 	result.ids.ids_val = res;
    731 	return (nldap_lookup_batch(state, &batch, &result));
    732 }
    733 
    734 /* ARGSUSED */
    735 idmap_retcode
    736 nldap_lookup_batch(lookup_state_t *state, idmap_mapping_batch *batch,
    737 		idmap_ids_res *result)
    738 {
    739 	idmap_retcode			retcode, rc1;
    740 	int				i, add, is_wuser;
    741 	idmap_mapping			*req;
    742 	idmap_id_res			*res;
    743 	idmap_nldap_query_state_t	*qs = NULL;
    744 	idmap_how			*how;
    745 
    746 	if (state->nldap_nqueries == 0)
    747 		return (IDMAP_SUCCESS);
    748 
    749 	/* Create nldap lookup batch */
    750 	retcode = idmap_nldap_lookup_batch_start(state->nldap_nqueries, &qs);
    751 	if (retcode != IDMAP_SUCCESS) {
    752 		idmapdlog(LOG_ERR,
    753 		    "Failed to create batch for native LDAP lookup");
    754 		goto out;
    755 	}
    756 
    757 	qs->nldap_winname_attr = state->nldap_winname_attr;
    758 	qs->defdom = state->defdom;
    759 
    760 	/* Add requests to the batch */
    761 	for (i = 0, add = 0; i < batch->idmap_mapping_batch_len; i++) {
    762 		req = &batch->idmap_mapping_batch_val[i];
    763 		res = &result->ids.ids_val[i];
    764 		retcode = IDMAP_SUCCESS;
    765 
    766 		/* Skip if not marked for nldap lookup */
    767 		if (!(req->direction & _IDMAP_F_LOOKUP_NLDAP))
    768 			continue;
    769 
    770 		if (IS_REQUEST_SID(*req, 1)) {
    771 
    772 			/* win2unix request: */
    773 
    774 			/*
    775 			 * When processing a win2unix request, nldap lookup
    776 			 * is performed after AD lookup or a successful
    777 			 * name-cache lookup. Therefore we should already
    778 			 * have sid, winname and sidtype. Note that
    779 			 * windomain could be NULL e.g. well-known SIDs.
    780 			 */
    781 			assert(req->id1name != NULL &&
    782 			    (res->id.idtype == IDMAP_UID ||
    783 			    res->id.idtype == IDMAP_GID));
    784 
    785 			/* Skip if we already have pid and unixname */
    786 			if (req->id2name != NULL &&
    787 			    res->id.idmap_id_u.uid != SENTINEL_PID) {
    788 				res->retcode = IDMAP_SUCCESS;
    789 				continue;
    790 			}
    791 
    792 			/* Clear leftover value */
    793 			free(req->id2name);
    794 			req->id2name = NULL;
    795 
    796 			/* Lookup nldap by winname to get pid and unixname */
    797 			add = 1;
    798 			idmap_info_free(&res->info);
    799 			res->info.src = IDMAP_MAP_SRC_NEW;
    800 			how = &res->info.how;
    801 			how->map_type = IDMAP_MAP_TYPE_DS_NLDAP;
    802 			retcode = idmap_nldap_bywinname_batch_add(
    803 			    qs, req->id1name, req->id1domain,
    804 			    (res->id.idtype == IDMAP_UID) ? 1 : 0,
    805 			    &how->idmap_how_u.nldap.dn,
    806 			    &how->idmap_how_u.nldap.attr,
    807 			    &how->idmap_how_u.nldap.value,
    808 			    &req->id2name, &res->id.idmap_id_u.uid,
    809 			    &res->retcode);
    810 
    811 		} else if (IS_REQUEST_UID(*req) || IS_REQUEST_GID(*req)) {
    812 
    813 			/* unix2win request: */
    814 
    815 			/* Skip if we already have winname */
    816 			if (req->id2name != NULL) {
    817 				res->retcode = IDMAP_SUCCESS;
    818 				continue;
    819 			}
    820 
    821 			/* Clear old value */
    822 			free(req->id2domain);
    823 			req->id2domain = NULL;
    824 
    825 			/* Set how info */
    826 			idmap_info_free(&res->info);
    827 			res->info.src = IDMAP_MAP_SRC_NEW;
    828 			how = &res->info.how;
    829 			how->map_type = IDMAP_MAP_TYPE_DS_NLDAP;
    830 
    831 			/* Lookup nldap by pid or unixname to get winname */
    832 			if (req->id1.idmap_id_u.uid != SENTINEL_PID) {
    833 				add = 1;
    834 				retcode = idmap_nldap_bypid_batch_add(
    835 				    qs, req->id1.idmap_id_u.uid,
    836 				    (req->id1.idtype == IDMAP_UID) ? 1 : 0,
    837 				    &how->idmap_how_u.nldap.dn,
    838 				    &how->idmap_how_u.nldap.attr,
    839 				    &how->idmap_how_u.nldap.value,
    840 				    &req->id2name, &req->id2domain,
    841 				    (req->id1name == NULL) ?
    842 				    &req->id1name : NULL,
    843 				    &res->retcode);
    844 			} else if (req->id1name != NULL) {
    845 				add = 1;
    846 				retcode = idmap_nldap_byunixname_batch_add(
    847 				    qs, req->id1name,
    848 				    (req->id1.idtype == IDMAP_UID) ? 1 : 0,
    849 				    &how->idmap_how_u.nldap.dn,
    850 				    &how->idmap_how_u.nldap.attr,
    851 				    &how->idmap_how_u.nldap.value,
    852 				    &req->id2name, &req->id2domain,
    853 				    &req->id1.idmap_id_u.uid, &res->retcode);
    854 			}
    855 
    856 		}
    857 
    858 		/*
    859 		 * nldap_batch_add API returns error only on fatal failures
    860 		 * otherwise it returns success and the actual status
    861 		 * is stored in the individual request (res->retcode).
    862 		 * Stop adding requests to this batch on fatal failures
    863 		 * (i.e. if retcode != success)
    864 		 */
    865 		if (retcode != IDMAP_SUCCESS)
    866 			break;
    867 	}
    868 
    869 	if (!add)
    870 		idmap_nldap_lookup_batch_release(qs);
    871 	else if (retcode != IDMAP_SUCCESS)
    872 		idmap_nldap_lookup_batch_release(qs);
    873 	else
    874 		retcode = idmap_nldap_lookup_batch_end(qs);
    875 
    876 out:
    877 	for (i = 0; i < batch->idmap_mapping_batch_len; i++) {
    878 		req = &batch->idmap_mapping_batch_val[i];
    879 		res = &result->ids.ids_val[i];
    880 		if (!(req->direction & _IDMAP_F_LOOKUP_NLDAP))
    881 			continue;
    882 
    883 		/* Reset nldap flag */
    884 		req->direction &= ~(_IDMAP_F_LOOKUP_NLDAP);
    885 
    886 		/*
    887 		 * As noted earlier retcode != success if there were fatal
    888 		 * errors during batch_start and batch_adds. If so then set
    889 		 * the status of each nldap request to that error.
    890 		 */
    891 		if (retcode != IDMAP_SUCCESS) {
    892 			res->retcode = retcode;
    893 			continue;
    894 		}
    895 		if (!add)
    896 			continue;
    897 
    898 		/*
    899 		 * If we successfully retrieved winname from nldap entry
    900 		 * then lookup winname2sid locally. If not found locally
    901 		 * then mark this request for AD lookup.
    902 		 */
    903 		if (res->retcode == IDMAP_SUCCESS &&
    904 		    req->id2name != NULL &&
    905 		    res->id.idmap_id_u.sid.prefix == NULL &&
    906 		    (IS_REQUEST_UID(*req) || IS_REQUEST_GID(*req))) {
    907 
    908 			is_wuser = -1;
    909 			rc1 = lookup_name2sid(state->cache,
    910 			    req->id2name, req->id2domain, &is_wuser,
    911 			    NULL, NULL,
    912 			    &res->id.idmap_id_u.sid.prefix,
    913 			    &res->id.idmap_id_u.sid.rid, req, 1);
    914 			if (rc1 == IDMAP_SUCCESS)
    915 				res->id.idtype =
    916 				    is_wuser ? IDMAP_USID : IDMAP_GSID;
    917 			else if (rc1 == IDMAP_ERR_NOTFOUND) {
    918 				req->direction |= _IDMAP_F_LOOKUP_AD;
    919 				state->ad_nqueries++;
    920 			} else
    921 				res->retcode = rc1;
    922 		}
    923 
    924 		/*
    925 		 * Unset non-fatal errors in individual request. This allows
    926 		 * the next pass to process other mapping mechanisms for
    927 		 * this request.
    928 		 */
    929 		if (res->retcode != IDMAP_SUCCESS &&
    930 		    res->retcode != IDMAP_ERR_NS_LDAP_BAD_WINNAME &&
    931 		    !(IDMAP_FATAL_ERROR(res->retcode))) {
    932 			idmap_info_free(&res->info);
    933 			res->retcode = IDMAP_SUCCESS;
    934 		}
    935 	}
    936 
    937 	state->nldap_nqueries = 0;
    938 	return (retcode);
    939 }
    940