xref: /illumos-gate/usr/src/cmd/idmap/idmapd/nldaputils.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
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