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