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
96typedef 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
113typedef 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 */
144static
145int
146merge_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
163static
164char
165hex_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 */
192char *
193sanitize_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 */
226static
227idmap_retcode
228nldaprc2retcode(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 */
257static
258idmap_retcode
259idmap_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 */
280static
281idmap_retcode
282idmap_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
409errout:
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 */
421static
422idmap_retcode
423idmap_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 */
487static
488idmap_retcode
489idmap_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 */
567static
568void
569idmap_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 */
593static
594idmap_retcode
595idmap_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
712out:
713	(void) idmap_nldap_lookup_batch_release(qs);
714	return (rc);
715}
716
717/* ARGSUSED */
718idmap_retcode
719nldap_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
859out:
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