xref: /illumos-gate/usr/src/cmd/idmap/idmapd/nldaputils.c (revision eb9a1df2aeb866bf1de4494433b6d7e5fa07b3ae)
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 
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 	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 
181 static
182 char
183 hex_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  */
210 char *
211 sanitize_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  */
244 static
245 idmap_retcode
246 nldaprc2retcode(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  */
275 static
276 idmap_retcode
277 idmap_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  */
298 static
299 idmap_retcode
300 idmap_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 
427 errout:
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  */
439 static
440 idmap_retcode
441 idmap_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  */
505 static
506 idmap_retcode
507 idmap_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  */
585 static
586 void
587 idmap_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  */
611 static
612 idmap_retcode
613 idmap_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 
730 out:
731 	(void) idmap_nldap_lookup_batch_release(qs);
732 	return (rc);
733 }
734 
735 /* ARGSUSED */
736 idmap_retcode
737 nldap_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 
877 out:
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