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