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