xref: /illumos-gate/usr/src/lib/libsldap/common/ns_reads.c (revision 1fa2a66491e7d8ae0be84e7da4da8e812480c710)
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  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
24  * Copyright 2020 Joyent, Inc.
25  */
26 
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <stdlib.h>
30 #include <libintl.h>
31 #include <ctype.h>
32 #include <syslog.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <priv.h>
39 
40 #include "ns_sldap.h"
41 #include "ns_internal.h"
42 #include "ns_cache_door.h"
43 #include "ns_connmgmt.h"
44 
45 #define	_NIS_FILTER	"nisdomain=*"
46 #define	_NIS_DOMAIN	"nisdomain"
47 static const char *nis_domain_attrs[] = {
48 	_NIS_DOMAIN,
49 	(char *)NULL
50 };
51 
52 static int validate_filter(ns_ldap_cookie_t *cookie);
53 
54 void
55 __ns_ldap_freeEntry(ns_ldap_entry_t *ep)
56 {
57 	int		j, k = 0;
58 
59 	if (ep == NULL)
60 		return;
61 
62 	if (ep->attr_pair == NULL) {
63 		free(ep);
64 		return;
65 	}
66 	for (j = 0; j < ep->attr_count; j++) {
67 		if (ep->attr_pair[j] == NULL)
68 			continue;
69 		if (ep->attr_pair[j]->attrname)
70 			free(ep->attr_pair[j]->attrname);
71 		if (ep->attr_pair[j]->attrvalue) {
72 			for (k = 0; (k < ep->attr_pair[j]->value_count) &&
73 			    (ep->attr_pair[j]->attrvalue[k]); k++) {
74 				free(ep->attr_pair[j]->attrvalue[k]);
75 			}
76 			free(ep->attr_pair[j]->attrvalue);
77 		}
78 		free(ep->attr_pair[j]);
79 	}
80 	free(ep->attr_pair);
81 	free(ep);
82 }
83 
84 static void
85 _freeControlList(LDAPControl ***ctrls)
86 {
87 	LDAPControl	**ctrl;
88 
89 	if (ctrls == NULL || *ctrls == NULL)
90 		return;
91 
92 	for (ctrl = *ctrls; *ctrl != NULL; ctrl++)
93 		ldap_control_free(*ctrl);
94 	free(*ctrls);
95 	*ctrls = NULL;
96 }
97 /*
98  * Convert attribute type in a RDN that has an attribute mapping to the
99  * original mappped type.
100  * e.g.
101  * cn<->cn-st and iphostnumber<->iphostnumber-st
102  * cn-st=aaa+iphostnumber-st=10.10.01.01
103  * is mapped to
104  * cn=aaa+iphostnumber=10.10.01.01
105  *
106  * Input - service: e.g. hosts, passwd etc.
107  *         rdn: RDN
108  * Return: NULL - No attribute mapping in the RDN
109  *         Non-NULL - The attribute type(s) in the RDN are mapped and
110  *                    the memory is allocated for the new rdn.
111  *
112  */
113 static char *
114 _cvtRDN(const char *service, const char *rdn)
115 {
116 	char	**attrs, **mapped_attrs, **mapp, *type, *value, *attr;
117 	char	*new_rdn = NULL;
118 	int	nAttr = 0, i, attr_mapped, len = 0;
119 
120 	/* Break down "type=value\0" pairs. Assume RDN is normalized */
121 	if ((attrs = ldap_explode_rdn(rdn, 0)) == NULL)
122 		return (NULL);
123 
124 	for (nAttr = 0; attrs[nAttr] != NULL; nAttr++)
125 		;
126 
127 	if ((mapped_attrs = (char **)calloc(nAttr, sizeof (char *))) == NULL) {
128 		ldap_value_free(attrs);
129 		return (NULL);
130 	}
131 
132 	attr_mapped = 0;
133 	for (i = 0; i < nAttr; i++) {
134 		/* Parse type=value pair */
135 		if ((type = strtok_r(attrs[i], "=", &value)) == NULL ||
136 		    value == NULL)
137 			goto cleanup;
138 		/* Reverse map: e.g. cn-sm -> cn */
139 		mapp = __ns_ldap_getOrigAttribute(service, type);
140 		if (mapp != NULL && mapp[0] != NULL) {
141 			/* The attribute mapping is found */
142 			type = mapp[0];
143 			attr_mapped = 1;
144 
145 			/* "type=value\0" */
146 			len = strlen(type) + strlen(value) + 2;
147 
148 			/* Reconstruct type=value pair. A string is allocated */
149 			if ((attr = (char *)calloc(1, len)) == NULL) {
150 				__s_api_free2dArray(mapp);
151 				goto cleanup;
152 			}
153 			(void) snprintf(attr, len, "%s=%s", type, value);
154 			mapped_attrs[i] = attr;
155 		} else {
156 			/*
157 			 * No attribute mapping. attrs[i] is going to be copied
158 			 * later. Restore "type\0value\0" back to
159 			 * "type=value\0".
160 			 */
161 			type[strlen(type)] = '=';
162 		}
163 		__s_api_free2dArray(mapp);
164 	}
165 	if (attr_mapped == 0)
166 		/* No attribute mapping. Don't bother to reconstruct RDN */
167 		goto cleanup;
168 
169 	len = 0;
170 	/* Reconstruct RDN from type=value pairs */
171 	for (i = 0; i < nAttr; i++) {
172 		if (mapped_attrs[i])
173 			len += strlen(mapped_attrs[i]);
174 		else
175 			len += strlen(attrs[i]);
176 		/* Add 1 for "+" */
177 		len++;
178 	}
179 	if ((new_rdn = (char *)calloc(1, ++len)) == NULL)
180 		goto cleanup;
181 	for (i = 0; i < nAttr; i++) {
182 		if (i > 0)
183 			/* Add seperator */
184 			(void) strlcat(new_rdn, "+", len);
185 
186 		if (mapped_attrs[i])
187 			(void) strlcat(new_rdn, mapped_attrs[i], len);
188 		else
189 			(void) strlcat(new_rdn, attrs[i], len);
190 
191 	}
192 cleanup:
193 	ldap_value_free(attrs);
194 	if (mapped_attrs) {
195 		if (attr_mapped) {
196 			for (i = 0; i < nAttr; i++) {
197 				if (mapped_attrs[i])
198 					free(mapped_attrs[i]);
199 			}
200 		}
201 		free(mapped_attrs);
202 	}
203 
204 	return (new_rdn);
205 }
206 /*
207  * Convert attribute type in a DN that has an attribute mapping to the
208  * original mappped type.
209  * e.g
210  * The mappings are cn<->cn-sm, iphostnumber<->iphostnumber-sm
211  *
212  * dn: cn-sm=aaa+iphostnumber-sm=9.9.9.9,dc=central,dc=sun,dc=com
213  * is converted to
214  * dn: cn=aaa+iphostnumber=9.9.9.9,dc=central,dc=sun,dc=com
215  *
216  * Input - service: e.g. hosts, passwd etc.
217  *         dn: the value of a distinguished name
218  * Return - NULL: error
219  *          non-NULL: A converted DN and the memory is allocated
220  */
221 static char *
222 _cvtDN(const char *service, const char *dn)
223 {
224 	char	**mapped_rdns;
225 	char	**rdns, *new_rdn, *new_dn = NULL;
226 	int	nRdn = 0, i, len = 0, rdn_mapped;
227 
228 	if (service == NULL || dn == NULL)
229 		return (NULL);
230 
231 	if ((rdns = ldap_explode_dn(dn, 0)) == NULL)
232 		return (NULL);
233 
234 	for (nRdn = 0; rdns[nRdn] != NULL; nRdn++)
235 		;
236 
237 	if ((mapped_rdns = (char **)calloc(nRdn, sizeof (char *))) == NULL) {
238 		ldap_value_free(rdns);
239 		return (NULL);
240 	}
241 
242 	rdn_mapped = 0;
243 	/* Break down RDNs in a DN */
244 	for (i = 0; i < nRdn; i++) {
245 		if ((new_rdn = _cvtRDN(service, rdns[i])) != NULL) {
246 			mapped_rdns[i] = new_rdn;
247 			rdn_mapped = 1;
248 		}
249 	}
250 	if (rdn_mapped == 0) {
251 		/*
252 		 * No RDN contains any attribute mapping.
253 		 * Don't bother to reconstruct DN from RDN. Copy DN directly.
254 		 */
255 		new_dn = strdup(dn);
256 		goto cleanup;
257 	}
258 	/*
259 	 * Reconstruct dn from RDNs.
260 	 * Calculate the length first.
261 	 */
262 	for (i = 0; i < nRdn; i++) {
263 		if (mapped_rdns[i])
264 			len += strlen(mapped_rdns[i]);
265 		else
266 			len += strlen(rdns[i]);
267 
268 		/* add 1 for ',' */
269 		len ++;
270 	}
271 	if ((new_dn = (char *)calloc(1, ++len)) == NULL)
272 		goto cleanup;
273 	for (i = 0; i < nRdn; i++) {
274 		if (i > 0)
275 			/* Add seperator */
276 			(void) strlcat(new_dn, ",", len);
277 
278 		if (mapped_rdns[i])
279 			(void) strlcat(new_dn, mapped_rdns[i], len);
280 		else
281 			(void) strlcat(new_dn, rdns[i], len);
282 
283 	}
284 
285 cleanup:
286 	ldap_value_free(rdns);
287 	if (mapped_rdns) {
288 		if (rdn_mapped) {
289 			for (i = 0; i < nRdn; i++) {
290 				if (mapped_rdns[i])
291 					free(mapped_rdns[i]);
292 			}
293 		}
294 		free(mapped_rdns);
295 	}
296 
297 	return (new_dn);
298 }
299 /*
300  * Convert a single ldap entry from a LDAPMessage
301  * into an ns_ldap_entry structure.
302  * Schema map the entry if specified in flags
303  */
304 
305 static int
306 __s_api_cvtEntry(LDAP *ld, const char *service, LDAPMessage *e, int flags,
307     ns_ldap_entry_t **ret, ns_ldap_error_t **error)
308 {
309 
310 	ns_ldap_entry_t	*ep = NULL;
311 	ns_ldap_attr_t	**ap = NULL;
312 	BerElement	*ber;
313 	char		*attr = NULL;
314 	char		**vals = NULL;
315 	char		**mapping;
316 	char		*dn;
317 	int		nAttrs = 0;
318 	int		i, j, k = 0;
319 	char		**gecos_mapping = NULL;
320 	int		gecos_val_index[3] = { -1, -1, -1};
321 	char		errstr[MAXERROR];
322 	int		schema_mapping_existed = FALSE;
323 	int		gecos_mapping_existed = FALSE;
324 	int		gecos_attr_matched;
325 	int		auto_service = FALSE;
326 	int		rc = NS_LDAP_SUCCESS;
327 
328 	if (e == NULL || ret == NULL || error == NULL)
329 		return (NS_LDAP_INVALID_PARAM);
330 
331 	*error = NULL;
332 
333 	ep = (ns_ldap_entry_t *)calloc(1, sizeof (ns_ldap_entry_t));
334 	if (ep == NULL)
335 		return (NS_LDAP_MEMORY);
336 
337 	if (service != NULL &&
338 	    (strncasecmp(service, "auto_", 5) == 0 ||
339 	    strcasecmp(service, "automount") == 0))
340 		auto_service = TRUE;
341 	/*
342 	 * see if schema mapping existed for the given service
343 	 */
344 	mapping = __ns_ldap_getOrigAttribute(service,
345 	    NS_HASH_SCHEMA_MAPPING_EXISTED);
346 	if (mapping) {
347 		schema_mapping_existed = TRUE;
348 		__s_api_free2dArray(mapping);
349 		mapping = NULL;
350 	} else if (auto_service) {
351 		/*
352 		 * If service == auto_* and no
353 		 * schema mapping found
354 		 * then try automount
355 		 * There is certain case that schema mapping exist
356 		 * but __ns_ldap_getOrigAttribute(service,
357 		 *	NS_HASH_SCHEMA_MAPPING_EXISTED);
358 		 * returns NULL.
359 		 * e.g.
360 		 * NS_LDAP_ATTRIBUTEMAP = automount:automountMapName=AAA
361 		 * NS_LDAP_OBJECTCLASSMAP = automount:automountMap=MynisMap
362 		 * NS_LDAP_OBJECTCLASSMAP = automount:automount=MynisObject
363 		 *
364 		 * Make a check for schema_mapping_existed here
365 		 * so later on __s_api_convert_automountmapname won't be called
366 		 * unnecessarily. It is also used for attribute mapping
367 		 * and objectclass mapping.
368 		 */
369 		mapping = __ns_ldap_getOrigAttribute("automount",
370 		    NS_HASH_SCHEMA_MAPPING_EXISTED);
371 		if (mapping) {
372 			schema_mapping_existed = TRUE;
373 			__s_api_free2dArray(mapping);
374 			mapping = NULL;
375 		}
376 	}
377 
378 	nAttrs = 1;  /* start with 1 for the DN attr */
379 	for (attr = ldap_first_attribute(ld, e, &ber); attr != NULL;
380 	    attr = ldap_next_attribute(ld, e, ber)) {
381 		nAttrs++;
382 		ldap_memfree(attr);
383 		attr = NULL;
384 	}
385 	ber_free(ber, 0);
386 	ber = NULL;
387 
388 	ep->attr_count = nAttrs;
389 
390 	/*
391 	 * add 1 for "gecos" 1 to N attribute mapping,
392 	 * just in case it is needed.
393 	 * ep->attr_count will be updated later if that is true.
394 	 */
395 	ap = (ns_ldap_attr_t **)calloc(ep->attr_count + 1,
396 	    sizeof (ns_ldap_attr_t *));
397 	if (ap == NULL) {
398 		__ns_ldap_freeEntry(ep);
399 		ep = NULL;
400 		return (NS_LDAP_MEMORY);
401 	}
402 	ep->attr_pair = ap;
403 
404 	/* DN attribute */
405 	dn = ldap_get_dn(ld, e);
406 	ap[0] = (ns_ldap_attr_t *)calloc(1, sizeof (ns_ldap_attr_t));
407 	if (ap[0] == NULL) {
408 		ldap_memfree(dn);
409 		dn = NULL;
410 		__ns_ldap_freeEntry(ep);
411 		ep = NULL;
412 		return (NS_LDAP_MEMORY);
413 	}
414 
415 	if ((ap[0]->attrname = strdup("dn")) == NULL) {
416 		ldap_memfree(dn);
417 		dn = NULL;
418 		__ns_ldap_freeEntry(ep);
419 		ep = NULL;
420 		return (NS_LDAP_INVALID_PARAM);
421 	}
422 	ap[0]->value_count = 1;
423 	if ((ap[0]->attrvalue = (char **)
424 	    calloc(2, sizeof (char *))) == NULL) {
425 		ldap_memfree(dn);
426 		dn = NULL;
427 		__ns_ldap_freeEntry(ep);
428 		ep = NULL;
429 		return (NS_LDAP_MEMORY);
430 	}
431 
432 	if (schema_mapping_existed && ((flags & NS_LDAP_NOT_CVT_DN) == 0))
433 		ap[0]->attrvalue[0] = _cvtDN(service, dn);
434 	else
435 		ap[0]->attrvalue[0] = strdup(dn);
436 
437 	if (ap[0]->attrvalue[0] == NULL) {
438 		ldap_memfree(dn);
439 		dn = NULL;
440 		__ns_ldap_freeEntry(ep);
441 		ep = NULL;
442 		return (NS_LDAP_MEMORY);
443 	}
444 	ldap_memfree(dn);
445 	dn = NULL;
446 
447 	if ((flags & NS_LDAP_NOMAP) == 0 && auto_service &&
448 	    schema_mapping_existed) {
449 		rc = __s_api_convert_automountmapname(service,
450 		    &ap[0]->attrvalue[0],
451 		    error);
452 		if (rc != NS_LDAP_SUCCESS) {
453 			__ns_ldap_freeEntry(ep);
454 			ep = NULL;
455 			return (rc);
456 		}
457 	}
458 
459 	/* other attributes */
460 	for (attr = ldap_first_attribute(ld, e, &ber), j = 1;
461 	    attr != NULL && j != nAttrs;
462 	    attr = ldap_next_attribute(ld, e, ber), j++) {
463 		/* allocate new attr name */
464 
465 		if ((ap[j] = (ns_ldap_attr_t *)
466 		    calloc(1, sizeof (ns_ldap_attr_t))) == NULL) {
467 			ber_free(ber, 0);
468 			ber = NULL;
469 			__ns_ldap_freeEntry(ep);
470 			ep = NULL;
471 			if (gecos_mapping)
472 				__s_api_free2dArray(gecos_mapping);
473 			gecos_mapping = NULL;
474 			return (NS_LDAP_MEMORY);
475 		}
476 
477 		if ((flags & NS_LDAP_NOMAP) || schema_mapping_existed == FALSE)
478 			mapping = NULL;
479 		else
480 			mapping = __ns_ldap_getOrigAttribute(service, attr);
481 
482 		if (mapping == NULL && auto_service &&
483 		    schema_mapping_existed && (flags & NS_LDAP_NOMAP) == 0)
484 			/*
485 			 * if service == auto_* and no schema mapping found
486 			 * and schema_mapping_existed is TRUE and NS_LDAP_NOMAP
487 			 * is not set then try automount e.g.
488 			 * NS_LDAP_ATTRIBUTEMAP = automount:automountMapName=AAA
489 			 */
490 			mapping = __ns_ldap_getOrigAttribute("automount",
491 			    attr);
492 
493 		if (mapping == NULL) {
494 			if ((ap[j]->attrname = strdup(attr)) == NULL) {
495 				ber_free(ber, 0);
496 				ber = NULL;
497 				__ns_ldap_freeEntry(ep);
498 				ep = NULL;
499 				if (gecos_mapping)
500 					__s_api_free2dArray(gecos_mapping);
501 				gecos_mapping = NULL;
502 				return (NS_LDAP_MEMORY);
503 			}
504 		} else {
505 			/*
506 			 * for "gecos" 1 to N mapping,
507 			 * do not remove the mapped attribute,
508 			 * just create a new gecos attribute
509 			 * and append it to the end of the attribute list
510 			 */
511 			if (strcasecmp(mapping[0], "gecos") == 0) {
512 				ap[j]->attrname = strdup(attr);
513 				gecos_mapping_existed = TRUE;
514 			} else {
515 				ap[j]->attrname = strdup(mapping[0]);
516 			}
517 
518 			if (ap[j]->attrname == NULL) {
519 				ber_free(ber, 0);
520 				ber = NULL;
521 				__ns_ldap_freeEntry(ep);
522 				ep = NULL;
523 				if (gecos_mapping)
524 					__s_api_free2dArray(gecos_mapping);
525 				gecos_mapping = NULL;
526 				return (NS_LDAP_MEMORY);
527 			}
528 			/*
529 			 * 1 to N attribute mapping processing
530 			 * is only done for "gecos"
531 			 */
532 
533 			if (strcasecmp(mapping[0], "gecos") == 0) {
534 				/*
535 				 * get attribute mapping for "gecos",
536 				 * need to know the number and order of the
537 				 * mapped attributes
538 				 */
539 				if (gecos_mapping == NULL) {
540 					gecos_mapping =
541 					    __ns_ldap_getMappedAttributes(
542 					    service, mapping[0]);
543 					if (gecos_mapping == NULL ||
544 					    gecos_mapping[0] == NULL) {
545 						/*
546 						 * this should never happens,
547 						 * syslog the error
548 						 */
549 						(void) sprintf(errstr,
550 						    gettext(
551 						    "Attribute mapping "
552 						    "inconsistency "
553 						    "found for attributes "
554 						    "'%s' and '%s'."),
555 						    mapping[0], attr);
556 						syslog(LOG_ERR, "libsldap: %s",
557 						    errstr);
558 
559 						ber_free(ber, 0);
560 						ber = NULL;
561 						__ns_ldap_freeEntry(ep);
562 						ep = NULL;
563 						__s_api_free2dArray(mapping);
564 						mapping = NULL;
565 						if (gecos_mapping)
566 							__s_api_free2dArray(
567 							    gecos_mapping);
568 						gecos_mapping = NULL;
569 						return (NS_LDAP_INTERNAL);
570 					}
571 				}
572 
573 				/*
574 				 * is this attribute the 1st, 2nd, or
575 				 * 3rd attr in the mapping list?
576 				 */
577 				gecos_attr_matched = FALSE;
578 				for (i = 0; i < 3 && gecos_mapping[i]; i++) {
579 					if (gecos_mapping[i] &&
580 					    strcasecmp(gecos_mapping[i],
581 					    attr) == 0) {
582 						gecos_val_index[i] = j;
583 						gecos_attr_matched = TRUE;
584 						break;
585 					}
586 				}
587 				if (gecos_attr_matched == FALSE) {
588 					/*
589 					 * Not match found.
590 					 * This should never happens,
591 					 * syslog the error
592 					 */
593 					(void) sprintf(errstr,
594 					    gettext(
595 					    "Attribute mapping "
596 					    "inconsistency "
597 					    "found for attributes "
598 					    "'%s' and '%s'."),
599 					    mapping[0], attr);
600 					syslog(LOG_ERR, "libsldap: %s", errstr);
601 
602 					ber_free(ber, 0);
603 					ber = NULL;
604 					__ns_ldap_freeEntry(ep);
605 					ep = NULL;
606 					__s_api_free2dArray(mapping);
607 					mapping = NULL;
608 					__s_api_free2dArray(gecos_mapping);
609 					gecos_mapping = NULL;
610 					return (NS_LDAP_INTERNAL);
611 				}
612 			}
613 			__s_api_free2dArray(mapping);
614 			mapping = NULL;
615 		}
616 
617 		if ((vals = ldap_get_values(ld, e, attr)) != NULL) {
618 
619 			if ((ap[j]->value_count =
620 			    ldap_count_values(vals)) == 0) {
621 				ldap_value_free(vals);
622 				vals = NULL;
623 				continue;
624 			} else {
625 				ap[j]->attrvalue = (char **)
626 				    calloc(ap[j]->value_count+1,
627 				    sizeof (char *));
628 				if (ap[j]->attrvalue == NULL) {
629 					ber_free(ber, 0);
630 					ber = NULL;
631 					__ns_ldap_freeEntry(ep);
632 					ep = NULL;
633 					if (gecos_mapping)
634 						__s_api_free2dArray(
635 						    gecos_mapping);
636 					gecos_mapping = NULL;
637 					return (NS_LDAP_MEMORY);
638 				}
639 			}
640 
641 			/* map object classes if necessary */
642 			if ((flags & NS_LDAP_NOMAP) == 0 &&
643 			    schema_mapping_existed && ap[j]->attrname &&
644 			    strcasecmp(ap[j]->attrname, "objectclass") == 0) {
645 				for (k = 0; k < ap[j]->value_count; k++) {
646 					mapping =
647 					    __ns_ldap_getOrigObjectClass(
648 					    service, vals[k]);
649 
650 					if (mapping == NULL && auto_service)
651 						/*
652 						 * if service == auto_* and no
653 						 * schema mapping found
654 						 * then try automount
655 						 */
656 					mapping =
657 					    __ns_ldap_getOrigObjectClass(
658 					    "automount", vals[k]);
659 
660 					if (mapping == NULL) {
661 						ap[j]->attrvalue[k] =
662 						    strdup(vals[k]);
663 					} else {
664 						ap[j]->attrvalue[k] =
665 						    strdup(mapping[0]);
666 						__s_api_free2dArray(mapping);
667 						mapping = NULL;
668 					}
669 					if (ap[j]->attrvalue[k] == NULL) {
670 						ber_free(ber, 0);
671 						ber = NULL;
672 						__ns_ldap_freeEntry(ep);
673 						ep = NULL;
674 						if (gecos_mapping)
675 							__s_api_free2dArray(
676 							    gecos_mapping);
677 						gecos_mapping = NULL;
678 						return (NS_LDAP_MEMORY);
679 					}
680 				}
681 			} else {
682 				for (k = 0; k < ap[j]->value_count; k++) {
683 					if ((ap[j]->attrvalue[k] =
684 					    strdup(vals[k])) == NULL) {
685 						ber_free(ber, 0);
686 						ber = NULL;
687 						__ns_ldap_freeEntry(ep);
688 						ep = NULL;
689 						if (gecos_mapping)
690 							__s_api_free2dArray(
691 							    gecos_mapping);
692 						gecos_mapping = NULL;
693 						return (NS_LDAP_MEMORY);
694 					}
695 				}
696 			}
697 
698 			ap[j]->attrvalue[k] = NULL;
699 			ldap_value_free(vals);
700 			vals = NULL;
701 		}
702 
703 		ldap_memfree(attr);
704 		attr = NULL;
705 	}
706 
707 	ber_free(ber, 0);
708 	ber = NULL;
709 
710 	if (gecos_mapping) {
711 		__s_api_free2dArray(gecos_mapping);
712 		gecos_mapping = NULL;
713 	}
714 
715 	/* special processing for gecos 1 to up to 3 attribute mapping */
716 	if (schema_mapping_existed && gecos_mapping_existed) {
717 
718 		int	f = -1;
719 
720 		for (i = 0; i < 3; i++) {
721 			k = gecos_val_index[i];
722 
723 			/*
724 			 * f is the index of the first returned
725 			 * attribute which "gecos" attribute mapped to
726 			 */
727 			if (k != -1 && f == -1)
728 				f = k;
729 
730 			if (k != -1 && ap[k]->value_count > 0 &&
731 			    ap[k]->attrvalue[0] &&
732 			    strlen(ap[k]->attrvalue[0]) > 0) {
733 
734 				if (k == f) {
735 					/*
736 					 * Create and fill in the last reserved
737 					 * ap with the data from the "gecos"
738 					 * mapping attributes
739 					 */
740 					ap[nAttrs] = (ns_ldap_attr_t *)
741 					    calloc(1,
742 					    sizeof (ns_ldap_attr_t));
743 					if (ap[nAttrs] == NULL) {
744 						__ns_ldap_freeEntry(ep);
745 						ep = NULL;
746 						return (NS_LDAP_MEMORY);
747 					}
748 					ap[nAttrs]->attrvalue = (char **)calloc(
749 					    2, sizeof (char *));
750 					if (ap[nAttrs]->attrvalue == NULL) {
751 						__ns_ldap_freeEntry(ep);
752 						ep = NULL;
753 						return (NS_LDAP_MEMORY);
754 					}
755 					/* add 1 more for a possible "," */
756 					ap[nAttrs]->attrvalue[0] =
757 					    (char *)calloc(
758 					    strlen(ap[f]->attrvalue[0]) +
759 					    2, 1);
760 					if (ap[nAttrs]->attrvalue[0] == NULL) {
761 						__ns_ldap_freeEntry(ep);
762 						ep = NULL;
763 						return (NS_LDAP_MEMORY);
764 					}
765 					(void) strcpy(ap[nAttrs]->attrvalue[0],
766 					    ap[f]->attrvalue[0]);
767 
768 					ap[nAttrs]->attrname = strdup("gecos");
769 					if (ap[nAttrs]->attrname == NULL) {
770 						__ns_ldap_freeEntry(ep);
771 						ep = NULL;
772 						return (NS_LDAP_MEMORY);
773 					}
774 
775 					ap[nAttrs]->value_count = 1;
776 					ep->attr_count = nAttrs + 1;
777 
778 				} else {
779 					char	*tmp = NULL;
780 
781 					/*
782 					 * realloc to add "," and
783 					 * ap[k]->attrvalue[0]
784 					 */
785 					tmp = (char *)realloc(
786 					    ap[nAttrs]->attrvalue[0],
787 					    strlen(ap[nAttrs]->
788 					    attrvalue[0]) +
789 					    strlen(ap[k]->
790 					    attrvalue[0]) + 2);
791 					if (tmp == NULL) {
792 						__ns_ldap_freeEntry(ep);
793 						ep = NULL;
794 						return (NS_LDAP_MEMORY);
795 					}
796 					ap[nAttrs]->attrvalue[0] = tmp;
797 					(void) strcat(ap[nAttrs]->attrvalue[0],
798 					    ",");
799 					(void) strcat(ap[nAttrs]->attrvalue[0],
800 					    ap[k]->attrvalue[0]);
801 				}
802 			}
803 		}
804 	}
805 
806 	*ret = ep;
807 	return (NS_LDAP_SUCCESS);
808 }
809 
810 static int
811 __s_api_getEntry(ns_ldap_cookie_t *cookie)
812 {
813 	ns_ldap_entry_t	*curEntry = NULL;
814 	int		ret;
815 
816 #ifdef DEBUG
817 	(void) fprintf(stderr, "__s_api_getEntry START\n");
818 #endif
819 
820 	if (cookie->resultMsg == NULL) {
821 		return (NS_LDAP_INVALID_PARAM);
822 	}
823 	ret = __s_api_cvtEntry(cookie->conn->ld, cookie->service,
824 	    cookie->resultMsg, cookie->i_flags,
825 	    &curEntry, &cookie->errorp);
826 	if (ret != NS_LDAP_SUCCESS) {
827 		return (ret);
828 	}
829 
830 	if (cookie->result == NULL) {
831 		cookie->result = (ns_ldap_result_t *)
832 		    calloc(1, sizeof (ns_ldap_result_t));
833 		if (cookie->result == NULL) {
834 			__ns_ldap_freeEntry(curEntry);
835 			curEntry = NULL;
836 			return (NS_LDAP_MEMORY);
837 		}
838 		cookie->result->entry = curEntry;
839 		cookie->nextEntry = curEntry;
840 	} else {
841 		cookie->nextEntry->next = curEntry;
842 		cookie->nextEntry = curEntry;
843 	}
844 	cookie->result->entries_count++;
845 
846 	return (NS_LDAP_SUCCESS);
847 }
848 
849 static int
850 __s_api_get_cachemgr_data(const char *type, const char *from, char **to)
851 {
852 	union {
853 		ldap_data_t	s_d;
854 		char		s_b[DOORBUFFERSIZE];
855 	} space;
856 	ldap_data_t	*sptr;
857 	int		ndata;
858 	int		adata;
859 	int		rc;
860 
861 #ifdef DEBUG
862 	(void) fprintf(stderr, "__s_api_get_cachemgr_data START\n");
863 #endif
864 	/*
865 	 * We are not going to perform DN to domain mapping
866 	 * in the Standalone mode
867 	 */
868 	if (__s_api_isStandalone()) {
869 		return (-1);
870 	}
871 
872 	if (from == NULL || from[0] == '\0' || to == NULL)
873 		return (-1);
874 
875 	*to = NULL;
876 	(void) memset(space.s_b, 0, DOORBUFFERSIZE);
877 
878 	space.s_d.ldap_call.ldap_callnumber = GETCACHE;
879 	(void) snprintf(space.s_d.ldap_call.ldap_u.domainname,
880 	    DOORBUFFERSIZE - sizeof (space.s_d.ldap_call.ldap_callnumber),
881 	    "%s%s%s",
882 	    type,
883 	    DOORLINESEP,
884 	    from);
885 	ndata = sizeof (space);
886 	adata = sizeof (ldap_call_t) +
887 	    strlen(space.s_d.ldap_call.ldap_u.domainname) + 1;
888 	sptr = &space.s_d;
889 
890 	rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
891 	if (rc != NS_CACHE_SUCCESS)
892 		return (-1);
893 	else
894 		*to = strdup(sptr->ldap_ret.ldap_u.buff);
895 	return (NS_LDAP_SUCCESS);
896 }
897 
898 static int
899 __s_api_set_cachemgr_data(const char *type, const char *from, const char *to)
900 {
901 	union {
902 		ldap_data_t	s_d;
903 		char		s_b[DOORBUFFERSIZE];
904 	} space;
905 	ldap_data_t	*sptr;
906 	int		ndata;
907 	int		adata;
908 	int		rc;
909 
910 #ifdef DEBUG
911 	(void) fprintf(stderr, "__s_api_set_cachemgr_data START\n");
912 #endif
913 	/*
914 	 * We are not going to perform DN to domain mapping
915 	 * in the Standalone mode
916 	 */
917 	if (__s_api_isStandalone()) {
918 		return (-1);
919 	}
920 
921 	if ((from == NULL) || (from[0] == '\0') ||
922 	    (to == NULL) || (to[0] == '\0'))
923 		return (-1);
924 
925 	(void) memset(space.s_b, 0, DOORBUFFERSIZE);
926 
927 	space.s_d.ldap_call.ldap_callnumber = SETCACHE;
928 	(void) snprintf(space.s_d.ldap_call.ldap_u.domainname,
929 	    DOORBUFFERSIZE - sizeof (space.s_d.ldap_call.ldap_callnumber),
930 	    "%s%s%s%s%s",
931 	    type,
932 	    DOORLINESEP,
933 	    from,
934 	    DOORLINESEP,
935 	    to);
936 
937 	ndata = sizeof (space);
938 	adata = sizeof (ldap_call_t) +
939 	    strlen(space.s_d.ldap_call.ldap_u.domainname) + 1;
940 	sptr = &space.s_d;
941 
942 	rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata);
943 	if (rc != NS_CACHE_SUCCESS)
944 		return (-1);
945 
946 	return (NS_LDAP_SUCCESS);
947 }
948 
949 
950 static char *
951 __s_api_remove_rdn_space(char *rdn)
952 {
953 	char	*tf, *tl, *vf, *vl, *eqsign;
954 
955 	/* if no space(s) to remove, return */
956 	if (strchr(rdn, SPACETOK) == NULL)
957 		return (rdn);
958 
959 	/* if no '=' separator, return */
960 	eqsign = strchr(rdn, '=');
961 	if (eqsign == NULL)
962 		return (rdn);
963 
964 	tf = rdn;
965 	tl = eqsign - 1;
966 	vf = eqsign + 1;
967 	vl = rdn + strlen(rdn) - 1;
968 
969 	/* now two strings, type and value */
970 	*eqsign = '\0';
971 
972 	/* remove type's leading spaces */
973 	while (tf < tl && *tf == SPACETOK)
974 		tf++;
975 	/* remove type's trailing spaces */
976 	while (tf < tl && *tl == SPACETOK)
977 		tl--;
978 	/* add '=' separator back */
979 	*(++tl) = '=';
980 	/* remove value's leading spaces */
981 	while (vf < vl && *vf == SPACETOK)
982 		vf++;
983 	/* remove value's trailing spaces */
984 	while (vf < vl && *vl == SPACETOK)
985 		*vl-- = '\0';
986 
987 	/* move value up if necessary */
988 	if (vf != tl + 1)
989 		(void) strcpy(tl + 1, vf);
990 
991 	return (tf);
992 }
993 
994 static
995 ns_ldap_cookie_t *
996 init_search_state_machine()
997 {
998 	ns_ldap_cookie_t	*cookie;
999 	ns_config_t		*cfg;
1000 
1001 	cookie = (ns_ldap_cookie_t *)calloc(1, sizeof (ns_ldap_cookie_t));
1002 	if (cookie == NULL)
1003 		return (NULL);
1004 	cookie->state = INIT;
1005 	/* assign other state variables */
1006 	cfg = __s_api_loadrefresh_config();
1007 	cookie->connectionId = -1;
1008 	if (cfg == NULL ||
1009 	    cfg->paramList[NS_LDAP_SEARCH_TIME_P].ns_ptype == NS_UNKNOWN) {
1010 		cookie->search_timeout.tv_sec = NS_DEFAULT_SEARCH_TIMEOUT;
1011 	} else {
1012 		cookie->search_timeout.tv_sec =
1013 		    cfg->paramList[NS_LDAP_SEARCH_TIME_P].ns_i;
1014 	}
1015 	if (cfg != NULL)
1016 		__s_api_release_config(cfg);
1017 	cookie->search_timeout.tv_usec = 0;
1018 
1019 	return (cookie);
1020 }
1021 
1022 static void
1023 delete_search_cookie(ns_ldap_cookie_t *cookie)
1024 {
1025 	if (cookie == NULL)
1026 		return;
1027 	if (cookie->connectionId > -1)
1028 		DropConnection(cookie->connectionId, cookie->i_flags);
1029 	if (cookie->filter)
1030 		free(cookie->filter);
1031 	if (cookie->i_filter)
1032 		free(cookie->i_filter);
1033 	if (cookie->service)
1034 		free(cookie->service);
1035 	if (cookie->sdlist)
1036 		(void) __ns_ldap_freeSearchDescriptors(&(cookie->sdlist));
1037 	if (cookie->result)
1038 		(void) __ns_ldap_freeResult(&cookie->result);
1039 	if (cookie->attribute)
1040 		__s_api_free2dArray(cookie->attribute);
1041 	if (cookie->errorp)
1042 		(void) __ns_ldap_freeError(&cookie->errorp);
1043 	if (cookie->reflist)
1044 		__s_api_deleteRefInfo(cookie->reflist);
1045 	if (cookie->basedn)
1046 		free(cookie->basedn);
1047 	if (cookie->ctrlCookie)
1048 		ber_bvfree(cookie->ctrlCookie);
1049 	_freeControlList(&cookie->p_serverctrls);
1050 	if (cookie->resultctrl)
1051 		ldap_controls_free(cookie->resultctrl);
1052 	free(cookie);
1053 }
1054 
1055 static int
1056 get_mapped_filter(ns_ldap_cookie_t *cookie, char **new_filter)
1057 {
1058 
1059 	typedef	struct	filter_mapping_info {
1060 		char	oc_or_attr;
1061 		char	*name_start;
1062 		char	*name_end;
1063 		char	*veq_pos;
1064 		char	*from_name;
1065 		char	*to_name;
1066 		char	**mapping;
1067 	} filter_mapping_info_t;
1068 
1069 	char			*c, *last_copied;
1070 	char			*filter_c, *filter_c_next;
1071 	char			*key, *tail, *head;
1072 	char			errstr[MAXERROR];
1073 	int			num_eq = 0, num_veq = 0;
1074 	boolean_t		in_quote = B_FALSE;
1075 	boolean_t		is_value = B_FALSE;
1076 	int			i, j, oc_len, len;
1077 	boolean_t		at_least_one = B_FALSE;
1078 	filter_mapping_info_t	**info, *info1;
1079 	char			**mapping;
1080 	char			*service, *filter, *err;
1081 	boolean_t		auto_service = B_FALSE;
1082 
1083 	if (cookie == NULL || new_filter == NULL)
1084 		return (NS_LDAP_INVALID_PARAM);
1085 
1086 	*new_filter = NULL;
1087 	service = cookie->service;
1088 	filter = cookie->filter;
1089 
1090 	/*
1091 	 * count the number of '=' char
1092 	 */
1093 	for (c = filter; *c; c++) {
1094 		if (*c == TOKENSEPARATOR)
1095 			num_eq++;
1096 	}
1097 
1098 	if (service != NULL && strncasecmp(service, "auto_", 5) == 0)
1099 		auto_service = TRUE;
1100 
1101 	/*
1102 	 * See if schema mapping existed for the given service.
1103 	 * If not, just return success.
1104 	 */
1105 	mapping = __ns_ldap_getOrigAttribute(service,
1106 	    NS_HASH_SCHEMA_MAPPING_EXISTED);
1107 
1108 	if (mapping == NULL && auto_service)
1109 		/*
1110 		 * if service == auto_* and no
1111 		 * schema mapping found
1112 		 * then try automount
1113 		 */
1114 		mapping = __ns_ldap_getOrigAttribute(
1115 		    "automount", NS_HASH_SCHEMA_MAPPING_EXISTED);
1116 
1117 	if (mapping)
1118 		__s_api_free2dArray(mapping);
1119 	else
1120 		return (NS_LDAP_SUCCESS);
1121 
1122 	/*
1123 	 * no '=' sign, just say OK and return nothing
1124 	 */
1125 	if (num_eq == 0)
1126 		return (NS_LDAP_SUCCESS);
1127 
1128 	/*
1129 	 * Make a copy of the filter string
1130 	 * for saving the name of the objectclasses or
1131 	 * attributes that need to be passed to the
1132 	 * objectclass or attribute mapping functions.
1133 	 * pointer "info->from_name" points to the locations
1134 	 * within this string.
1135 	 *
1136 	 * The input filter string, filter, will be used
1137 	 * to indicate where these names start and end.
1138 	 * pointers "info->name_start" and "info->name_end"
1139 	 * point to locations within the input filter string,
1140 	 * and are used at the end of this function to
1141 	 * merge the original filter data with the
1142 	 * mapped objectclass or attribute names.
1143 	 */
1144 	filter_c = strdup(filter);
1145 	if (filter_c == NULL)
1146 		return (NS_LDAP_MEMORY);
1147 	filter_c_next = filter_c;
1148 
1149 	/*
1150 	 * get memory for info arrays
1151 	 */
1152 	info = (filter_mapping_info_t **)calloc(num_eq + 1,
1153 	    sizeof (filter_mapping_info_t *));
1154 
1155 	if (info == NULL) {
1156 		free(filter_c);
1157 		return (NS_LDAP_MEMORY);
1158 	}
1159 
1160 	/*
1161 	 * find valid '=' for further processing,
1162 	 * ignore the "escaped =" (.i.e. "\="), or
1163 	 * "=" in quoted string
1164 	 */
1165 	for (c = filter_c; *c; c++) {
1166 
1167 		switch (*c) {
1168 		case TOKENSEPARATOR:
1169 			if (!in_quote && !is_value) {
1170 				info1 = (filter_mapping_info_t *)calloc(1,
1171 				    sizeof (filter_mapping_info_t));
1172 				if (info1 == NULL) {
1173 					free(filter_c);
1174 					for (i = 0; i < num_veq; i++)
1175 						free(info[i]);
1176 					free(info);
1177 					return (NS_LDAP_MEMORY);
1178 				}
1179 				info[num_veq] = info1;
1180 
1181 				/*
1182 				 * remember the location of this "="
1183 				 */
1184 				info[num_veq++]->veq_pos = c;
1185 
1186 				/*
1187 				 * skip until the end of the attribute value
1188 				 */
1189 				is_value = B_TRUE;
1190 			}
1191 			break;
1192 		case CPARATOK:
1193 			/*
1194 			 * mark the end of the attribute value
1195 			 */
1196 			if (!in_quote)
1197 				is_value = B_FALSE;
1198 			break;
1199 		case QUOTETOK:
1200 			/*
1201 			 * switch on/off the in_quote mode
1202 			 */
1203 			in_quote = (in_quote == B_FALSE);
1204 			break;
1205 		case '\\':
1206 			/*
1207 			 * ignore escape characters
1208 			 * don't skip if next char is '\0'
1209 			 */
1210 			if (!in_quote)
1211 				if (*(++c) == '\0')
1212 					c--;
1213 			break;
1214 		}
1215 
1216 	}
1217 
1218 	/*
1219 	 * for each valid "=" found, get the name to
1220 	 * be mapped
1221 	 */
1222 	oc_len = strlen("objectclass");
1223 	for (i = 0; i < num_veq; i++) {
1224 
1225 		/*
1226 		 * look at the left side of "=" to see
1227 		 * if assertion is "objectclass=<ocname>"
1228 		 * or "<attribute name>=<attribute value>"
1229 		 *
1230 		 * first skip spaces before "=".
1231 		 * Note that filter_c_next may not point to the
1232 		 * start of the filter string. For i > 0,
1233 		 * it points to the end of the last name processed + 2
1234 		 */
1235 		for (tail = info[i]->veq_pos; (tail > filter_c_next) &&
1236 		    (*(tail - 1) == SPACETOK); tail--)
1237 			;
1238 
1239 		/*
1240 		 * mark the end of the left side string (the key)
1241 		 */
1242 		*tail = '\0';
1243 		info[i]->name_end = tail - filter_c - 1 + filter;
1244 
1245 		/*
1246 		 * find the start of the key
1247 		 */
1248 		key = filter_c_next;
1249 		for (c = tail; filter_c_next <= c; c--) {
1250 			/* OPARATOK is '(' */
1251 			if (*c == OPARATOK ||
1252 			    *c == SPACETOK) {
1253 				key = c + 1;
1254 				break;
1255 			}
1256 		}
1257 		info[i]->name_start = key - filter_c + filter;
1258 
1259 		if ((key + oc_len) <= tail) {
1260 			if (strncasecmp(key, "objectclass",
1261 			    oc_len) == 0) {
1262 				/*
1263 				 * assertion is "objectclass=ocname",
1264 				 * ocname is the one needs to be mapped
1265 				 *
1266 				 * skip spaces after "=" to find start
1267 				 * of the ocname
1268 				 */
1269 				head = info[i]->veq_pos;
1270 				for (head = info[i]->veq_pos + 1;
1271 				    *head && *head == SPACETOK; head++)
1272 					;
1273 
1274 				/* ignore empty ocname */
1275 				if (!(*head))
1276 					continue;
1277 
1278 				info[i]->name_start = head - filter_c +
1279 				    filter;
1280 
1281 				/*
1282 				 * now find the end of the ocname
1283 				 */
1284 				for (c = head; ; c++) {
1285 					/* CPARATOK is ')' */
1286 					if (*c == CPARATOK ||
1287 					    *c == '\0' ||
1288 					    *c == SPACETOK) {
1289 						*c = '\0';
1290 						info[i]->name_end =
1291 						    c - filter_c - 1 +
1292 						    filter;
1293 						filter_c_next = c + 1;
1294 						info[i]->oc_or_attr = 'o';
1295 						info[i]->from_name = head;
1296 						break;
1297 					}
1298 				}
1299 			}
1300 		}
1301 
1302 		/*
1303 		 * assertion is not "objectclass=ocname",
1304 		 * assume assertion is "<key> = <value>",
1305 		 * <key> is the one needs to be mapped
1306 		 */
1307 		if (info[i]->from_name == NULL && strlen(key) > 0) {
1308 			info[i]->oc_or_attr = 'a';
1309 			info[i]->from_name = key;
1310 		}
1311 	}
1312 
1313 	/* perform schema mapping */
1314 	for (i = 0; i < num_veq; i++) {
1315 		if (info[i]->from_name == NULL)
1316 			continue;
1317 
1318 		if (info[i]->oc_or_attr == 'a')
1319 			info[i]->mapping =
1320 			    __ns_ldap_getMappedAttributes(service,
1321 			    info[i]->from_name);
1322 		else
1323 			info[i]->mapping =
1324 			    __ns_ldap_getMappedObjectClass(service,
1325 			    info[i]->from_name);
1326 
1327 		if (info[i]->mapping == NULL && auto_service)  {
1328 			/*
1329 			 * If no mapped attribute/objectclass is found
1330 			 * and service == auto*
1331 			 * try to find automount's
1332 			 * mapped attribute/objectclass
1333 			 */
1334 			if (info[i]->oc_or_attr == 'a')
1335 				info[i]->mapping =
1336 				    __ns_ldap_getMappedAttributes("automount",
1337 				    info[i]->from_name);
1338 			else
1339 				info[i]->mapping =
1340 				    __ns_ldap_getMappedObjectClass("automount",
1341 				    info[i]->from_name);
1342 		}
1343 
1344 		if (info[i]->mapping == NULL ||
1345 		    info[i]->mapping[0] == NULL) {
1346 			info[i]->to_name = NULL;
1347 		} else if (info[i]->mapping[1] == NULL) {
1348 			info[i]->to_name = info[i]->mapping[0];
1349 			at_least_one = TRUE;
1350 		} else {
1351 			__s_api_free2dArray(info[i]->mapping);
1352 			/*
1353 			 * multiple mapping
1354 			 * not allowed
1355 			 */
1356 			(void) sprintf(errstr,
1357 			    gettext(
1358 			    "Multiple attribute or objectclass "
1359 			    "mapping for '%s' in filter "
1360 			    "'%s' not allowed."),
1361 			    info[i]->from_name, filter);
1362 			err = strdup(errstr);
1363 			if (err) {
1364 				MKERROR(LOG_WARNING, cookie->errorp,
1365 				    NS_CONFIG_SYNTAX,
1366 				    err, NS_LDAP_MEMORY);
1367 			}
1368 
1369 			free(filter_c);
1370 			for (j = 0; j < num_veq; j++) {
1371 				if (info[j]->mapping)
1372 					__s_api_free2dArray(
1373 					    info[j]->mapping);
1374 				free(info[j]);
1375 			}
1376 			free(info);
1377 			return (NS_LDAP_CONFIG);
1378 		}
1379 	}
1380 
1381 
1382 	if (at_least_one) {
1383 
1384 		len = strlen(filter);
1385 		last_copied = filter - 1;
1386 
1387 		for (i = 0; i < num_veq; i++) {
1388 			if (info[i]->to_name)
1389 				len += strlen(info[i]->to_name);
1390 		}
1391 
1392 		*new_filter = (char *)calloc(1, len);
1393 		if (*new_filter == NULL) {
1394 			free(filter_c);
1395 			for (j = 0; j < num_veq; j++) {
1396 				if (info[j]->mapping)
1397 					__s_api_free2dArray(
1398 					    info[j]->mapping);
1399 				free(info[j]);
1400 			}
1401 			free(info);
1402 			return (NS_LDAP_MEMORY);
1403 		}
1404 
1405 		for (i = 0; i < num_veq; i++) {
1406 			if (info[i]->to_name != NULL &&
1407 			    info[i]->to_name != NULL) {
1408 
1409 				/*
1410 				 * copy the original filter data
1411 				 * between the last name and current
1412 				 * name
1413 				 */
1414 				if ((last_copied + 1) != info[i]->name_start)
1415 					(void) strncat(*new_filter,
1416 					    last_copied + 1,
1417 					    info[i]->name_start -
1418 					    last_copied - 1);
1419 
1420 				/* the data is copied */
1421 				last_copied = info[i]->name_end;
1422 
1423 				/*
1424 				 * replace the name with
1425 				 * the mapped name
1426 				 */
1427 				(void) strcat(*new_filter, info[i]->to_name);
1428 			}
1429 
1430 			/* copy the filter data after the last name */
1431 			if (i == (num_veq -1) &&
1432 			    info[i]->name_end <
1433 			    (filter + strlen(filter)))
1434 				(void) strncat(*new_filter, last_copied + 1,
1435 				    filter + strlen(filter) -
1436 				    last_copied - 1);
1437 		}
1438 
1439 	}
1440 
1441 	/* free memory */
1442 	free(filter_c);
1443 	for (j = 0; j < num_veq; j++) {
1444 		if (info[j]->mapping)
1445 			__s_api_free2dArray(info[j]->mapping);
1446 		free(info[j]);
1447 	}
1448 	free(info);
1449 
1450 	return (NS_LDAP_SUCCESS);
1451 }
1452 
1453 static int
1454 setup_next_search(ns_ldap_cookie_t *cookie)
1455 {
1456 	ns_ldap_search_desc_t	*dptr;
1457 	int			scope;
1458 	char			*filter, *str;
1459 	int			baselen;
1460 	int			rc;
1461 	void			**param;
1462 
1463 	dptr = *cookie->sdpos;
1464 	scope = cookie->i_flags & (NS_LDAP_SCOPE_BASE |
1465 	    NS_LDAP_SCOPE_ONELEVEL |
1466 	    NS_LDAP_SCOPE_SUBTREE);
1467 	if (scope)
1468 		cookie->scope = scope;
1469 	else
1470 		cookie->scope = dptr->scope;
1471 	switch (cookie->scope) {
1472 	case NS_LDAP_SCOPE_BASE:
1473 		cookie->scope = LDAP_SCOPE_BASE;
1474 		break;
1475 	case NS_LDAP_SCOPE_ONELEVEL:
1476 		cookie->scope = LDAP_SCOPE_ONELEVEL;
1477 		break;
1478 	case NS_LDAP_SCOPE_SUBTREE:
1479 		cookie->scope = LDAP_SCOPE_SUBTREE;
1480 		break;
1481 	}
1482 
1483 	filter = NULL;
1484 	if (cookie->use_filtercb && cookie->init_filter_cb &&
1485 	    dptr->filter && strlen(dptr->filter) > 0) {
1486 		(*cookie->init_filter_cb)(dptr, &filter,
1487 		    cookie->userdata);
1488 	}
1489 	if (filter == NULL) {
1490 		if (cookie->i_filter == NULL) {
1491 			cookie->err_rc = NS_LDAP_INVALID_PARAM;
1492 			return (-1);
1493 		} else {
1494 			if (cookie->filter)
1495 				free(cookie->filter);
1496 			cookie->filter = strdup(cookie->i_filter);
1497 			if (cookie->filter == NULL) {
1498 				cookie->err_rc = NS_LDAP_MEMORY;
1499 				return (-1);
1500 			}
1501 		}
1502 	} else {
1503 		if (cookie->filter)
1504 			free(cookie->filter);
1505 		cookie->filter = strdup(filter);
1506 		free(filter);
1507 		if (cookie->filter == NULL) {
1508 			cookie->err_rc = NS_LDAP_MEMORY;
1509 			return (-1);
1510 		}
1511 	}
1512 
1513 	/*
1514 	 * perform attribute/objectclass mapping on filter
1515 	 */
1516 	filter = NULL;
1517 
1518 	if (cookie->service) {
1519 		rc = get_mapped_filter(cookie, &filter);
1520 		if (rc != NS_LDAP_SUCCESS) {
1521 			cookie->err_rc = rc;
1522 			return (-1);
1523 		} else {
1524 			/*
1525 			 * get_mapped_filter returns
1526 			 * NULL filter pointer, if
1527 			 * no mapping was done
1528 			 */
1529 			if (filter) {
1530 				free(cookie->filter);
1531 				cookie->filter = filter;
1532 			}
1533 		}
1534 	}
1535 
1536 	/*
1537 	 * validate filter to make sure it's legal
1538 	 * [remove redundant ()'s]
1539 	 */
1540 	rc = validate_filter(cookie);
1541 	if (rc != NS_LDAP_SUCCESS) {
1542 		cookie->err_rc = rc;
1543 		return (-1);
1544 	}
1545 
1546 	baselen = strlen(dptr->basedn);
1547 	if (baselen > 0 && dptr->basedn[baselen-1] == COMMATOK) {
1548 		rc = __ns_ldap_getParam(NS_LDAP_SEARCH_BASEDN_P,
1549 		    (void ***)&param, &cookie->errorp);
1550 		if (rc != NS_LDAP_SUCCESS) {
1551 			cookie->err_rc = rc;
1552 			return (-1);
1553 		}
1554 		str = ((char **)param)[0];
1555 		baselen += strlen(str)+1;
1556 		if (cookie->basedn)
1557 			free(cookie->basedn);
1558 		cookie->basedn = (char *)malloc(baselen);
1559 		if (cookie->basedn == NULL) {
1560 			cookie->err_rc = NS_LDAP_MEMORY;
1561 			return (-1);
1562 		}
1563 		(void) strcpy(cookie->basedn, dptr->basedn);
1564 		(void) strcat(cookie->basedn, str);
1565 		(void) __ns_ldap_freeParam(&param);
1566 	} else {
1567 		if (cookie->basedn)
1568 			free(cookie->basedn);
1569 		cookie->basedn = strdup(dptr->basedn);
1570 	}
1571 	return (0);
1572 }
1573 
1574 static int
1575 setup_referral_search(ns_ldap_cookie_t *cookie)
1576 {
1577 	ns_referral_info_t	*ref;
1578 
1579 	ref = cookie->refpos;
1580 	cookie->scope = ref->refScope;
1581 	if (cookie->filter) {
1582 		free(cookie->filter);
1583 	}
1584 	cookie->filter = strdup(ref->refFilter);
1585 	if (cookie->basedn) {
1586 		free(cookie->basedn);
1587 	}
1588 	cookie->basedn = strdup(ref->refDN);
1589 	if (cookie->filter == NULL || cookie->basedn == NULL) {
1590 		cookie->err_rc = NS_LDAP_MEMORY;
1591 		return (-1);
1592 	}
1593 	return (0);
1594 }
1595 
1596 static int
1597 get_current_session(ns_ldap_cookie_t *cookie)
1598 {
1599 	ConnectionID	connectionId = -1;
1600 	Connection	*conp = NULL;
1601 	int		rc;
1602 	int		fail_if_new_pwd_reqd = 1;
1603 
1604 	rc = __s_api_getConnection(NULL, cookie->i_flags,
1605 	    cookie->i_auth, &connectionId, &conp,
1606 	    &cookie->errorp, fail_if_new_pwd_reqd,
1607 	    cookie->nopasswd_acct_mgmt, cookie->conn_user);
1608 
1609 	/*
1610 	 * If password control attached in *cookie->errorp,
1611 	 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1612 	 * free the error structure (we do not need
1613 	 * the sec_to_expired info).
1614 	 * Reset rc to NS_LDAP_SUCCESS.
1615 	 */
1616 	if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1617 		(void) __ns_ldap_freeError(
1618 		    &cookie->errorp);
1619 		cookie->errorp = NULL;
1620 		rc = NS_LDAP_SUCCESS;
1621 	}
1622 
1623 	if (rc != NS_LDAP_SUCCESS) {
1624 		cookie->err_rc = rc;
1625 		return (-1);
1626 	}
1627 	cookie->conn = conp;
1628 	cookie->connectionId = connectionId;
1629 
1630 	return (0);
1631 }
1632 
1633 static int
1634 get_next_session(ns_ldap_cookie_t *cookie)
1635 {
1636 	ConnectionID	connectionId = -1;
1637 	Connection	*conp = NULL;
1638 	int		rc;
1639 	int		fail_if_new_pwd_reqd = 1;
1640 
1641 	if (cookie->connectionId > -1) {
1642 		DropConnection(cookie->connectionId, cookie->i_flags);
1643 		cookie->connectionId = -1;
1644 	}
1645 
1646 	/* If using a MT connection, return it. */
1647 	if (cookie->conn_user != NULL &&
1648 	    cookie->conn_user->conn_mt != NULL)
1649 		__s_api_conn_mt_return(cookie->conn_user);
1650 
1651 	rc = __s_api_getConnection(NULL, cookie->i_flags,
1652 	    cookie->i_auth, &connectionId, &conp,
1653 	    &cookie->errorp, fail_if_new_pwd_reqd,
1654 	    cookie->nopasswd_acct_mgmt, cookie->conn_user);
1655 
1656 	/*
1657 	 * If password control attached in *cookie->errorp,
1658 	 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1659 	 * free the error structure (we do not need
1660 	 * the sec_to_expired info).
1661 	 * Reset rc to NS_LDAP_SUCCESS.
1662 	 */
1663 	if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1664 		(void) __ns_ldap_freeError(
1665 		    &cookie->errorp);
1666 		cookie->errorp = NULL;
1667 		rc = NS_LDAP_SUCCESS;
1668 	}
1669 
1670 	if (rc != NS_LDAP_SUCCESS) {
1671 		cookie->err_rc = rc;
1672 		return (-1);
1673 	}
1674 	cookie->conn = conp;
1675 	cookie->connectionId = connectionId;
1676 	return (0);
1677 }
1678 
1679 static int
1680 get_referral_session(ns_ldap_cookie_t *cookie)
1681 {
1682 	ConnectionID	connectionId = -1;
1683 	Connection	*conp = NULL;
1684 	int		rc;
1685 	int		fail_if_new_pwd_reqd = 1;
1686 
1687 	if (cookie->connectionId > -1) {
1688 		DropConnection(cookie->connectionId, cookie->i_flags);
1689 		cookie->connectionId = -1;
1690 	}
1691 
1692 	/* set it up to use a connection opened for referral */
1693 	if (cookie->conn_user != NULL) {
1694 		/* If using a MT connection, return it. */
1695 		if (cookie->conn_user->conn_mt != NULL)
1696 			__s_api_conn_mt_return(cookie->conn_user);
1697 		cookie->conn_user->referral = B_TRUE;
1698 	}
1699 
1700 	rc = __s_api_getConnection(cookie->refpos->refHost, 0,
1701 	    cookie->i_auth, &connectionId, &conp,
1702 	    &cookie->errorp, fail_if_new_pwd_reqd,
1703 	    cookie->nopasswd_acct_mgmt, cookie->conn_user);
1704 
1705 	/*
1706 	 * If password control attached in *cookie->errorp,
1707 	 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1708 	 * free the error structure (we do not need
1709 	 * the sec_to_expired info).
1710 	 * Reset rc to NS_LDAP_SUCCESS.
1711 	 */
1712 	if (rc == NS_LDAP_SUCCESS_WITH_INFO) {
1713 		(void) __ns_ldap_freeError(
1714 		    &cookie->errorp);
1715 		cookie->errorp = NULL;
1716 		rc = NS_LDAP_SUCCESS;
1717 	}
1718 
1719 	if (rc != NS_LDAP_SUCCESS) {
1720 		cookie->err_rc = rc;
1721 		return (-1);
1722 	}
1723 	cookie->conn = conp;
1724 	cookie->connectionId = connectionId;
1725 	return (0);
1726 }
1727 
1728 static int
1729 paging_supported(ns_ldap_cookie_t *cookie)
1730 {
1731 	int		rc;
1732 
1733 	cookie->listType = 0;
1734 	rc = __s_api_isCtrlSupported(cookie->conn,
1735 	    LDAP_CONTROL_VLVREQUEST);
1736 	if (rc == NS_LDAP_SUCCESS) {
1737 		cookie->listType = VLVCTRLFLAG;
1738 		return (1);
1739 	}
1740 	rc = __s_api_isCtrlSupported(cookie->conn,
1741 	    LDAP_CONTROL_SIMPLE_PAGE);
1742 	if (rc == NS_LDAP_SUCCESS) {
1743 		cookie->listType = SIMPLEPAGECTRLFLAG;
1744 		return (1);
1745 	}
1746 	return (0);
1747 }
1748 
1749 typedef struct servicesorttype {
1750 	char *service;
1751 	ns_srvsidesort_t type;
1752 } servicesorttype_t;
1753 
1754 static servicesorttype_t *sort_type = NULL;
1755 static int sort_type_size = 0;
1756 static int sort_type_hwm = 0;
1757 static mutex_t sort_type_mutex = DEFAULTMUTEX;
1758 
1759 
1760 static ns_srvsidesort_t
1761 get_srvsidesort_type(char *service)
1762 {
1763 	int i;
1764 	ns_srvsidesort_t type = SSS_UNKNOWN;
1765 
1766 	if (service == NULL)
1767 		return (type);
1768 
1769 	(void) mutex_lock(&sort_type_mutex);
1770 	if (sort_type != NULL) {
1771 		for (i = 0; i < sort_type_hwm; i++) {
1772 			if (strcmp(sort_type[i].service, service) == 0) {
1773 				type = sort_type[i].type;
1774 				break;
1775 			}
1776 		}
1777 	}
1778 	(void) mutex_unlock(&sort_type_mutex);
1779 	return (type);
1780 }
1781 
1782 static void
1783 update_srvsidesort_type(char *service, ns_srvsidesort_t type)
1784 {
1785 	int i, size;
1786 	servicesorttype_t *tmp;
1787 
1788 	if (service == NULL)
1789 		return;
1790 
1791 	(void) mutex_lock(&sort_type_mutex);
1792 
1793 	for (i = 0; i < sort_type_hwm; i++) {
1794 		if (strcmp(sort_type[i].service, service) == 0) {
1795 			sort_type[i].type = type;
1796 			(void) mutex_unlock(&sort_type_mutex);
1797 			return;
1798 		}
1799 	}
1800 	if (sort_type == NULL) {
1801 		size = 10;
1802 		tmp = malloc(size * sizeof (servicesorttype_t));
1803 		if (tmp == NULL) {
1804 			(void) mutex_unlock(&sort_type_mutex);
1805 			return;
1806 		}
1807 		sort_type = tmp;
1808 		sort_type_size = size;
1809 	} else if (sort_type_hwm >= sort_type_size) {
1810 		size = sort_type_size + 10;
1811 		tmp = realloc(sort_type, size * sizeof (servicesorttype_t));
1812 		if (tmp == NULL) {
1813 			(void) mutex_unlock(&sort_type_mutex);
1814 			return;
1815 		}
1816 		sort_type = tmp;
1817 		sort_type_size = size;
1818 	}
1819 	sort_type[sort_type_hwm].service = strdup(service);
1820 	if (sort_type[sort_type_hwm].service == NULL) {
1821 		(void) mutex_unlock(&sort_type_mutex);
1822 		return;
1823 	}
1824 	sort_type[sort_type_hwm].type = type;
1825 	sort_type_hwm++;
1826 
1827 	(void) mutex_unlock(&sort_type_mutex);
1828 }
1829 
1830 static int
1831 setup_vlv_params(ns_ldap_cookie_t *cookie)
1832 {
1833 	LDAPControl	**ctrls;
1834 	LDAPsortkey	**sortkeylist;
1835 	LDAPControl	*sortctrl = NULL;
1836 	LDAPControl	*vlvctrl = NULL;
1837 	LDAPVirtualList	vlist;
1838 	char		*sortattr;
1839 	int		rc;
1840 	int		free_sort = FALSE;
1841 
1842 	_freeControlList(&cookie->p_serverctrls);
1843 
1844 	if (cookie->sortTypeTry == SSS_UNKNOWN)
1845 		cookie->sortTypeTry = get_srvsidesort_type(cookie->service);
1846 	if (cookie->sortTypeTry == SSS_UNKNOWN)
1847 		cookie->sortTypeTry = SSS_SINGLE_ATTR;
1848 
1849 	if (cookie->sortTypeTry == SSS_SINGLE_ATTR) {
1850 		if ((cookie->i_flags & NS_LDAP_NOMAP) == 0 &&
1851 		    cookie->i_sortattr) {
1852 			sortattr =  __ns_ldap_mapAttribute(cookie->service,
1853 			    cookie->i_sortattr);
1854 			free_sort = TRUE;
1855 		} else if (cookie->i_sortattr) {
1856 			sortattr = (char *)cookie->i_sortattr;
1857 		} else {
1858 			sortattr = "cn";
1859 		}
1860 	} else {
1861 		sortattr = "cn uid";
1862 	}
1863 
1864 	rc = ldap_create_sort_keylist(&sortkeylist, sortattr);
1865 	if (free_sort)
1866 		free(sortattr);
1867 	if (rc != LDAP_SUCCESS) {
1868 		(void) ldap_get_option(cookie->conn->ld,
1869 		    LDAP_OPT_ERROR_NUMBER, &rc);
1870 		return (rc);
1871 	}
1872 	rc = ldap_create_sort_control(cookie->conn->ld,
1873 	    sortkeylist, 1, &sortctrl);
1874 	ldap_free_sort_keylist(sortkeylist);
1875 	if (rc != LDAP_SUCCESS) {
1876 		(void) ldap_get_option(cookie->conn->ld,
1877 		    LDAP_OPT_ERROR_NUMBER, &rc);
1878 		return (rc);
1879 	}
1880 
1881 	vlist.ldvlist_index = cookie->index;
1882 	vlist.ldvlist_size = 0;
1883 
1884 	vlist.ldvlist_before_count = 0;
1885 	vlist.ldvlist_after_count = LISTPAGESIZE-1;
1886 	vlist.ldvlist_attrvalue = NULL;
1887 	vlist.ldvlist_extradata = NULL;
1888 
1889 	rc = ldap_create_virtuallist_control(cookie->conn->ld,
1890 	    &vlist, &vlvctrl);
1891 	if (rc != LDAP_SUCCESS) {
1892 		ldap_control_free(sortctrl);
1893 		(void) ldap_get_option(cookie->conn->ld, LDAP_OPT_ERROR_NUMBER,
1894 		    &rc);
1895 		return (rc);
1896 	}
1897 
1898 	ctrls = (LDAPControl **)calloc(3, sizeof (LDAPControl *));
1899 	if (ctrls == NULL) {
1900 		ldap_control_free(sortctrl);
1901 		ldap_control_free(vlvctrl);
1902 		return (LDAP_NO_MEMORY);
1903 	}
1904 
1905 	ctrls[0] = sortctrl;
1906 	ctrls[1] = vlvctrl;
1907 
1908 	cookie->p_serverctrls = ctrls;
1909 	return (LDAP_SUCCESS);
1910 }
1911 
1912 static int
1913 setup_simplepg_params(ns_ldap_cookie_t *cookie)
1914 {
1915 	LDAPControl	**ctrls;
1916 	LDAPControl	*pgctrl = NULL;
1917 	int		rc;
1918 
1919 	_freeControlList(&cookie->p_serverctrls);
1920 
1921 	rc = ldap_create_page_control(cookie->conn->ld, LISTPAGESIZE,
1922 	    cookie->ctrlCookie, (char)0, &pgctrl);
1923 	if (rc != LDAP_SUCCESS) {
1924 		(void) ldap_get_option(cookie->conn->ld, LDAP_OPT_ERROR_NUMBER,
1925 		    &rc);
1926 		return (rc);
1927 	}
1928 
1929 	ctrls = (LDAPControl **)calloc(2, sizeof (LDAPControl *));
1930 	if (ctrls == NULL) {
1931 		ldap_control_free(pgctrl);
1932 		return (LDAP_NO_MEMORY);
1933 	}
1934 	ctrls[0] = pgctrl;
1935 	cookie->p_serverctrls = ctrls;
1936 	return (LDAP_SUCCESS);
1937 }
1938 
1939 static void
1940 proc_result_referrals(ns_ldap_cookie_t *cookie)
1941 {
1942 	int		errCode, i, rc;
1943 	char		**referrals = NULL;
1944 
1945 	/*
1946 	 * Only follow one level of referrals, i.e.
1947 	 * if already in referral mode, do nothing
1948 	 */
1949 	if (cookie->refpos == NULL) {
1950 		cookie->new_state = END_RESULT;
1951 		rc = ldap_parse_result(cookie->conn->ld,
1952 		    cookie->resultMsg,
1953 		    &errCode, NULL,
1954 		    NULL, &referrals,
1955 		    NULL, 0);
1956 		if (rc != NS_LDAP_SUCCESS) {
1957 			(void) ldap_get_option(cookie->conn->ld,
1958 			    LDAP_OPT_ERROR_NUMBER,
1959 			    &cookie->err_rc);
1960 			cookie->new_state = LDAP_ERROR;
1961 			return;
1962 		}
1963 		if (errCode == LDAP_REFERRAL) {
1964 			for (i = 0; referrals[i] != NULL;
1965 			    i++) {
1966 				/* add to referral list */
1967 				rc = __s_api_addRefInfo(
1968 				    &cookie->reflist,
1969 				    referrals[i],
1970 				    cookie->basedn,
1971 				    &cookie->scope,
1972 				    cookie->filter,
1973 				    cookie->conn->ld);
1974 				if (rc != NS_LDAP_SUCCESS) {
1975 					cookie->new_state =
1976 					    ERROR;
1977 					break;
1978 				}
1979 			}
1980 			ldap_value_free(referrals);
1981 		}
1982 	}
1983 }
1984 
1985 static void
1986 proc_search_references(ns_ldap_cookie_t *cookie)
1987 {
1988 	char		**refurls = NULL;
1989 	int		i, rc;
1990 
1991 	/*
1992 	 * Only follow one level of referrals, i.e.
1993 	 * if already in referral mode, do nothing
1994 	 */
1995 	if (cookie->refpos == NULL) {
1996 		refurls = ldap_get_reference_urls(
1997 		    cookie->conn->ld,
1998 		    cookie->resultMsg);
1999 		if (refurls == NULL) {
2000 			(void) ldap_get_option(cookie->conn->ld,
2001 			    LDAP_OPT_ERROR_NUMBER,
2002 			    &cookie->err_rc);
2003 			cookie->new_state = LDAP_ERROR;
2004 			return;
2005 		}
2006 		for (i = 0; refurls[i] != NULL; i++) {
2007 			/* add to referral list */
2008 			rc = __s_api_addRefInfo(
2009 			    &cookie->reflist,
2010 			    refurls[i],
2011 			    cookie->basedn,
2012 			    &cookie->scope,
2013 			    cookie->filter,
2014 			    cookie->conn->ld);
2015 			if (rc != NS_LDAP_SUCCESS) {
2016 				cookie->new_state =
2017 				    ERROR;
2018 				break;
2019 			}
2020 		}
2021 		/* free allocated storage */
2022 		for (i = 0; refurls[i] != NULL; i++)
2023 			free(refurls[i]);
2024 	}
2025 }
2026 
2027 static ns_state_t
2028 multi_result(ns_ldap_cookie_t *cookie)
2029 {
2030 	char		errstr[MAXERROR];
2031 	char		*err;
2032 	ns_ldap_error_t **errorp = NULL;
2033 	LDAPControl	**retCtrls = NULL;
2034 	int		i, rc;
2035 	int		errCode;
2036 	boolean_t	finished = B_FALSE;
2037 	unsigned long	target_posp = 0;
2038 	unsigned long	list_size = 0;
2039 	unsigned int	count = 0;
2040 	char		**referrals = NULL;
2041 
2042 	if (cookie->listType == VLVCTRLFLAG) {
2043 		rc = ldap_parse_result(cookie->conn->ld, cookie->resultMsg,
2044 		    &errCode, NULL, NULL, &referrals, &retCtrls, 0);
2045 		if (rc != LDAP_SUCCESS) {
2046 			(void) ldap_get_option(cookie->conn->ld,
2047 			    LDAP_OPT_ERROR_NUMBER,
2048 			    &cookie->err_rc);
2049 			(void) sprintf(errstr,
2050 			    gettext("LDAP ERROR (%d): %s.\n"),
2051 			    cookie->err_rc,
2052 			    gettext(ldap_err2string(cookie->err_rc)));
2053 			err = strdup(errstr);
2054 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2055 			    LDAP_ERROR);
2056 			cookie->err_rc = NS_LDAP_INTERNAL;
2057 			cookie->errorp = *errorp;
2058 			return (LDAP_ERROR);
2059 		}
2060 		if (errCode == LDAP_REFERRAL) {
2061 			for (i = 0; referrals[i] != NULL;
2062 			    i++) {
2063 				/* add to referral list */
2064 				rc = __s_api_addRefInfo(
2065 				    &cookie->reflist,
2066 				    referrals[i],
2067 				    cookie->basedn,
2068 				    &cookie->scope,
2069 				    cookie->filter,
2070 				    cookie->conn->ld);
2071 				if (rc != NS_LDAP_SUCCESS) {
2072 					ldap_value_free(
2073 					    referrals);
2074 					if (retCtrls)
2075 						ldap_controls_free(
2076 						    retCtrls);
2077 					return (ERROR);
2078 				}
2079 			}
2080 			ldap_value_free(referrals);
2081 			if (retCtrls)
2082 				ldap_controls_free(retCtrls);
2083 			return (END_RESULT);
2084 		}
2085 		if (retCtrls) {
2086 			rc = ldap_parse_virtuallist_control(
2087 			    cookie->conn->ld, retCtrls,
2088 			    &target_posp, &list_size, &errCode);
2089 			if (rc == LDAP_SUCCESS) {
2090 				/*
2091 				 * AD does not return valid target_posp
2092 				 * and list_size
2093 				 */
2094 				if (target_posp != 0 && list_size != 0) {
2095 					cookie->index =
2096 					    target_posp + LISTPAGESIZE;
2097 					if (cookie->index > list_size)
2098 						finished = B_TRUE;
2099 				} else {
2100 					if (cookie->entryCount < LISTPAGESIZE)
2101 						finished = B_TRUE;
2102 					else
2103 						cookie->index +=
2104 						    cookie->entryCount;
2105 				}
2106 			}
2107 			ldap_controls_free(retCtrls);
2108 			retCtrls = NULL;
2109 		} else {
2110 			finished = B_TRUE;
2111 		}
2112 	} else if (cookie->listType == SIMPLEPAGECTRLFLAG) {
2113 		rc = ldap_parse_result(cookie->conn->ld, cookie->resultMsg,
2114 		    &errCode, NULL, NULL, &referrals, &retCtrls, 0);
2115 		if (rc != LDAP_SUCCESS) {
2116 			(void) ldap_get_option(cookie->conn->ld,
2117 			    LDAP_OPT_ERROR_NUMBER,
2118 			    &cookie->err_rc);
2119 			(void) sprintf(errstr,
2120 			    gettext("LDAP ERROR (%d): %s.\n"),
2121 			    cookie->err_rc,
2122 			    gettext(ldap_err2string(cookie->err_rc)));
2123 			err = strdup(errstr);
2124 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2125 			    LDAP_ERROR);
2126 			cookie->err_rc = NS_LDAP_INTERNAL;
2127 			cookie->errorp = *errorp;
2128 			return (LDAP_ERROR);
2129 		}
2130 		if (errCode == LDAP_REFERRAL) {
2131 			for (i = 0; referrals[i] != NULL;
2132 			    i++) {
2133 				/* add to referral list */
2134 				rc = __s_api_addRefInfo(
2135 				    &cookie->reflist,
2136 				    referrals[i],
2137 				    cookie->basedn,
2138 				    &cookie->scope,
2139 				    cookie->filter,
2140 				    cookie->conn->ld);
2141 				if (rc != NS_LDAP_SUCCESS) {
2142 					ldap_value_free(
2143 					    referrals);
2144 					if (retCtrls)
2145 						ldap_controls_free(
2146 						    retCtrls);
2147 					return (ERROR);
2148 				}
2149 			}
2150 			ldap_value_free(referrals);
2151 			if (retCtrls)
2152 				ldap_controls_free(retCtrls);
2153 			return (END_RESULT);
2154 		}
2155 		if (retCtrls) {
2156 			if (cookie->ctrlCookie)
2157 				ber_bvfree(cookie->ctrlCookie);
2158 			cookie->ctrlCookie = NULL;
2159 			rc = ldap_parse_page_control(
2160 			    cookie->conn->ld, retCtrls,
2161 			    &count, &cookie->ctrlCookie);
2162 			if (rc == LDAP_SUCCESS) {
2163 				if ((cookie->ctrlCookie == NULL) ||
2164 				    (cookie->ctrlCookie->bv_val == NULL) ||
2165 				    (cookie->ctrlCookie->bv_len == 0))
2166 					finished = B_TRUE;
2167 			}
2168 			ldap_controls_free(retCtrls);
2169 			retCtrls = NULL;
2170 		} else {
2171 			finished = B_TRUE;
2172 		}
2173 	}
2174 	if (!finished && cookie->listType == VLVCTRLFLAG)
2175 		return (NEXT_VLV);
2176 	if (!finished && cookie->listType == SIMPLEPAGECTRLFLAG)
2177 		return (NEXT_PAGE);
2178 	if (finished)
2179 		return (END_RESULT);
2180 	return (ERROR);
2181 }
2182 
2183 /*
2184  * clear_results(ns_ldap_cookie_t):
2185  *
2186  * Attempt to obtain remnants of ldap responses and free them.  If remnants are
2187  * not obtained within a certain time period tell the server we wish to abandon
2188  * the request.
2189  *
2190  * Note that we do not initially tell the server to abandon the request as that
2191  * can be an expensive operation for the server, while it is cheap for us to
2192  * just flush the input.
2193  *
2194  * If something was to remain in libldap queue as a result of some error then
2195  * it would be freed later during drop connection call or when no other
2196  * requests share the connection.
2197  */
2198 static void
2199 clear_results(ns_ldap_cookie_t *cookie)
2200 {
2201 	int rc;
2202 	if (cookie->conn != NULL && cookie->conn->ld != NULL &&
2203 	    (cookie->connectionId != -1 ||
2204 	    (cookie->conn_user != NULL &&
2205 	    cookie->conn_user->conn_mt != NULL)) &&
2206 	    cookie->msgId != 0) {
2207 		/*
2208 		 * We need to cleanup the rest of response (if there is such)
2209 		 * and LDAP abandon is too heavy for LDAP servers, so we will
2210 		 * wait for the rest of response till timeout and "process" it.
2211 		 */
2212 		rc = ldap_result(cookie->conn->ld, cookie->msgId, LDAP_MSG_ALL,
2213 		    (struct timeval *)&cookie->search_timeout,
2214 		    &cookie->resultMsg);
2215 		if (rc != -1 && rc != 0 && cookie->resultMsg != NULL) {
2216 			(void) ldap_msgfree(cookie->resultMsg);
2217 			cookie->resultMsg = NULL;
2218 		}
2219 
2220 		/*
2221 		 * If there was timeout then we will send  ABANDON request to
2222 		 * LDAP server to decrease load.
2223 		 */
2224 		if (rc == 0)
2225 			(void) ldap_abandon_ext(cookie->conn->ld, cookie->msgId,
2226 			    NULL, NULL);
2227 		/* Disassociate cookie with msgId */
2228 		cookie->msgId = 0;
2229 	}
2230 }
2231 
2232 /*
2233  * This state machine performs one or more LDAP searches to a given
2234  * directory server using service search descriptors and schema
2235  * mapping as appropriate.  The approximate pseudocode for
2236  * this routine is the following:
2237  *    Given the current configuration [set/reset connection etc.]
2238  *    and the current service search descriptor list
2239  *        or default search filter parameters
2240  *    foreach (service search filter) {
2241  *        initialize the filter [via filter_init if appropriate]
2242  *		  get a valid session/connection (preferably the current one)
2243  *					Recover if the connection is lost
2244  *        perform the search
2245  *        foreach (result entry) {
2246  *            process result [via callback if appropriate]
2247  *                save result for caller if accepted.
2248  *                exit and return all collected if allResults found;
2249  *        }
2250  *    }
2251  *    return collected results and exit
2252  */
2253 
2254 static
2255 ns_state_t
2256 search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
2257 {
2258 	char		errstr[MAXERROR];
2259 	char		*err;
2260 	int		rc, ret;
2261 	int		rc_save;
2262 	ns_ldap_entry_t	*nextEntry;
2263 	ns_ldap_error_t *error = NULL;
2264 	ns_ldap_error_t **errorp;
2265 	struct timeval	tv;
2266 
2267 	errorp = &error;
2268 	cookie->state = state;
2269 	errstr[0] = '\0';
2270 
2271 	for (;;) {
2272 		switch (cookie->state) {
2273 		case CLEAR_RESULTS:
2274 			clear_results(cookie);
2275 			cookie->new_state = EXIT;
2276 			break;
2277 		case GET_ACCT_MGMT_INFO:
2278 			/*
2279 			 * Set the flag to get ldap account management controls.
2280 			 */
2281 			cookie->nopasswd_acct_mgmt = 1;
2282 			cookie->new_state = INIT;
2283 			break;
2284 		case EXIT:
2285 			/* state engine/connection cleaned up in delete */
2286 			if (cookie->attribute) {
2287 				__s_api_free2dArray(cookie->attribute);
2288 				cookie->attribute = NULL;
2289 			}
2290 			if (cookie->reflist) {
2291 				__s_api_deleteRefInfo(cookie->reflist);
2292 				cookie->reflist = NULL;
2293 			}
2294 			return (EXIT);
2295 		case INIT:
2296 			cookie->sdpos = NULL;
2297 			cookie->new_state = NEXT_SEARCH_DESCRIPTOR;
2298 			if (cookie->attribute) {
2299 				__s_api_free2dArray(cookie->attribute);
2300 				cookie->attribute = NULL;
2301 			}
2302 			if ((cookie->i_flags & NS_LDAP_NOMAP) == 0 &&
2303 			    cookie->i_attr) {
2304 				cookie->attribute =
2305 				    __ns_ldap_mapAttributeList(
2306 				    cookie->service,
2307 				    cookie->i_attr);
2308 			}
2309 			break;
2310 		case REINIT:
2311 			/* Check if we've reached MAX retries. */
2312 			cookie->retries++;
2313 			if (cookie->retries > NS_LIST_TRY_MAX - 1) {
2314 				cookie->new_state = LDAP_ERROR;
2315 				break;
2316 			}
2317 
2318 			/*
2319 			 * Even if we still have retries left, check
2320 			 * if retry is possible.
2321 			 */
2322 			if (cookie->conn_user != NULL) {
2323 				int		retry;
2324 				ns_conn_mgmt_t	*cmg;
2325 				cmg = cookie->conn_user->conn_mgmt;
2326 				retry = cookie->conn_user->retry;
2327 				if (cmg != NULL && cmg->cfg_reloaded == 1)
2328 					retry = 1;
2329 				if (retry == 0) {
2330 					cookie->new_state = LDAP_ERROR;
2331 					break;
2332 				}
2333 			}
2334 			/*
2335 			 * Free results if any, reset to the first
2336 			 * search descriptor and start a new session.
2337 			 */
2338 			if (cookie->resultMsg != NULL) {
2339 				(void) ldap_msgfree(cookie->resultMsg);
2340 				cookie->resultMsg = NULL;
2341 			}
2342 			(void) __ns_ldap_freeError(&cookie->errorp);
2343 			(void) __ns_ldap_freeResult(&cookie->result);
2344 			cookie->sdpos = cookie->sdlist;
2345 			cookie->err_from_result = 0;
2346 			cookie->err_rc = 0;
2347 			cookie->new_state = NEXT_SESSION;
2348 			break;
2349 		case NEXT_SEARCH_DESCRIPTOR:
2350 			/* get next search descriptor */
2351 			if (cookie->sdpos == NULL) {
2352 				cookie->sdpos = cookie->sdlist;
2353 				cookie->new_state = GET_SESSION;
2354 			} else {
2355 				cookie->sdpos++;
2356 				cookie->new_state = NEXT_SEARCH;
2357 			}
2358 			if (*cookie->sdpos == NULL)
2359 				cookie->new_state = EXIT;
2360 			break;
2361 		case GET_SESSION:
2362 			if (get_current_session(cookie) < 0)
2363 				cookie->new_state = NEXT_SESSION;
2364 			else
2365 				cookie->new_state = NEXT_SEARCH;
2366 			break;
2367 		case NEXT_SESSION:
2368 			if (get_next_session(cookie) < 0)
2369 				cookie->new_state = RESTART_SESSION;
2370 			else
2371 				cookie->new_state = NEXT_SEARCH;
2372 			break;
2373 		case RESTART_SESSION:
2374 			if (cookie->i_flags & NS_LDAP_HARD) {
2375 				cookie->new_state = NEXT_SESSION;
2376 				break;
2377 			}
2378 			(void) sprintf(errstr,
2379 			    gettext("Session error no available conn.\n"),
2380 			    state);
2381 			err = strdup(errstr);
2382 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2383 			    LDAP_ERROR);
2384 			cookie->err_rc = NS_LDAP_INTERNAL;
2385 			cookie->errorp = *errorp;
2386 			cookie->new_state = EXIT;
2387 			break;
2388 		case NEXT_SEARCH:
2389 			/* setup referrals search if necessary */
2390 			if (cookie->refpos) {
2391 				if (setup_referral_search(cookie) < 0) {
2392 					cookie->new_state = EXIT;
2393 					break;
2394 				}
2395 			} else if (setup_next_search(cookie) < 0) {
2396 				cookie->new_state = EXIT;
2397 				break;
2398 			}
2399 			/* only do VLV/PAGE on scopes onelevel/subtree */
2400 			if (paging_supported(cookie)) {
2401 				if (cookie->use_paging &&
2402 				    (cookie->scope != LDAP_SCOPE_BASE)) {
2403 					cookie->index = 1;
2404 					if (cookie->listType == VLVCTRLFLAG)
2405 						cookie->new_state = NEXT_VLV;
2406 					else
2407 						cookie->new_state = NEXT_PAGE;
2408 					break;
2409 				}
2410 			}
2411 			cookie->new_state = ONE_SEARCH;
2412 			break;
2413 		case NEXT_VLV:
2414 			rc = setup_vlv_params(cookie);
2415 			if (rc != LDAP_SUCCESS) {
2416 				cookie->err_rc = rc;
2417 				cookie->new_state = LDAP_ERROR;
2418 				break;
2419 			}
2420 			cookie->next_state = MULTI_RESULT;
2421 			cookie->new_state = DO_SEARCH;
2422 			break;
2423 		case NEXT_PAGE:
2424 			rc = setup_simplepg_params(cookie);
2425 			if (rc != LDAP_SUCCESS) {
2426 				cookie->err_rc = rc;
2427 				cookie->new_state = LDAP_ERROR;
2428 				break;
2429 			}
2430 			cookie->next_state = MULTI_RESULT;
2431 			cookie->new_state = DO_SEARCH;
2432 			break;
2433 		case ONE_SEARCH:
2434 			cookie->next_state = NEXT_RESULT;
2435 			cookie->new_state = DO_SEARCH;
2436 			break;
2437 		case DO_SEARCH:
2438 			cookie->entryCount = 0;
2439 			rc = ldap_search_ext(cookie->conn->ld,
2440 			    cookie->basedn,
2441 			    cookie->scope,
2442 			    cookie->filter,
2443 			    cookie->attribute,
2444 			    0,
2445 			    cookie->p_serverctrls,
2446 			    NULL,
2447 			    &cookie->search_timeout, 0,
2448 			    &cookie->msgId);
2449 			if (rc != LDAP_SUCCESS) {
2450 				if (rc == LDAP_BUSY ||
2451 				    rc == LDAP_UNAVAILABLE ||
2452 				    rc == LDAP_UNWILLING_TO_PERFORM ||
2453 				    rc == LDAP_CONNECT_ERROR ||
2454 				    rc == LDAP_SERVER_DOWN) {
2455 
2456 					if (cookie->reinit_on_retriable_err) {
2457 						cookie->err_rc = rc;
2458 						cookie->new_state = REINIT;
2459 					} else {
2460 						cookie->new_state =
2461 						    NEXT_SESSION;
2462 					}
2463 
2464 					/*
2465 					 * If not able to reach the
2466 					 * server, inform the ldap
2467 					 * cache manager that the
2468 					 * server should be removed
2469 					 * from it's server list.
2470 					 * Thus, the manager will not
2471 					 * return this server on the next
2472 					 * get-server request and will
2473 					 * also reduce the server list
2474 					 * refresh TTL, so that it will
2475 					 * find out sooner when the server
2476 					 * is up again.
2477 					 */
2478 					if ((rc == LDAP_CONNECT_ERROR ||
2479 					    rc == LDAP_SERVER_DOWN) &&
2480 					    (cookie->conn_user == NULL ||
2481 					    cookie->conn_user->conn_mt ==
2482 					    NULL)) {
2483 						ret = __s_api_removeServer(
2484 						    cookie->conn->serverAddr);
2485 						if (ret == NS_CACHE_NOSERVER &&
2486 						    cookie->conn_auth_type
2487 						    == NS_LDAP_AUTH_NONE) {
2488 							/*
2489 							 * Couldn't remove
2490 							 * server from server
2491 							 * list.
2492 							 * Exit to avoid
2493 							 * potential infinite
2494 							 * loop.
2495 							 */
2496 							cookie->err_rc = rc;
2497 							cookie->new_state =
2498 							    LDAP_ERROR;
2499 						}
2500 						if (cookie->connectionId > -1) {
2501 							/*
2502 							 * NS_LDAP_NEW_CONN
2503 							 * indicates that the
2504 							 * connection should
2505 							 * be deleted, not
2506 							 * kept alive
2507 							 */
2508 							DropConnection(
2509 							    cookie->
2510 							    connectionId,
2511 							    NS_LDAP_NEW_CONN);
2512 							cookie->connectionId =
2513 							    -1;
2514 						}
2515 					} else if ((rc == LDAP_CONNECT_ERROR ||
2516 					    rc == LDAP_SERVER_DOWN) &&
2517 					    cookie->conn_user != NULL) {
2518 						if (cookie->
2519 						    reinit_on_retriable_err) {
2520 							/*
2521 							 * MT connection not
2522 							 * usable, close it
2523 							 * before REINIT.
2524 							 * rc has already
2525 							 * been saved in
2526 							 * cookie->err_rc above.
2527 							 */
2528 							__s_api_conn_mt_close(
2529 							    cookie->conn_user,
2530 							    rc,
2531 							    &cookie->errorp);
2532 						} else {
2533 							/*
2534 							 * MT connection not
2535 							 * usable, close it in
2536 							 * the LDAP_ERROR state.
2537 							 * A retry will be done
2538 							 * next if allowed.
2539 							 */
2540 							cookie->err_rc = rc;
2541 							cookie->new_state =
2542 							    LDAP_ERROR;
2543 						}
2544 					}
2545 					break;
2546 				}
2547 				cookie->err_rc = rc;
2548 				cookie->new_state = LDAP_ERROR;
2549 				break;
2550 			}
2551 			cookie->new_state = cookie->next_state;
2552 			break;
2553 		case NEXT_RESULT:
2554 			/*
2555 			 * Caller (e.g. __ns_ldap_list_batch_add)
2556 			 * does not want to block on ldap_result().
2557 			 * Therefore we execute ldap_result() with
2558 			 * a zeroed timeval.
2559 			 */
2560 			if (cookie->no_wait == B_TRUE)
2561 				(void) memset(&tv, 0, sizeof (tv));
2562 			else
2563 				tv = cookie->search_timeout;
2564 			rc = ldap_result(cookie->conn->ld, cookie->msgId,
2565 			    LDAP_MSG_ONE,
2566 			    &tv,
2567 			    &cookie->resultMsg);
2568 			if (rc == LDAP_RES_SEARCH_RESULT) {
2569 				cookie->new_state = END_RESULT;
2570 				/* check and process referrals info */
2571 				if (cookie->followRef)
2572 					proc_result_referrals(
2573 					    cookie);
2574 				(void) ldap_msgfree(cookie->resultMsg);
2575 				cookie->resultMsg = NULL;
2576 				break;
2577 			}
2578 			/* handle referrals if necessary */
2579 			if (rc == LDAP_RES_SEARCH_REFERENCE) {
2580 				if (cookie->followRef)
2581 					proc_search_references(cookie);
2582 				(void) ldap_msgfree(cookie->resultMsg);
2583 				cookie->resultMsg = NULL;
2584 				break;
2585 			}
2586 			if (rc != LDAP_RES_SEARCH_ENTRY) {
2587 				switch (rc) {
2588 				case 0:
2589 					if (cookie->no_wait == B_TRUE) {
2590 						(void) ldap_msgfree(
2591 						    cookie->resultMsg);
2592 						cookie->resultMsg = NULL;
2593 						return (cookie->new_state);
2594 					}
2595 					rc = LDAP_TIMEOUT;
2596 					break;
2597 				case -1:
2598 					rc = ldap_get_lderrno(cookie->conn->ld,
2599 					    NULL, NULL);
2600 					break;
2601 				default:
2602 					rc = ldap_result2error(cookie->conn->ld,
2603 					    cookie->resultMsg, 1);
2604 					break;
2605 				}
2606 				if ((rc == LDAP_TIMEOUT ||
2607 				    rc == LDAP_SERVER_DOWN) &&
2608 				    (cookie->conn_user == NULL ||
2609 				    cookie->conn_user->conn_mt == NULL)) {
2610 					if (rc == LDAP_TIMEOUT)
2611 						(void) __s_api_removeServer(
2612 						    cookie->conn->serverAddr);
2613 					if (cookie->connectionId > -1) {
2614 						DropConnection(
2615 						    cookie->connectionId,
2616 						    NS_LDAP_NEW_CONN);
2617 						cookie->connectionId = -1;
2618 					}
2619 					cookie->err_from_result = 1;
2620 				}
2621 				(void) ldap_msgfree(cookie->resultMsg);
2622 				cookie->resultMsg = NULL;
2623 				if (rc == LDAP_BUSY ||
2624 				    rc == LDAP_UNAVAILABLE ||
2625 				    rc == LDAP_UNWILLING_TO_PERFORM) {
2626 					if (cookie->reinit_on_retriable_err) {
2627 						cookie->err_rc = rc;
2628 						cookie->err_from_result = 1;
2629 						cookie->new_state = REINIT;
2630 					} else {
2631 						cookie->new_state =
2632 						    NEXT_SESSION;
2633 					}
2634 					break;
2635 				}
2636 				if ((rc == LDAP_CONNECT_ERROR ||
2637 				    rc == LDAP_SERVER_DOWN) &&
2638 				    cookie->reinit_on_retriable_err) {
2639 					ns_ldap_error_t *errorp = NULL;
2640 					cookie->err_rc = rc;
2641 					cookie->err_from_result = 1;
2642 					cookie->new_state = REINIT;
2643 					if (cookie->conn_user != NULL)
2644 						__s_api_conn_mt_close(
2645 						    cookie->conn_user,
2646 						    rc, &errorp);
2647 					if (errorp != NULL) {
2648 						(void) __ns_ldap_freeError(
2649 						    &cookie->errorp);
2650 						cookie->errorp = errorp;
2651 					}
2652 					break;
2653 				}
2654 				cookie->err_rc = rc;
2655 				cookie->new_state = LDAP_ERROR;
2656 				break;
2657 			}
2658 			/* else LDAP_RES_SEARCH_ENTRY */
2659 			/* get account management response control */
2660 			if (cookie->nopasswd_acct_mgmt == 1) {
2661 				rc = ldap_get_entry_controls(cookie->conn->ld,
2662 				    cookie->resultMsg,
2663 				    &(cookie->resultctrl));
2664 				if (rc != LDAP_SUCCESS) {
2665 					cookie->new_state = LDAP_ERROR;
2666 					cookie->err_rc = rc;
2667 					break;
2668 				}
2669 			}
2670 			rc = __s_api_getEntry(cookie);
2671 			(void) ldap_msgfree(cookie->resultMsg);
2672 			cookie->resultMsg = NULL;
2673 			if (rc != NS_LDAP_SUCCESS) {
2674 				cookie->new_state = LDAP_ERROR;
2675 				break;
2676 			}
2677 			cookie->new_state = PROCESS_RESULT;
2678 			cookie->next_state = NEXT_RESULT;
2679 			break;
2680 		case MULTI_RESULT:
2681 			if (cookie->no_wait == B_TRUE)
2682 				(void) memset(&tv, 0, sizeof (tv));
2683 			else
2684 				tv = cookie->search_timeout;
2685 			rc = ldap_result(cookie->conn->ld, cookie->msgId,
2686 			    LDAP_MSG_ONE,
2687 			    &tv,
2688 			    &cookie->resultMsg);
2689 			if (rc == LDAP_RES_SEARCH_RESULT) {
2690 				rc = ldap_result2error(cookie->conn->ld,
2691 				    cookie->resultMsg, 0);
2692 				if (rc == LDAP_ADMINLIMIT_EXCEEDED &&
2693 				    cookie->listType == VLVCTRLFLAG &&
2694 				    cookie->sortTypeTry == SSS_SINGLE_ATTR) {
2695 					/* Try old "cn uid" server side sort */
2696 					cookie->sortTypeTry = SSS_CN_UID_ATTRS;
2697 					cookie->new_state = NEXT_VLV;
2698 					(void) ldap_msgfree(cookie->resultMsg);
2699 					cookie->resultMsg = NULL;
2700 					break;
2701 				}
2702 				if (rc != LDAP_SUCCESS) {
2703 					cookie->err_rc = rc;
2704 					cookie->new_state = LDAP_ERROR;
2705 					(void) ldap_msgfree(cookie->resultMsg);
2706 					cookie->resultMsg = NULL;
2707 					break;
2708 				}
2709 				cookie->new_state = multi_result(cookie);
2710 				(void) ldap_msgfree(cookie->resultMsg);
2711 				cookie->resultMsg = NULL;
2712 				break;
2713 			}
2714 			/* handle referrals if necessary */
2715 			if (rc == LDAP_RES_SEARCH_REFERENCE &&
2716 			    cookie->followRef) {
2717 				proc_search_references(cookie);
2718 				(void) ldap_msgfree(cookie->resultMsg);
2719 				cookie->resultMsg = NULL;
2720 				break;
2721 			}
2722 			if (rc != LDAP_RES_SEARCH_ENTRY) {
2723 				switch (rc) {
2724 				case 0:
2725 					if (cookie->no_wait == B_TRUE) {
2726 						(void) ldap_msgfree(
2727 						    cookie->resultMsg);
2728 						cookie->resultMsg = NULL;
2729 						return (cookie->new_state);
2730 					}
2731 					rc = LDAP_TIMEOUT;
2732 					break;
2733 				case -1:
2734 					rc = ldap_get_lderrno(cookie->conn->ld,
2735 					    NULL, NULL);
2736 					break;
2737 				default:
2738 					rc = ldap_result2error(cookie->conn->ld,
2739 					    cookie->resultMsg, 1);
2740 					break;
2741 				}
2742 				if ((rc == LDAP_TIMEOUT ||
2743 				    rc == LDAP_SERVER_DOWN) &&
2744 				    (cookie->conn_user == NULL ||
2745 				    cookie->conn_user->conn_mt == NULL)) {
2746 					if (rc == LDAP_TIMEOUT)
2747 						(void) __s_api_removeServer(
2748 						    cookie->conn->serverAddr);
2749 					if (cookie->connectionId > -1) {
2750 						DropConnection(
2751 						    cookie->connectionId,
2752 						    NS_LDAP_NEW_CONN);
2753 						cookie->connectionId = -1;
2754 					}
2755 					cookie->err_from_result = 1;
2756 				}
2757 				(void) ldap_msgfree(cookie->resultMsg);
2758 				cookie->resultMsg = NULL;
2759 				if (rc == LDAP_BUSY ||
2760 				    rc == LDAP_UNAVAILABLE ||
2761 				    rc == LDAP_UNWILLING_TO_PERFORM) {
2762 					if (cookie->reinit_on_retriable_err) {
2763 						cookie->err_rc = rc;
2764 						cookie->err_from_result = 1;
2765 						cookie->new_state = REINIT;
2766 					} else {
2767 						cookie->new_state =
2768 						    NEXT_SESSION;
2769 					}
2770 					break;
2771 				}
2772 
2773 				if ((rc == LDAP_CONNECT_ERROR ||
2774 				    rc == LDAP_SERVER_DOWN) &&
2775 				    cookie->reinit_on_retriable_err) {
2776 					ns_ldap_error_t *errorp = NULL;
2777 					cookie->err_rc = rc;
2778 					cookie->err_from_result = 1;
2779 					cookie->new_state = REINIT;
2780 					if (cookie->conn_user != NULL)
2781 						__s_api_conn_mt_close(
2782 						    cookie->conn_user,
2783 						    rc, &errorp);
2784 					if (errorp != NULL) {
2785 						(void) __ns_ldap_freeError(
2786 						    &cookie->errorp);
2787 						cookie->errorp = errorp;
2788 					}
2789 					break;
2790 				}
2791 				cookie->err_rc = rc;
2792 				cookie->new_state = LDAP_ERROR;
2793 				break;
2794 			}
2795 			/* else LDAP_RES_SEARCH_ENTRY */
2796 			cookie->entryCount++;
2797 			rc = __s_api_getEntry(cookie);
2798 			(void) ldap_msgfree(cookie->resultMsg);
2799 			cookie->resultMsg = NULL;
2800 			if (rc != NS_LDAP_SUCCESS) {
2801 				cookie->new_state = LDAP_ERROR;
2802 				break;
2803 			}
2804 			/*
2805 			 * If VLV search was successfull save the server
2806 			 * side sort type tried.
2807 			 */
2808 			if (cookie->listType == VLVCTRLFLAG)
2809 				update_srvsidesort_type(cookie->service,
2810 				    cookie->sortTypeTry);
2811 
2812 			cookie->new_state = PROCESS_RESULT;
2813 			cookie->next_state = MULTI_RESULT;
2814 			break;
2815 		case PROCESS_RESULT:
2816 			/* NOTE THIS STATE MAY BE PROCESSED BY CALLER */
2817 			if (cookie->use_usercb && cookie->callback) {
2818 				rc = 0;
2819 				for (nextEntry = cookie->result->entry;
2820 				    nextEntry != NULL;
2821 				    nextEntry = nextEntry->next) {
2822 					rc = (*cookie->callback)(nextEntry,
2823 					    cookie->userdata);
2824 
2825 					if (rc == NS_LDAP_CB_DONE) {
2826 					/* cb doesn't want any more data */
2827 						rc = NS_LDAP_PARTIAL;
2828 						cookie->err_rc = rc;
2829 						break;
2830 					} else if (rc != NS_LDAP_CB_NEXT) {
2831 					/* invalid return code */
2832 						rc = NS_LDAP_OP_FAILED;
2833 						cookie->err_rc = rc;
2834 						break;
2835 					}
2836 				}
2837 				(void) __ns_ldap_freeResult(&cookie->result);
2838 				cookie->result = NULL;
2839 			}
2840 			if (rc != 0) {
2841 				cookie->new_state = EXIT;
2842 				break;
2843 			}
2844 			/* NOTE PREVIOUS STATE SPECIFIES NEXT STATE */
2845 			cookie->new_state = cookie->next_state;
2846 			break;
2847 		case END_PROCESS_RESULT:
2848 			cookie->new_state = cookie->next_state;
2849 			break;
2850 		case END_RESULT:
2851 			/*
2852 			 * XXX DO WE NEED THIS CASE?
2853 			 * if (search is complete) {
2854 			 *	cookie->new_state = EXIT;
2855 			 * } else
2856 			 */
2857 				/*
2858 				 * entering referral mode if necessary
2859 				 */
2860 				if (cookie->followRef && cookie->reflist)
2861 					cookie->new_state =
2862 					    NEXT_REFERRAL;
2863 				else
2864 					cookie->new_state =
2865 					    NEXT_SEARCH_DESCRIPTOR;
2866 			break;
2867 		case NEXT_REFERRAL:
2868 			/* get next referral info */
2869 			if (cookie->refpos == NULL)
2870 				cookie->refpos =
2871 				    cookie->reflist;
2872 			else
2873 				cookie->refpos =
2874 				    cookie->refpos->next;
2875 			/* check see if done with all referrals */
2876 			if (cookie->refpos != NULL) {
2877 				cookie->new_state =
2878 				    GET_REFERRAL_SESSION;
2879 			} else {
2880 				__s_api_deleteRefInfo(cookie->reflist);
2881 				cookie->reflist = NULL;
2882 				cookie->new_state =
2883 				    NEXT_SEARCH_DESCRIPTOR;
2884 				if (cookie->conn_user != NULL)
2885 					cookie->conn_user->referral = B_FALSE;
2886 			}
2887 			break;
2888 		case GET_REFERRAL_SESSION:
2889 			if (get_referral_session(cookie) < 0) {
2890 				cookie->new_state = EXIT;
2891 			} else {
2892 				cookie->new_state = NEXT_SEARCH;
2893 			}
2894 			break;
2895 		case LDAP_ERROR:
2896 			rc_save = cookie->err_rc;
2897 			if (cookie->err_from_result) {
2898 				if (cookie->err_rc == LDAP_SERVER_DOWN) {
2899 					(void) sprintf(errstr,
2900 					    gettext("LDAP ERROR (%d): "
2901 					    "Error occurred during"
2902 					    " receiving results. "
2903 					    "Connection to server lost."),
2904 					    cookie->err_rc);
2905 				} else if (cookie->err_rc == LDAP_TIMEOUT) {
2906 					(void) sprintf(errstr,
2907 					    gettext("LDAP ERROR (%d): "
2908 					    "Error occurred during"
2909 					    " receiving results. %s"
2910 					    "."), cookie->err_rc,
2911 					    ldap_err2string(
2912 					    cookie->err_rc));
2913 				}
2914 			} else {
2915 				(void) sprintf(errstr,
2916 				    gettext("LDAP ERROR (%d): %s."),
2917 				    cookie->err_rc,
2918 				    ldap_err2string(cookie->err_rc));
2919 			}
2920 			err = strdup(errstr);
2921 			if (cookie->err_from_result) {
2922 				if (cookie->err_rc == LDAP_SERVER_DOWN) {
2923 					MKERROR(LOG_INFO, *errorp,
2924 					    cookie->err_rc, err,
2925 					    LDAP_ERROR);
2926 				} else {
2927 					MKERROR(LOG_WARNING, *errorp,
2928 					    cookie->err_rc, err,
2929 					    LDAP_ERROR);
2930 				}
2931 			} else {
2932 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2933 				    err, LDAP_ERROR);
2934 			}
2935 			cookie->err_rc = NS_LDAP_INTERNAL;
2936 			cookie->errorp = *errorp;
2937 			if (cookie->conn_user != NULL)  {
2938 				if (rc_save == LDAP_SERVER_DOWN ||
2939 				    rc_save == LDAP_CONNECT_ERROR) {
2940 					/*
2941 					 * MT connection is not usable,
2942 					 * close it.
2943 					 */
2944 					__s_api_conn_mt_close(cookie->conn_user,
2945 					    rc_save, &cookie->errorp);
2946 					return (ERROR);
2947 				}
2948 			}
2949 			return (ERROR);
2950 		default:
2951 		case ERROR:
2952 			(void) sprintf(errstr,
2953 			    gettext("Internal State machine exit (%d).\n"),
2954 			    cookie->state);
2955 			err = strdup(errstr);
2956 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2957 			    LDAP_ERROR);
2958 			cookie->err_rc = NS_LDAP_INTERNAL;
2959 			cookie->errorp = *errorp;
2960 			return (ERROR);
2961 		}
2962 
2963 		if (cookie->conn_user != NULL &&
2964 		    cookie->conn_user->bad_mt_conn ==  B_TRUE) {
2965 			__s_api_conn_mt_close(cookie->conn_user, 0, NULL);
2966 			cookie->err_rc = cookie->conn_user->ns_rc;
2967 			cookie->errorp = cookie->conn_user->ns_error;
2968 			cookie->conn_user->ns_error = NULL;
2969 			return (ERROR);
2970 		}
2971 
2972 		if (cycle == ONE_STEP) {
2973 			return (cookie->new_state);
2974 		}
2975 		cookie->state = cookie->new_state;
2976 	}
2977 	/*NOTREACHED*/
2978 #if 0
2979 	(void) sprintf(errstr,
2980 	    gettext("Unexpected State machine error.\n"));
2981 	err = strdup(errstr);
2982 	MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err, NS_LDAP_MEMORY);
2983 	cookie->err_rc = NS_LDAP_INTERNAL;
2984 	cookie->errorp = *errorp;
2985 	return (ERROR);
2986 #endif
2987 }
2988 
2989 /*
2990  * For a lookup of shadow data, if shadow update is enabled,
2991  * check the calling process' privilege to ensure it's
2992  * allowed to perform such operation.
2993  */
2994 static int
2995 check_shadow(ns_ldap_cookie_t *cookie, const char *service)
2996 {
2997 	char errstr[MAXERROR];
2998 	char *err;
2999 	boolean_t priv;
3000 	/* caller */
3001 	priv_set_t *ps;
3002 	/* zone */
3003 	priv_set_t *zs;
3004 
3005 	/*
3006 	 * If service is "shadow", we may need
3007 	 * to use privilege credentials.
3008 	 */
3009 	if ((strcmp(service, "shadow") == 0) &&
3010 	    __ns_ldap_is_shadow_update_enabled()) {
3011 		/*
3012 		 * Since we release admin credentials after
3013 		 * connection is closed and we do not cache
3014 		 * them, we allow any root or all zone
3015 		 * privilege process to read shadow data.
3016 		 */
3017 		priv = (geteuid() == 0);
3018 		if (!priv) {
3019 			/* caller */
3020 			ps = priv_allocset();
3021 
3022 			(void) getppriv(PRIV_EFFECTIVE, ps);
3023 			zs = priv_str_to_set("zone", ",", NULL);
3024 			priv = priv_isequalset(ps, zs);
3025 			priv_freeset(ps);
3026 			priv_freeset(zs);
3027 		}
3028 		if (!priv) {
3029 			(void) sprintf(errstr,
3030 			    gettext("Permission denied"));
3031 			err = strdup(errstr);
3032 			if (err == NULL)
3033 				return (NS_LDAP_MEMORY);
3034 			MKERROR(LOG_INFO, cookie->errorp, NS_LDAP_INTERNAL, err,
3035 			    NS_LDAP_MEMORY);
3036 			return (NS_LDAP_INTERNAL);
3037 		}
3038 		cookie->i_flags |= NS_LDAP_READ_SHADOW;
3039 		/*
3040 		 * We do not want to reuse connection (hence
3041 		 * keep it open) with admin credentials.
3042 		 * If NS_LDAP_KEEP_CONN is set, reject the
3043 		 * request.
3044 		 */
3045 		if (cookie->i_flags & NS_LDAP_KEEP_CONN)
3046 			return (NS_LDAP_INVALID_PARAM);
3047 		cookie->i_flags |= NS_LDAP_NEW_CONN;
3048 	}
3049 
3050 	return (NS_LDAP_SUCCESS);
3051 }
3052 
3053 /*
3054  * internal function for __ns_ldap_list
3055  */
3056 static int
3057 ldap_list(
3058 	ns_ldap_list_batch_t *batch,
3059 	const char *service,
3060 	const char *filter,
3061 	const char *sortattr,
3062 	int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3063 	char **realfilter, const void *userdata),
3064 	const char * const *attribute,
3065 	const ns_cred_t *auth,
3066 	const int flags,
3067 	ns_ldap_result_t **rResult, /* return result entries */
3068 	ns_ldap_error_t **errorp,
3069 	int *rcp,
3070 	int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3071 	const void *userdata, ns_conn_user_t *conn_user)
3072 {
3073 	ns_ldap_cookie_t	*cookie;
3074 	ns_ldap_search_desc_t	**sdlist = NULL;
3075 	ns_ldap_search_desc_t	*dptr;
3076 	ns_ldap_error_t		*error = NULL;
3077 	char			**dns = NULL;
3078 	int			scope;
3079 	int			rc;
3080 	int			from_result;
3081 
3082 	*errorp = NULL;
3083 	*rResult = NULL;
3084 	*rcp = NS_LDAP_SUCCESS;
3085 
3086 	/*
3087 	 * Sanity check - NS_LDAP_READ_SHADOW is for our
3088 	 * own internal use.
3089 	 */
3090 	if (flags & NS_LDAP_READ_SHADOW)
3091 		return (NS_LDAP_INVALID_PARAM);
3092 
3093 	/* Initialize State machine cookie */
3094 	cookie = init_search_state_machine();
3095 	if (cookie == NULL) {
3096 		*rcp = NS_LDAP_MEMORY;
3097 		return (NS_LDAP_MEMORY);
3098 	}
3099 	cookie->conn_user = conn_user;
3100 
3101 	/* see if need to follow referrals */
3102 	rc = __s_api_toFollowReferrals(flags,
3103 	    &cookie->followRef, errorp);
3104 	if (rc != NS_LDAP_SUCCESS) {
3105 		delete_search_cookie(cookie);
3106 		*rcp = rc;
3107 		return (rc);
3108 	}
3109 
3110 	/* get the service descriptor - or create a default one */
3111 	rc = __s_api_get_SSD_from_SSDtoUse_service(service,
3112 	    &sdlist, &error);
3113 	if (rc != NS_LDAP_SUCCESS) {
3114 		delete_search_cookie(cookie);
3115 		*errorp = error;
3116 		*rcp = rc;
3117 		return (rc);
3118 	}
3119 
3120 	if (sdlist == NULL) {
3121 		/* Create default service Desc */
3122 		sdlist = (ns_ldap_search_desc_t **)calloc(2,
3123 		    sizeof (ns_ldap_search_desc_t *));
3124 		if (sdlist == NULL) {
3125 			delete_search_cookie(cookie);
3126 			cookie = NULL;
3127 			*rcp = NS_LDAP_MEMORY;
3128 			return (NS_LDAP_MEMORY);
3129 		}
3130 		dptr = (ns_ldap_search_desc_t *)
3131 		    calloc(1, sizeof (ns_ldap_search_desc_t));
3132 		if (dptr == NULL) {
3133 			free(sdlist);
3134 			delete_search_cookie(cookie);
3135 			cookie = NULL;
3136 			*rcp = NS_LDAP_MEMORY;
3137 			return (NS_LDAP_MEMORY);
3138 		}
3139 		sdlist[0] = dptr;
3140 
3141 		/* default base */
3142 		rc = __s_api_getDNs(&dns, service, &cookie->errorp);
3143 		if (rc != NS_LDAP_SUCCESS) {
3144 			if (dns) {
3145 				__s_api_free2dArray(dns);
3146 				dns = NULL;
3147 			}
3148 			*errorp = cookie->errorp;
3149 			cookie->errorp = NULL;
3150 			delete_search_cookie(cookie);
3151 			cookie = NULL;
3152 			*rcp = rc;
3153 			return (rc);
3154 		}
3155 		dptr->basedn = strdup(dns[0]);
3156 		__s_api_free2dArray(dns);
3157 		dns = NULL;
3158 
3159 		/* default scope */
3160 		scope = 0;
3161 		rc = __s_api_getSearchScope(&scope, &cookie->errorp);
3162 		dptr->scope = scope;
3163 	}
3164 
3165 	cookie->sdlist = sdlist;
3166 
3167 	/*
3168 	 * use VLV/PAGE control only if NS_LDAP_PAGE_CTRL is set
3169 	 */
3170 	if (flags & NS_LDAP_PAGE_CTRL)
3171 		cookie->use_paging = TRUE;
3172 	else
3173 		cookie->use_paging = FALSE;
3174 
3175 	/* Set up other arguments */
3176 	cookie->userdata = userdata;
3177 	if (init_filter_cb != NULL) {
3178 		cookie->init_filter_cb = init_filter_cb;
3179 		cookie->use_filtercb = 1;
3180 	}
3181 	if (callback != NULL) {
3182 		cookie->callback = callback;
3183 		cookie->use_usercb = 1;
3184 	}
3185 
3186 	/* check_shadow() may add extra value to cookie->i_flags */
3187 	cookie->i_flags = flags;
3188 	if (service) {
3189 		cookie->service = strdup(service);
3190 		if (cookie->service == NULL) {
3191 			delete_search_cookie(cookie);
3192 			cookie = NULL;
3193 			*rcp = NS_LDAP_MEMORY;
3194 			return (NS_LDAP_MEMORY);
3195 		}
3196 
3197 		/*
3198 		 * If given, use the credential given by the caller, and
3199 		 * skip the credential check required for shadow update.
3200 		 */
3201 		if (auth == NULL) {
3202 			rc = check_shadow(cookie, service);
3203 			if (rc != NS_LDAP_SUCCESS) {
3204 				*errorp = cookie->errorp;
3205 				cookie->errorp = NULL;
3206 				delete_search_cookie(cookie);
3207 				cookie = NULL;
3208 				*rcp = rc;
3209 				return (rc);
3210 			}
3211 		}
3212 	}
3213 
3214 	cookie->i_filter = strdup(filter);
3215 	cookie->i_attr = attribute;
3216 	cookie->i_auth = auth;
3217 	cookie->i_sortattr = sortattr;
3218 
3219 	if (batch != NULL) {
3220 		cookie->batch = batch;
3221 		cookie->reinit_on_retriable_err = B_TRUE;
3222 		cookie->no_wait = B_TRUE;
3223 		(void) search_state_machine(cookie, INIT, 0);
3224 		cookie->no_wait = B_FALSE;
3225 		rc = cookie->err_rc;
3226 
3227 		if (rc == NS_LDAP_SUCCESS) {
3228 			/*
3229 			 * Here rc == NS_LDAP_SUCCESS means that the state
3230 			 * machine init'ed successfully. The actual status
3231 			 * of the search will be determined by
3232 			 * __ns_ldap_list_batch_end(). Add the cookie to our
3233 			 * batch.
3234 			 */
3235 			cookie->caller_result = rResult;
3236 			cookie->caller_errorp = errorp;
3237 			cookie->caller_rc = rcp;
3238 			cookie->next_cookie_in_batch = batch->cookie_list;
3239 			batch->cookie_list = cookie;
3240 			batch->nactive++;
3241 			return (rc);
3242 		}
3243 		/*
3244 		 * If state machine init failed then copy error to the caller
3245 		 * and delete the cookie.
3246 		 */
3247 	} else {
3248 		(void) search_state_machine(cookie, INIT, 0);
3249 	}
3250 
3251 	/* Copy results back to user */
3252 	rc = cookie->err_rc;
3253 	if (rc != NS_LDAP_SUCCESS) {
3254 		if (conn_user != NULL && conn_user->ns_error != NULL) {
3255 			*errorp = conn_user->ns_error;
3256 			conn_user->ns_error = NULL;
3257 		} else {
3258 			*errorp = cookie->errorp;
3259 		}
3260 	}
3261 	*rResult = cookie->result;
3262 	from_result = cookie->err_from_result;
3263 
3264 	cookie->errorp = NULL;
3265 	cookie->result = NULL;
3266 	delete_search_cookie(cookie);
3267 	cookie = NULL;
3268 
3269 	if (from_result == 0 && *rResult == NULL)
3270 		rc = NS_LDAP_NOTFOUND;
3271 	*rcp = rc;
3272 	return (rc);
3273 }
3274 
3275 
3276 /*
3277  * __ns_ldap_list performs one or more LDAP searches to a given
3278  * directory server using service search descriptors and schema
3279  * mapping as appropriate. The operation may be retried a
3280  * couple of times in error situations.
3281  */
3282 int
3283 __ns_ldap_list(
3284 	const char *service,
3285 	const char *filter,
3286 	int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3287 	char **realfilter, const void *userdata),
3288 	const char * const *attribute,
3289 	const ns_cred_t *auth,
3290 	const int flags,
3291 	ns_ldap_result_t **rResult, /* return result entries */
3292 	ns_ldap_error_t **errorp,
3293 	int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3294 	const void *userdata)
3295 {
3296 	int mod_flags;
3297 	/*
3298 	 * Strip the NS_LDAP_PAGE_CTRL option as this interface does not
3299 	 * support this. If you want to use this option call the API
3300 	 * __ns_ldap_list_sort() with has the sort attribute.
3301 	 */
3302 	mod_flags = flags & (~NS_LDAP_PAGE_CTRL);
3303 
3304 	return (__ns_ldap_list_sort(service, filter, NULL, init_filter_cb,
3305 	    attribute, auth, mod_flags, rResult, errorp,
3306 	    callback, userdata));
3307 }
3308 
3309 /*
3310  * __ns_ldap_list_sort performs one or more LDAP searches to a given
3311  * directory server using service search descriptors and schema
3312  * mapping as appropriate. The operation may be retried a
3313  * couple of times in error situations.
3314  */
3315 int
3316 __ns_ldap_list_sort(
3317 	const char *service,
3318 	const char *filter,
3319 	const char *sortattr,
3320 	int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3321 	char **realfilter, const void *userdata),
3322 	const char * const *attribute,
3323 	const ns_cred_t *auth,
3324 	const int flags,
3325 	ns_ldap_result_t **rResult, /* return result entries */
3326 	ns_ldap_error_t **errorp,
3327 	int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3328 	const void *userdata)
3329 {
3330 	ns_conn_user_t	*cu = NULL;
3331 	int		try_cnt = 0;
3332 	int		rc = NS_LDAP_SUCCESS, trc;
3333 
3334 	for (;;) {
3335 		if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
3336 		    &try_cnt, &rc, errorp) == 0)
3337 			break;
3338 		rc = ldap_list(NULL, service, filter, sortattr, init_filter_cb,
3339 		    attribute, auth, flags, rResult, errorp, &trc, callback,
3340 		    userdata, cu);
3341 	}
3342 
3343 	return (rc);
3344 }
3345 
3346 /*
3347  * Create and initialize batch for native LDAP lookups
3348  */
3349 int
3350 __ns_ldap_list_batch_start(ns_ldap_list_batch_t **batch)
3351 {
3352 	*batch = calloc(1, sizeof (ns_ldap_list_batch_t));
3353 	if (*batch == NULL)
3354 		return (NS_LDAP_MEMORY);
3355 	return (NS_LDAP_SUCCESS);
3356 }
3357 
3358 
3359 /*
3360  * Add a LDAP search request to the batch.
3361  */
3362 int
3363 __ns_ldap_list_batch_add(
3364 	ns_ldap_list_batch_t *batch,
3365 	const char *service,
3366 	const char *filter,
3367 	int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3368 	char **realfilter, const void *userdata),
3369 	const char * const *attribute,
3370 	const ns_cred_t *auth,
3371 	const int flags,
3372 	ns_ldap_result_t **rResult, /* return result entries */
3373 	ns_ldap_error_t **errorp,
3374 	int *rcp,
3375 	int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
3376 	const void *userdata)
3377 {
3378 	ns_conn_user_t	*cu;
3379 	int		rc;
3380 	int		mod_flags;
3381 
3382 	cu =  __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, 0);
3383 	if (cu == NULL) {
3384 		if (rcp != NULL)
3385 			*rcp = NS_LDAP_MEMORY;
3386 		return (NS_LDAP_MEMORY);
3387 	}
3388 
3389 	/*
3390 	 * Strip the NS_LDAP_PAGE_CTRL option as the batch interface does not
3391 	 * support this.
3392 	 */
3393 	mod_flags = flags & (~NS_LDAP_PAGE_CTRL);
3394 
3395 	rc = ldap_list(batch, service, filter, NULL, init_filter_cb, attribute,
3396 	    auth, mod_flags, rResult, errorp, rcp, callback, userdata, cu);
3397 
3398 	/*
3399 	 * Free the conn_user if the cookie was not batched. If the cookie
3400 	 * was batched then __ns_ldap_list_batch_end or release will free the
3401 	 * conn_user. The batch API instructs the search_state_machine
3402 	 * to reinit and retry (max 3 times) on retriable LDAP errors.
3403 	 */
3404 	if (rc != NS_LDAP_SUCCESS && cu != NULL) {
3405 		if (cu->conn_mt != NULL)
3406 			__s_api_conn_mt_return(cu);
3407 		__s_api_conn_user_free(cu);
3408 	}
3409 	return (rc);
3410 }
3411 
3412 
3413 /*
3414  * Free batch.
3415  */
3416 void
3417 __ns_ldap_list_batch_release(ns_ldap_list_batch_t *batch)
3418 {
3419 	ns_ldap_cookie_t	*c, *next;
3420 
3421 	for (c = batch->cookie_list; c != NULL; c = next) {
3422 		next = c->next_cookie_in_batch;
3423 		if (c->conn_user != NULL) {
3424 			if (c->conn_user->conn_mt != NULL)
3425 				__s_api_conn_mt_return(c->conn_user);
3426 			__s_api_conn_user_free(c->conn_user);
3427 			c->conn_user = NULL;
3428 		}
3429 		delete_search_cookie(c);
3430 	}
3431 	free(batch);
3432 }
3433 
3434 #define	LD_USING_STATE(st) \
3435 	((st == DO_SEARCH) || (st == MULTI_RESULT) || (st == NEXT_RESULT))
3436 
3437 /*
3438  * Process batch. Everytime this function is called it selects an
3439  * active cookie from the batch and single steps through the
3440  * search_state_machine for the selected cookie. If lookup associated
3441  * with the cookie is complete (success or error) then the cookie is
3442  * removed from the batch and its memory freed.
3443  *
3444  * Returns 1 (if batch still has active cookies)
3445  *         0 (if batch has no more active cookies)
3446  *        -1 (on errors, *rcp will contain the error code)
3447  *
3448  * The caller should call this function in a loop as long as it returns 1
3449  * to process all the requests added to the batch. The results (and errors)
3450  * will be available in the locations provided by the caller at the time of
3451  * __ns_ldap_list_batch_add().
3452  */
3453 static
3454 int
3455 __ns_ldap_list_batch_process(ns_ldap_list_batch_t *batch, int *rcp)
3456 {
3457 	ns_ldap_cookie_t	*c, *ptr, **prev;
3458 	ns_state_t		state;
3459 	ns_ldap_error_t		*errorp = NULL;
3460 	int			rc;
3461 
3462 	/* Check if are already done */
3463 	if (batch->nactive == 0)
3464 		return (0);
3465 
3466 	/* Get the next cookie from the batch */
3467 	c = (batch->next_cookie == NULL) ?
3468 	    batch->cookie_list : batch->next_cookie;
3469 
3470 	batch->next_cookie = c->next_cookie_in_batch;
3471 
3472 	/*
3473 	 * Checks the status of the cookie's connection if it needs
3474 	 * to use that connection for ldap_search_ext or ldap_result.
3475 	 * If the connection is no longer good but worth retrying
3476 	 * then reinit the search_state_machine for this cookie
3477 	 * starting from the first search descriptor. REINIT will
3478 	 * clear any leftover results if max retries have not been
3479 	 * reached and redo the search (which may also involve
3480 	 * following referrals again).
3481 	 *
3482 	 * Note that each cookie in the batch will make this
3483 	 * determination when it reaches one of the LD_USING_STATES.
3484 	 */
3485 	if (LD_USING_STATE(c->new_state) && c->conn_user != NULL) {
3486 		rc = __s_api_setup_getnext(c->conn_user, &c->err_rc, &errorp);
3487 		if (rc == LDAP_BUSY || rc == LDAP_UNAVAILABLE ||
3488 		    rc == LDAP_UNWILLING_TO_PERFORM) {
3489 			if (errorp != NULL) {
3490 				(void) __ns_ldap_freeError(&c->errorp);
3491 				c->errorp = errorp;
3492 			}
3493 			c->new_state = REINIT;
3494 		} else if (rc == LDAP_CONNECT_ERROR ||
3495 		    rc == LDAP_SERVER_DOWN) {
3496 			if (errorp != NULL) {
3497 				(void) __ns_ldap_freeError(&c->errorp);
3498 				c->errorp = errorp;
3499 			}
3500 			c->new_state = REINIT;
3501 			/*
3502 			 * MT connection is not usable,
3503 			 * close it before REINIT.
3504 			 */
3505 			__s_api_conn_mt_close(
3506 			    c->conn_user, rc, NULL);
3507 		} else if (rc != NS_LDAP_SUCCESS) {
3508 			if (rcp != NULL)
3509 				*rcp = rc;
3510 			*c->caller_result = NULL;
3511 			*c->caller_errorp = errorp;
3512 			*c->caller_rc = rc;
3513 			return (-1);
3514 		}
3515 	}
3516 
3517 	for (;;) {
3518 		/* Single step through the search_state_machine */
3519 		state = search_state_machine(c, c->new_state, ONE_STEP);
3520 		switch (state) {
3521 		case LDAP_ERROR:
3522 			(void) search_state_machine(c, state, ONE_STEP);
3523 			(void) search_state_machine(c, CLEAR_RESULTS, ONE_STEP);
3524 			/* FALLTHROUGH */
3525 		case ERROR:
3526 		case EXIT:
3527 			*c->caller_result = c->result;
3528 			*c->caller_errorp = c->errorp;
3529 			*c->caller_rc =
3530 			    (c->result == NULL && c->err_from_result == 0)
3531 			    ? NS_LDAP_NOTFOUND : c->err_rc;
3532 			c->result = NULL;
3533 			c->errorp = NULL;
3534 			/* Remove the cookie from the batch */
3535 			ptr = batch->cookie_list;
3536 			prev = &batch->cookie_list;
3537 			while (ptr != NULL) {
3538 				if (ptr == c) {
3539 					*prev = ptr->next_cookie_in_batch;
3540 					break;
3541 				}
3542 				prev = &ptr->next_cookie_in_batch;
3543 				ptr = ptr->next_cookie_in_batch;
3544 			}
3545 			/* Delete cookie and decrement active cookie count */
3546 			if (c->conn_user != NULL) {
3547 				if (c->conn_user->conn_mt != NULL)
3548 					__s_api_conn_mt_return(c->conn_user);
3549 				__s_api_conn_user_free(c->conn_user);
3550 				c->conn_user = NULL;
3551 			}
3552 			delete_search_cookie(c);
3553 			batch->nactive--;
3554 			break;
3555 		case NEXT_RESULT:
3556 		case MULTI_RESULT:
3557 			/*
3558 			 * This means that search_state_machine needs to do
3559 			 * another ldap_result() for the cookie in question.
3560 			 * We only do at most one ldap_result() per call in
3561 			 * this function and therefore we return. This allows
3562 			 * the caller to process results from other cookies
3563 			 * in the batch without getting tied up on just one
3564 			 * cookie.
3565 			 */
3566 			break;
3567 		default:
3568 			/*
3569 			 * This includes states that follow NEXT_RESULT or
3570 			 * MULTI_RESULT such as PROCESS_RESULT and
3571 			 * END_PROCESS_RESULT. We continue processing
3572 			 * this cookie till we reach either the error, exit
3573 			 * or the result states.
3574 			 */
3575 			continue;
3576 		}
3577 		break;
3578 	}
3579 
3580 	/* Return 0 if no more cookies left otherwise 1 */
3581 	return ((batch->nactive > 0) ? 1 : 0);
3582 }
3583 
3584 
3585 /*
3586  * Process all the active cookies in the batch and when none
3587  * remains finalize the batch.
3588  */
3589 int
3590 __ns_ldap_list_batch_end(ns_ldap_list_batch_t *batch)
3591 {
3592 	int rc = NS_LDAP_SUCCESS;
3593 	while (__ns_ldap_list_batch_process(batch, &rc) > 0)
3594 		;
3595 	__ns_ldap_list_batch_release(batch);
3596 	return (rc);
3597 }
3598 
3599 typedef struct lookup_data {
3600 	const char *lkd_dn;
3601 	const char *lkd_service;
3602 	const char *lkd_filter;
3603 	const ns_cred_t *lkd_cred;
3604 	ns_conn_user_t *lkd_user;
3605 } lookup_data_t;
3606 
3607 /*
3608  * This creates a service search descriptor that can be used to
3609  * retrieve a specific DN by using the DN as the basedn with a search
3610  * scope of 'base'. We don't use any service SSDs in this instance since
3611  * they are intended to search specific locations/subtrees and filter the
3612  * results, while here we are wanting to retrieve a specific entry.
3613  */
3614 static int
3615 lookup_create_ssd(lookup_data_t *dn_data, ns_ldap_search_desc_t **descpp)
3616 {
3617 	ns_ldap_search_desc_t *dptr;
3618 
3619 	*descpp = NULL;
3620 
3621 	dptr = calloc(1, sizeof (ns_ldap_search_desc_t));
3622 	if (dptr == NULL)
3623 		return (NS_LDAP_MEMORY);
3624 
3625 	dptr->basedn = strdup(dn_data->lkd_dn);
3626 	dptr->scope = NS_LDAP_SCOPE_BASE;
3627 	dptr->filter = strdup(dn_data->lkd_filter);
3628 
3629 	if (dptr->basedn == NULL || dptr->filter == NULL) {
3630 		__ns_ldap_freeASearchDesc(dptr);
3631 		return (NS_LDAP_MEMORY);
3632 	}
3633 
3634 	*descpp = dptr;
3635 	return (NS_LDAP_SUCCESS);
3636 }
3637 
3638 static int
3639 lookup_dn(lookup_data_t *dn_data, const char **attrs,
3640     ns_ldap_result_t **resultp, ns_ldap_error_t **errorp)
3641 {
3642 	ns_ldap_cookie_t	*cookie;
3643 	int			rc = 0;
3644 	int			flags = 0;
3645 
3646 	*errorp = NULL;
3647 	*resultp = NULL;
3648 
3649 	if (dn_data == NULL || dn_data->lkd_dn == NULL ||
3650 	    dn_data->lkd_dn[0] == '\0' || dn_data->lkd_filter == NULL)
3651 		return (NS_LDAP_INVALID_PARAM);
3652 
3653 	cookie = init_search_state_machine();
3654 	if (cookie == NULL)
3655 		return (NS_LDAP_MEMORY);
3656 
3657 	rc = __s_api_toFollowReferrals(flags, &cookie->followRef, errorp);
3658 	if (rc != NS_LDAP_SUCCESS)
3659 		goto out;
3660 
3661 	/* 1 for SSD, 1 for terminating NULL */
3662 	cookie->sdlist = calloc(2, sizeof (ns_ldap_search_desc_t *));
3663 	if (cookie->sdlist == NULL) {
3664 		rc = NS_LDAP_MEMORY;
3665 		goto out;
3666 	}
3667 
3668 	rc = lookup_create_ssd(dn_data, &cookie->sdlist[0]);
3669 	if (rc != NS_LDAP_SUCCESS)
3670 		goto out;
3671 
3672 	if (dn_data->lkd_service != NULL) {
3673 		/*
3674 		 * If a service was specified, set that on the cookie so
3675 		 * that search_state_machine() will properly map
3676 		 * attributes and objectclasses.
3677 		 */
3678 		cookie->service = strdup(dn_data->lkd_service);
3679 		if (cookie->service == NULL) {
3680 			rc = NS_LDAP_MEMORY;
3681 			goto out;
3682 		}
3683 	}
3684 
3685 	cookie->i_attr = attrs;
3686 	cookie->i_auth = dn_data->lkd_cred;
3687 	cookie->i_flags = 0;
3688 	cookie->i_filter = strdup(dn_data->lkd_filter);
3689 	if (cookie->i_filter == NULL) {
3690 		rc = NS_LDAP_MEMORY;
3691 		goto out;
3692 	}
3693 
3694 	/*
3695 	 * Actually perform the search. The return value is only used when
3696 	 * iterating through multiple results. Since we are searching with
3697 	 * a scope of base, we will always get at most one result entry,
3698 	 * we ignore the return value and look at err_rc to determine if
3699 	 * there were any errors.
3700 	 */
3701 	(void) search_state_machine(cookie, INIT, 0);
3702 	rc = cookie->err_rc;
3703 
3704 	if (rc != NS_LDAP_SUCCESS) {
3705 		ns_conn_user_t *user = dn_data->lkd_user;
3706 
3707 		if (user != NULL && user->ns_error != NULL) {
3708 			*errorp = user->ns_error;
3709 			user->ns_error = NULL;
3710 		} else {
3711 			*errorp = cookie->errorp;
3712 			cookie->errorp = NULL;
3713 		}
3714 	} else if (cookie->result != NULL) {
3715 		*resultp = cookie->result;
3716 		cookie->result = NULL;
3717 	} else {
3718 		rc = NS_LDAP_NOTFOUND;
3719 	}
3720 
3721 out:
3722 	delete_search_cookie(cookie);
3723 	return (rc);
3724 }
3725 
3726 /*
3727  * find_domainname performs one or more LDAP searches to
3728  * find the value of the nisdomain attribute associated with
3729  * the input DN (with no retry).
3730  */
3731 
3732 static int
3733 find_domainname(const char *dn, char **domainname, const ns_cred_t *cred,
3734     ns_ldap_error_t **errorp, ns_conn_user_t *conn_user)
3735 {
3736 	lookup_data_t		ldata;
3737 	ns_ldap_result_t	*result;
3738 	char			**value;
3739 	int			rc;
3740 
3741 	*domainname = NULL;
3742 	*errorp = NULL;
3743 
3744 	ldata.lkd_dn = dn;
3745 	ldata.lkd_service = NULL;
3746 	ldata.lkd_filter = _NIS_FILTER;
3747 	ldata.lkd_cred = cred;
3748 	ldata.lkd_user = conn_user;
3749 
3750 	rc = lookup_dn(&ldata, nis_domain_attrs, &result, errorp);
3751 	if (rc != NS_LDAP_SUCCESS)
3752 		return (rc);
3753 
3754 	value = __ns_ldap_getAttr(result->entry, _NIS_DOMAIN);
3755 
3756 	if (value != NULL && value[0] != NULL) {
3757 		*domainname = strdup(value[0]);
3758 		if (*domainname == NULL)
3759 			rc = NS_LDAP_MEMORY;
3760 	} else {
3761 		rc = NS_LDAP_NOTFOUND;
3762 	}
3763 
3764 	(void) __ns_ldap_freeResult(&result);
3765 	return (rc);
3766 }
3767 
3768 /*
3769  * __s_api_find_domainname performs one or more LDAP searches to
3770  * find the value of the nisdomain attribute associated with
3771  * the input DN (with retry).
3772  */
3773 
3774 static int
3775 __s_api_find_domainname(const char *dn, char **domainname,
3776     const ns_cred_t *cred, ns_ldap_error_t **errorp)
3777 {
3778 	ns_conn_user_t	*cu = NULL;
3779 	int		try_cnt = 0;
3780 	int		rc = NS_LDAP_SUCCESS;
3781 
3782 	for (;;) {
3783 		if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
3784 		    &try_cnt, &rc, errorp) == 0)
3785 			break;
3786 		rc = find_domainname(dn, domainname, cred, errorp, cu);
3787 	}
3788 
3789 	return (rc);
3790 }
3791 
3792 static int
3793 firstEntry(
3794     const char *service,
3795     const char *filter,
3796     const char *sortattr,
3797     int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3798     char **realfilter, const void *userdata),
3799     const char * const *attribute,
3800     const ns_cred_t *auth,
3801     const int flags,
3802     void **vcookie,
3803     ns_ldap_result_t **result,
3804     ns_ldap_error_t ** errorp,
3805     const void *userdata,
3806     ns_conn_user_t *conn_user)
3807 {
3808 	ns_ldap_cookie_t	*cookie = NULL;
3809 	ns_ldap_error_t		*error = NULL;
3810 	ns_state_t		state;
3811 	ns_ldap_search_desc_t	**sdlist;
3812 	ns_ldap_search_desc_t	*dptr;
3813 	char			**dns = NULL;
3814 	int			scope;
3815 	int			rc;
3816 
3817 	*errorp = NULL;
3818 	*result = NULL;
3819 
3820 	/*
3821 	 * Sanity check - NS_LDAP_READ_SHADOW is for our
3822 	 * own internal use.
3823 	 */
3824 	if (flags & NS_LDAP_READ_SHADOW)
3825 		return (NS_LDAP_INVALID_PARAM);
3826 
3827 	/* get the service descriptor - or create a default one */
3828 	rc = __s_api_get_SSD_from_SSDtoUse_service(service,
3829 	    &sdlist, &error);
3830 	if (rc != NS_LDAP_SUCCESS) {
3831 		*errorp = error;
3832 		return (rc);
3833 	}
3834 	if (sdlist == NULL) {
3835 		/* Create default service Desc */
3836 		sdlist = (ns_ldap_search_desc_t **)calloc(2,
3837 		    sizeof (ns_ldap_search_desc_t *));
3838 		if (sdlist == NULL) {
3839 			return (NS_LDAP_MEMORY);
3840 		}
3841 		dptr = (ns_ldap_search_desc_t *)
3842 		    calloc(1, sizeof (ns_ldap_search_desc_t));
3843 		if (dptr == NULL) {
3844 			free(sdlist);
3845 			return (NS_LDAP_MEMORY);
3846 		}
3847 		sdlist[0] = dptr;
3848 
3849 		/* default base */
3850 		rc = __s_api_getDNs(&dns, service, &error);
3851 		if (rc != NS_LDAP_SUCCESS) {
3852 			if (dns) {
3853 				__s_api_free2dArray(dns);
3854 				dns = NULL;
3855 			}
3856 			if (sdlist) {
3857 				(void) __ns_ldap_freeSearchDescriptors(
3858 				    &sdlist);
3859 
3860 				sdlist = NULL;
3861 			}
3862 			*errorp = error;
3863 			return (rc);
3864 		}
3865 		dptr->basedn = strdup(dns[0]);
3866 		__s_api_free2dArray(dns);
3867 		dns = NULL;
3868 
3869 		/* default scope */
3870 		scope = 0;
3871 		cookie = init_search_state_machine();
3872 		if (cookie == NULL) {
3873 			if (sdlist) {
3874 				(void) __ns_ldap_freeSearchDescriptors(&sdlist);
3875 				sdlist = NULL;
3876 			}
3877 			return (NS_LDAP_MEMORY);
3878 		}
3879 		rc = __s_api_getSearchScope(&scope, &cookie->errorp);
3880 		dptr->scope = scope;
3881 	}
3882 
3883 	/* Initialize State machine cookie */
3884 	if (cookie == NULL)
3885 		cookie = init_search_state_machine();
3886 	if (cookie == NULL) {
3887 		if (sdlist) {
3888 			(void) __ns_ldap_freeSearchDescriptors(&sdlist);
3889 			sdlist = NULL;
3890 		}
3891 		return (NS_LDAP_MEMORY);
3892 	}
3893 
3894 	/* identify self as a getent user */
3895 	cookie->conn_user = conn_user;
3896 
3897 	cookie->sdlist = sdlist;
3898 
3899 	/* see if need to follow referrals */
3900 	rc = __s_api_toFollowReferrals(flags,
3901 	    &cookie->followRef, errorp);
3902 	if (rc != NS_LDAP_SUCCESS) {
3903 		delete_search_cookie(cookie);
3904 		return (rc);
3905 	}
3906 
3907 	/*
3908 	 * use VLV/PAGE control only if NS_LDAP_NO_PAGE_CTRL is not set
3909 	 */
3910 	if (flags & NS_LDAP_NO_PAGE_CTRL)
3911 		cookie->use_paging = FALSE;
3912 	else
3913 		cookie->use_paging = TRUE;
3914 
3915 	/* Set up other arguments */
3916 	cookie->userdata = userdata;
3917 	if (init_filter_cb != NULL) {
3918 		cookie->init_filter_cb = init_filter_cb;
3919 		cookie->use_filtercb = 1;
3920 	}
3921 	cookie->use_usercb = 0;
3922 	/* check_shadow() may add extra value to cookie->i_flags */
3923 	cookie->i_flags = flags;
3924 	if (service) {
3925 		cookie->service = strdup(service);
3926 		if (cookie->service == NULL) {
3927 			delete_search_cookie(cookie);
3928 			return (NS_LDAP_MEMORY);
3929 		}
3930 
3931 		/*
3932 		 * If given, use the credential given by the caller, and
3933 		 * skip the credential check required for shadow update.
3934 		 */
3935 		if (auth == NULL) {
3936 			rc = check_shadow(cookie, service);
3937 			if (rc != NS_LDAP_SUCCESS) {
3938 				*errorp = cookie->errorp;
3939 				cookie->errorp = NULL;
3940 				delete_search_cookie(cookie);
3941 				cookie = NULL;
3942 				return (rc);
3943 			}
3944 		}
3945 	}
3946 
3947 	cookie->i_filter = strdup(filter);
3948 	cookie->i_attr = attribute;
3949 	cookie->i_sortattr = sortattr;
3950 	cookie->i_auth = auth;
3951 
3952 	state = INIT;
3953 	for (;;) {
3954 		state = search_state_machine(cookie, state, ONE_STEP);
3955 		switch (state) {
3956 		case PROCESS_RESULT:
3957 			*result = cookie->result;
3958 			cookie->result = NULL;
3959 			*vcookie = (void *)cookie;
3960 			return (NS_LDAP_SUCCESS);
3961 		case LDAP_ERROR:
3962 			state = search_state_machine(cookie, state, ONE_STEP);
3963 			state = search_state_machine(cookie, CLEAR_RESULTS,
3964 			    ONE_STEP);
3965 			/* FALLTHROUGH */
3966 		case ERROR:
3967 			rc = cookie->err_rc;
3968 			if (conn_user != NULL && conn_user->ns_error != NULL) {
3969 				*errorp = conn_user->ns_error;
3970 				conn_user->ns_error = NULL;
3971 			} else {
3972 				*errorp = cookie->errorp;
3973 				cookie->errorp = NULL;
3974 			}
3975 			delete_search_cookie(cookie);
3976 			return (rc);
3977 		case EXIT:
3978 			rc = cookie->err_rc;
3979 			if (rc != NS_LDAP_SUCCESS) {
3980 				*errorp = cookie->errorp;
3981 				cookie->errorp = NULL;
3982 			} else {
3983 				rc = NS_LDAP_NOTFOUND;
3984 			}
3985 
3986 			delete_search_cookie(cookie);
3987 			return (rc);
3988 
3989 		default:
3990 			break;
3991 		}
3992 	}
3993 }
3994 
3995 int
3996 __ns_ldap_firstEntry(
3997     const char *service,
3998     const char *filter,
3999     const char *vlv_sort,
4000     int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
4001     char **realfilter, const void *userdata),
4002     const char * const *attribute,
4003     const ns_cred_t *auth,
4004     const int flags,
4005     void **vcookie,
4006     ns_ldap_result_t **result,
4007     ns_ldap_error_t ** errorp,
4008     const void *userdata)
4009 {
4010 	ns_conn_user_t	*cu = NULL;
4011 	int		try_cnt = 0;
4012 	int		rc = NS_LDAP_SUCCESS;
4013 
4014 	for (;;) {
4015 		if (__s_api_setup_retry_search(&cu, NS_CONN_USER_GETENT,
4016 		    &try_cnt, &rc, errorp) == 0)
4017 			break;
4018 		rc = firstEntry(service, filter, vlv_sort, init_filter_cb,
4019 		    attribute, auth, flags, vcookie, result, errorp, userdata,
4020 		    cu);
4021 	}
4022 	return (rc);
4023 }
4024 
4025 /*ARGSUSED2*/
4026 int
4027 __ns_ldap_nextEntry(void *vcookie, ns_ldap_result_t **result,
4028     ns_ldap_error_t ** errorp)
4029 {
4030 	ns_ldap_cookie_t	*cookie;
4031 	ns_state_t		state;
4032 	int			rc;
4033 
4034 	cookie = (ns_ldap_cookie_t *)vcookie;
4035 	cookie->result = NULL;
4036 	*result = NULL;
4037 
4038 	if (cookie->conn_user != NULL) {
4039 		rc = __s_api_setup_getnext(cookie->conn_user,
4040 		    &cookie->err_rc, errorp);
4041 		if (rc != NS_LDAP_SUCCESS)
4042 			return (rc);
4043 	}
4044 
4045 	state = END_PROCESS_RESULT;
4046 	for (;;) {
4047 		state = search_state_machine(cookie, state, ONE_STEP);
4048 		switch (state) {
4049 		case PROCESS_RESULT:
4050 			*result = cookie->result;
4051 			cookie->result = NULL;
4052 			return (NS_LDAP_SUCCESS);
4053 		case LDAP_ERROR:
4054 			state = search_state_machine(cookie, state, ONE_STEP);
4055 			state = search_state_machine(cookie, CLEAR_RESULTS,
4056 			    ONE_STEP);
4057 			/* FALLTHROUGH */
4058 		case ERROR:
4059 			rc = cookie->err_rc;
4060 			*errorp = cookie->errorp;
4061 			cookie->errorp = NULL;
4062 			return (rc);
4063 		case EXIT:
4064 			return (NS_LDAP_SUCCESS);
4065 		}
4066 	}
4067 }
4068 
4069 int
4070 __ns_ldap_endEntry(
4071 	void **vcookie,
4072 	ns_ldap_error_t ** errorp)
4073 {
4074 	ns_ldap_cookie_t	*cookie;
4075 	int			rc;
4076 
4077 	if (*vcookie == NULL)
4078 		return (NS_LDAP_INVALID_PARAM);
4079 
4080 	cookie = (ns_ldap_cookie_t *)(*vcookie);
4081 	cookie->result = NULL;
4082 
4083 	/* Complete search */
4084 	rc = search_state_machine(cookie, CLEAR_RESULTS, 0);
4085 
4086 	/* Copy results back to user */
4087 	rc = cookie->err_rc;
4088 	if (rc != NS_LDAP_SUCCESS)
4089 		*errorp = cookie->errorp;
4090 
4091 	cookie->errorp = NULL;
4092 	if (cookie->conn_user != NULL) {
4093 		if (cookie->conn_user->conn_mt != NULL)
4094 			__s_api_conn_mt_return(cookie->conn_user);
4095 		__s_api_conn_user_free(cookie->conn_user);
4096 	}
4097 	delete_search_cookie(cookie);
4098 	cookie = NULL;
4099 	*vcookie = NULL;
4100 
4101 	return (rc);
4102 }
4103 
4104 
4105 int
4106 __ns_ldap_freeResult(ns_ldap_result_t **result)
4107 {
4108 
4109 	ns_ldap_entry_t	*curEntry = NULL;
4110 	ns_ldap_entry_t	*delEntry = NULL;
4111 	int		i;
4112 	ns_ldap_result_t	*res = *result;
4113 
4114 #ifdef DEBUG
4115 	(void) fprintf(stderr, "__ns_ldap_freeResult START\n");
4116 #endif
4117 	if (res == NULL)
4118 		return (NS_LDAP_INVALID_PARAM);
4119 
4120 	if (res->entry != NULL)
4121 		curEntry = res->entry;
4122 
4123 	for (i = 0; i < res->entries_count; i++) {
4124 		if (curEntry != NULL) {
4125 			delEntry = curEntry;
4126 			curEntry = curEntry->next;
4127 			__ns_ldap_freeEntry(delEntry);
4128 		}
4129 	}
4130 
4131 	free(res);
4132 	*result = NULL;
4133 	return (NS_LDAP_SUCCESS);
4134 }
4135 
4136 int
4137 __ns_ldap_auth(const ns_cred_t *auth, const int flags, ns_ldap_error_t **errorp,
4138     LDAPControl **serverctrls __unused, LDAPControl **clientctrls __unused)
4139 {
4140 
4141 	ConnectionID	connectionId = -1;
4142 	Connection	*conp;
4143 	int		rc = 0;
4144 	int		do_not_fail_if_new_pwd_reqd = 0;
4145 	int		nopasswd_acct_mgmt = 0;
4146 	ns_conn_user_t	*conn_user;
4147 
4148 
4149 #ifdef DEBUG
4150 	(void) fprintf(stderr, "__ns_ldap_auth START\n");
4151 #endif
4152 
4153 	*errorp = NULL;
4154 	if (auth == NULL)
4155 		return (NS_LDAP_INVALID_PARAM);
4156 
4157 	conn_user = __s_api_conn_user_init(NS_CONN_USER_AUTH,
4158 	    NULL, B_FALSE);
4159 
4160 	rc = __s_api_getConnection(NULL, flags | NS_LDAP_NEW_CONN,
4161 	    auth, &connectionId, &conp, errorp,
4162 	    do_not_fail_if_new_pwd_reqd, nopasswd_acct_mgmt,
4163 	    conn_user);
4164 
4165 	if (conn_user != NULL)
4166 		__s_api_conn_user_free(conn_user);
4167 
4168 	if (rc == NS_LDAP_OP_FAILED && *errorp)
4169 		(void) __ns_ldap_freeError(errorp);
4170 
4171 	if (connectionId > -1)
4172 		DropConnection(connectionId, flags);
4173 	return (rc);
4174 }
4175 
4176 char **
4177 __ns_ldap_getAttr(const ns_ldap_entry_t *entry, const char *attrname)
4178 {
4179 	int	i;
4180 
4181 	if (entry == NULL)
4182 		return (NULL);
4183 	for (i = 0; i < entry->attr_count; i++) {
4184 		if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == 0)
4185 			return (entry->attr_pair[i]->attrvalue);
4186 	}
4187 	return (NULL);
4188 }
4189 
4190 ns_ldap_attr_t *
4191 __ns_ldap_getAttrStruct(const ns_ldap_entry_t *entry, const char *attrname)
4192 {
4193 	int	i;
4194 
4195 	if (entry == NULL)
4196 		return (NULL);
4197 	for (i = 0; i < entry->attr_count; i++) {
4198 		if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == 0)
4199 			return (entry->attr_pair[i]);
4200 	}
4201 	return (NULL);
4202 }
4203 
4204 
4205 int
4206 __ns_ldap_uid2dn(const char *uid, char **userDN, const ns_cred_t *cred,
4207     ns_ldap_error_t **errorp)
4208 {
4209 	ns_ldap_result_t	*result = NULL;
4210 	char		*filter, *userdata;
4211 	char		errstr[MAXERROR];
4212 	char		**value;
4213 	int		rc = 0;
4214 	int		i;
4215 	size_t		len;
4216 
4217 	*errorp = NULL;
4218 	*userDN = NULL;
4219 	if ((uid == NULL) || (uid[0] == '\0'))
4220 		return (NS_LDAP_INVALID_PARAM);
4221 
4222 	for (i = 0; uid[i] != '\0'; i++) {
4223 		if (uid[i] == '=') {
4224 			*userDN = strdup(uid);
4225 			return (NS_LDAP_SUCCESS);
4226 		}
4227 	}
4228 	for (i = 0; (uid[i] != '\0') && isdigit(uid[i]); i++)
4229 		;
4230 	if (uid[i] == '\0') {
4231 		len = strlen(UIDNUMFILTER) + strlen(uid) + 1;
4232 		filter = malloc(len);
4233 		if (filter == NULL) {
4234 			*userDN = NULL;
4235 			return (NS_LDAP_MEMORY);
4236 		}
4237 		(void) snprintf(filter, len, UIDNUMFILTER, uid);
4238 
4239 		len = strlen(UIDNUMFILTER_SSD) + strlen(uid) + 1;
4240 		userdata = malloc(len);
4241 		if (userdata == NULL) {
4242 			*userDN = NULL;
4243 			free(filter);
4244 			return (NS_LDAP_MEMORY);
4245 		}
4246 		(void) snprintf(userdata, len, UIDNUMFILTER_SSD, uid);
4247 	} else {
4248 		len = strlen(UIDFILTER) + strlen(uid) + 1;
4249 		filter = malloc(len);
4250 		if (filter == NULL) {
4251 			*userDN = NULL;
4252 			return (NS_LDAP_MEMORY);
4253 		}
4254 		(void) snprintf(filter, len, UIDFILTER, uid);
4255 
4256 		len = strlen(UIDFILTER_SSD) + strlen(uid) + 1;
4257 		userdata = malloc(len);
4258 		if (userdata == NULL) {
4259 			*userDN = NULL;
4260 			free(filter);
4261 			return (NS_LDAP_MEMORY);
4262 		}
4263 		(void) snprintf(userdata, len, UIDFILTER_SSD, uid);
4264 	}
4265 
4266 	/*
4267 	 * we want to retrieve the DN as it appears in LDAP
4268 	 * hence the use of NS_LDAP_NOT_CVT_DN in flags
4269 	 */
4270 	rc = __ns_ldap_list("passwd", filter,
4271 	    __s_api_merge_SSD_filter,
4272 	    NULL, cred, NS_LDAP_NOT_CVT_DN,
4273 	    &result, errorp, NULL,
4274 	    userdata);
4275 	free(filter);
4276 	filter = NULL;
4277 	free(userdata);
4278 	userdata = NULL;
4279 	if (rc != NS_LDAP_SUCCESS) {
4280 		if (result) {
4281 			(void) __ns_ldap_freeResult(&result);
4282 			result = NULL;
4283 		}
4284 		return (rc);
4285 	}
4286 	if (result->entries_count > 1) {
4287 		(void) __ns_ldap_freeResult(&result);
4288 		result = NULL;
4289 		*userDN = NULL;
4290 		(void) sprintf(errstr,
4291 		    gettext("Too many entries are returned for %s"), uid);
4292 		MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
4293 		    NS_LDAP_MEMORY);
4294 		return (NS_LDAP_INTERNAL);
4295 	}
4296 
4297 	value = __ns_ldap_getAttr(result->entry, "dn");
4298 	*userDN = strdup(value[0]);
4299 	(void) __ns_ldap_freeResult(&result);
4300 	result = NULL;
4301 	return (NS_LDAP_SUCCESS);
4302 }
4303 
4304 #define	_P_UID	"uid"
4305 static const char *dn2uid_attrs[] = {
4306 	_P_CN,
4307 	_P_UID,
4308 	(char *)NULL
4309 };
4310 
4311 int
4312 __ns_ldap_dn2uid(const char *dn, char **userIDp, const ns_cred_t *cred,
4313     ns_ldap_error_t **errorp)
4314 {
4315 	lookup_data_t		ldata;
4316 	ns_ldap_result_t	*result;
4317 	char			**value;
4318 	int			rc;
4319 
4320 	*errorp = NULL;
4321 	*userIDp = NULL;
4322 	if ((dn == NULL) || (dn[0] == '\0'))
4323 		return (NS_LDAP_INVALID_PARAM);
4324 
4325 	/*
4326 	 * Many LDAP servers do not support using the dn in a search
4327 	 * filter. As a result, we unfortunately cannot  use __ns_ldap_list()
4328 	 * to lookup the DN. Instead we perform a search with the baseDN
4329 	 * being the DN we are looking for with a scope of 'base' to
4330 	 * return the entry, as this should be supported by all LDAP servers.
4331 	 */
4332 	ldata.lkd_dn = dn;
4333 
4334 	/*
4335 	 * Since we are looking up a user account by its DN, use the attribute
4336 	 * and objectclass mappings (if present) for the passwd service.
4337 	 */
4338 	ldata.lkd_service = "passwd";
4339 	ldata.lkd_filter = UIDDNFILTER;
4340 	ldata.lkd_cred = cred;
4341 	ldata.lkd_user = NULL;
4342 
4343 	rc = lookup_dn(&ldata, dn2uid_attrs, &result, errorp);
4344 	if (rc != NS_LDAP_SUCCESS)
4345 		return (rc);
4346 
4347 	value = __ns_ldap_getAttr(result->entry, _P_UID);
4348 	if (value != NULL && value[0] != NULL) {
4349 		*userIDp = strdup(value[0]);
4350 		if (*userIDp == NULL)
4351 			rc = NS_LDAP_MEMORY;
4352 	} else {
4353 		rc = NS_LDAP_NOTFOUND;
4354 	}
4355 
4356 	(void) __ns_ldap_freeResult(&result);
4357 	return (rc);
4358 }
4359 
4360 int
4361 __ns_ldap_host2dn(const char *host, const char *domain, char **hostDN,
4362     const ns_cred_t *cred, ns_ldap_error_t **errorp)
4363 {
4364 	ns_ldap_result_t	*result = NULL;
4365 	char		*filter, *userdata;
4366 	char		errstr[MAXERROR];
4367 	char		**value;
4368 	int		rc;
4369 	size_t		len;
4370 
4371 	/*
4372 	 * XXX
4373 	 * the domain parameter needs to be used in case domain is not local,
4374 	 * if this routine is to support multi domain setups, it needs lots
4375 	 * of work...
4376 	 */
4377 	*errorp = NULL;
4378 	*hostDN = NULL;
4379 	if ((host == NULL) || (host[0] == '\0'))
4380 		return (NS_LDAP_INVALID_PARAM);
4381 
4382 	len = strlen(HOSTFILTER) + strlen(host) + 1;
4383 	filter = malloc(len);
4384 	if (filter == NULL) {
4385 		return (NS_LDAP_MEMORY);
4386 	}
4387 	(void) snprintf(filter,	len, HOSTFILTER, host);
4388 
4389 	len = strlen(HOSTFILTER_SSD) + strlen(host) + 1;
4390 	userdata = malloc(len);
4391 	if (userdata == NULL) {
4392 		free(filter);
4393 		return (NS_LDAP_MEMORY);
4394 	}
4395 	(void) snprintf(userdata, len, HOSTFILTER_SSD, host);
4396 
4397 	/*
4398 	 * we want to retrieve the DN as it appears in LDAP
4399 	 * hence the use of NS_LDAP_NOT_CVT_DN in flags
4400 	 */
4401 	rc = __ns_ldap_list("hosts", filter,
4402 	    __s_api_merge_SSD_filter,
4403 	    NULL, cred, NS_LDAP_NOT_CVT_DN, &result,
4404 	    errorp, NULL,
4405 	    userdata);
4406 	free(filter);
4407 	filter = NULL;
4408 	free(userdata);
4409 	userdata = NULL;
4410 	if (rc != NS_LDAP_SUCCESS) {
4411 		if (result) {
4412 			(void) __ns_ldap_freeResult(&result);
4413 			result = NULL;
4414 		}
4415 		return (rc);
4416 	}
4417 
4418 	if (result->entries_count > 1) {
4419 		(void) __ns_ldap_freeResult(&result);
4420 		result = NULL;
4421 		*hostDN = NULL;
4422 		(void) sprintf(errstr,
4423 		    gettext("Too many entries are returned for %s"), host);
4424 		MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
4425 		    NS_LDAP_MEMORY);
4426 		return (NS_LDAP_INTERNAL);
4427 	}
4428 
4429 	value = __ns_ldap_getAttr(result->entry, "dn");
4430 	*hostDN = strdup(value[0]);
4431 	(void) __ns_ldap_freeResult(&result);
4432 	result = NULL;
4433 	return (NS_LDAP_SUCCESS);
4434 }
4435 
4436 int
4437 __ns_ldap_dn2domain(const char *dn, char **domain, const ns_cred_t *cred,
4438     ns_ldap_error_t **errorp)
4439 {
4440 	int		rc, pnum, i, j, len = 0;
4441 	char		*newdn, **rdns = NULL;
4442 	char		**dns, *dn1;
4443 
4444 	*errorp = NULL;
4445 
4446 	if (domain == NULL)
4447 		return (NS_LDAP_INVALID_PARAM);
4448 	else
4449 		*domain = NULL;
4450 
4451 	if ((dn == NULL) || (dn[0] == '\0'))
4452 		return (NS_LDAP_INVALID_PARAM);
4453 
4454 	/*
4455 	 * break dn into rdns
4456 	 */
4457 	dn1 = strdup(dn);
4458 	if (dn1 == NULL)
4459 		return (NS_LDAP_MEMORY);
4460 	rdns = ldap_explode_dn(dn1, 0);
4461 	free(dn1);
4462 	if (rdns == NULL || *rdns == NULL)
4463 		return (NS_LDAP_INVALID_PARAM);
4464 
4465 	for (i = 0; rdns[i]; i++)
4466 		len += strlen(rdns[i]) + 1;
4467 	pnum = i;
4468 
4469 	newdn = (char *)malloc(len + 1);
4470 	dns = (char **)calloc(pnum, sizeof (char *));
4471 	if (newdn == NULL || dns == NULL) {
4472 		if (newdn)
4473 			free(newdn);
4474 		ldap_value_free(rdns);
4475 		return (NS_LDAP_MEMORY);
4476 	}
4477 
4478 	/* construct a semi-normalized dn, newdn */
4479 	*newdn = '\0';
4480 	for (i = 0; rdns[i]; i++) {
4481 		dns[i] = newdn + strlen(newdn);
4482 		(void) strcat(newdn,
4483 		    __s_api_remove_rdn_space(rdns[i]));
4484 		(void) strcat(newdn, ",");
4485 	}
4486 	/* remove the last ',' */
4487 	newdn[strlen(newdn) - 1] = '\0';
4488 	ldap_value_free(rdns);
4489 
4490 	/*
4491 	 * loop and find the domain name associated with newdn,
4492 	 * removing rdn one by one from left to right
4493 	 */
4494 	for (i = 0; i < pnum; i++) {
4495 
4496 		if (*errorp)
4497 			(void) __ns_ldap_freeError(errorp);
4498 
4499 		/*
4500 		 *  try cache manager first
4501 		 */
4502 		rc = __s_api_get_cachemgr_data(NS_CACHE_DN2DOMAIN,
4503 		    dns[i], domain);
4504 		if (rc != NS_LDAP_SUCCESS) {
4505 			/*
4506 			 *  try ldap server second
4507 			 */
4508 			rc = __s_api_find_domainname(dns[i], domain,
4509 			    cred, errorp);
4510 		} else {
4511 			/*
4512 			 * skip the last one,
4513 			 * since it is already cached by ldap_cachemgr
4514 			 */
4515 			i--;
4516 		}
4517 		if (rc == NS_LDAP_SUCCESS) {
4518 			if (__s_api_nscd_proc()) {
4519 				/*
4520 				 * If it's nscd, ask cache manager to save the
4521 				 * dn to domain mapping(s)
4522 				 */
4523 				for (j = 0; j <= i; j++) {
4524 					(void) __s_api_set_cachemgr_data(
4525 					    NS_CACHE_DN2DOMAIN,
4526 					    dns[j],
4527 					    *domain);
4528 				}
4529 			}
4530 			break;
4531 		}
4532 	}
4533 
4534 	free(dns);
4535 	free(newdn);
4536 	if (rc != NS_LDAP_SUCCESS)
4537 		rc = NS_LDAP_NOTFOUND;
4538 	return (rc);
4539 }
4540 
4541 int
4542 __ns_ldap_getServiceAuthMethods(const char *service, ns_auth_t ***auth,
4543     ns_ldap_error_t **errorp)
4544 {
4545 	char		errstr[MAXERROR];
4546 	int		rc, i;
4547 	boolean_t	done = B_FALSE;
4548 	int		slen;
4549 	void		**param;
4550 	char		**sam, *srv, *send;
4551 	ns_auth_t	**authpp = NULL, *ap;
4552 	int		cnt, max;
4553 	ns_config_t	*cfg;
4554 	ns_ldap_error_t	*error = NULL;
4555 
4556 	if (errorp == NULL)
4557 		return (NS_LDAP_INVALID_PARAM);
4558 	*errorp = NULL;
4559 
4560 	if ((service == NULL) || (service[0] == '\0') ||
4561 	    (auth == NULL))
4562 		return (NS_LDAP_INVALID_PARAM);
4563 
4564 	*auth = NULL;
4565 	rc = __ns_ldap_getParam(NS_LDAP_SERVICE_AUTH_METHOD_P, &param, &error);
4566 	if (rc != NS_LDAP_SUCCESS || param == NULL) {
4567 		*errorp = error;
4568 		return (rc);
4569 	}
4570 	sam = (char **)param;
4571 
4572 	cfg = __s_api_get_default_config();
4573 	cnt = 0;
4574 
4575 	slen = strlen(service);
4576 
4577 	for (; *sam; sam++) {
4578 		srv = *sam;
4579 		if (strncasecmp(service, srv, slen) != 0)
4580 			continue;
4581 		srv += slen;
4582 		if (*srv != COLONTOK)
4583 			continue;
4584 		send = srv;
4585 		srv++;
4586 		for (max = 1; (send = strchr(++send, SEMITOK)) != NULL; max++)
4587 			;
4588 		authpp = (ns_auth_t **)calloc(++max, sizeof (ns_auth_t *));
4589 		if (authpp == NULL) {
4590 			(void) __ns_ldap_freeParam(&param);
4591 			__s_api_release_config(cfg);
4592 			return (NS_LDAP_MEMORY);
4593 		}
4594 		while (!done) {
4595 			send = strchr(srv, SEMITOK);
4596 			if (send != NULL) {
4597 				*send = '\0';
4598 				send++;
4599 			}
4600 			i = __s_get_enum_value(cfg, srv, NS_LDAP_AUTH_P);
4601 			if (i == -1) {
4602 				(void) __ns_ldap_freeParam(&param);
4603 				(void) sprintf(errstr,
4604 				gettext("Unsupported "
4605 				    "serviceAuthenticationMethod: %s.\n"), srv);
4606 				MKERROR(LOG_WARNING, *errorp, NS_CONFIG_SYNTAX,
4607 				    strdup(errstr), NS_LDAP_MEMORY);
4608 				__s_api_release_config(cfg);
4609 				return (NS_LDAP_CONFIG);
4610 			}
4611 			ap = __s_api_AuthEnumtoStruct((EnumAuthType_t)i);
4612 			if (ap == NULL) {
4613 				(void) __ns_ldap_freeParam(&param);
4614 				__s_api_release_config(cfg);
4615 				return (NS_LDAP_MEMORY);
4616 			}
4617 			authpp[cnt++] = ap;
4618 			if (send == NULL)
4619 				done = B_TRUE;
4620 			else
4621 				srv = send;
4622 		}
4623 	}
4624 
4625 	*auth = authpp;
4626 	(void) __ns_ldap_freeParam(&param);
4627 	__s_api_release_config(cfg);
4628 	return (NS_LDAP_SUCCESS);
4629 }
4630 
4631 /*
4632  * This routine is called when certain scenario occurs
4633  * e.g.
4634  * service == auto_home
4635  * SSD = automount: ou = mytest,
4636  * NS_LDAP_MAPATTRIBUTE= auto_home: automountMapName=AAA
4637  * NS_LDAP_OBJECTCLASSMAP= auto_home:automountMap=MynisMap
4638  * NS_LDAP_OBJECTCLASSMAP= auto_home:automount=MynisObject
4639  *
4640  * The automountMapName is prepended implicitely but is mapped
4641  * to AAA. So dn could appers as
4642  * dn: AAA=auto_home,ou=bar,dc=foo,dc=com
4643  * dn: automountKey=user_01,AAA=auto_home,ou=bar,dc=foo,dc=com
4644  * dn: automountKey=user_02,AAA=auto_home,ou=bar,dc=foo,dc=com
4645  * in the directory.
4646  * This function is called to covert the mapped attr back to
4647  * orig attr when the entries are searched and returned
4648  */
4649 
4650 int
4651 __s_api_convert_automountmapname(const char *service, char **dn,
4652     ns_ldap_error_t **errp)
4653 {
4654 
4655 	char	**mapping = NULL;
4656 	char	*mapped_attr = NULL;
4657 	char	*automountmapname = "automountMapName";
4658 	char	*buffer = NULL;
4659 	int	rc = NS_LDAP_SUCCESS;
4660 	char	errstr[MAXERROR];
4661 
4662 	/*
4663 	 * dn is an input/out parameter, check it first
4664 	 */
4665 
4666 	if (service == NULL || dn == NULL || *dn == NULL)
4667 		return (NS_LDAP_INVALID_PARAM);
4668 
4669 	/*
4670 	 * Check to see if there is a mapped attribute for auto_xxx
4671 	 */
4672 
4673 	mapping = __ns_ldap_getMappedAttributes(service, automountmapname);
4674 
4675 	/*
4676 	 * if no mapped attribute for auto_xxx, try automount
4677 	 */
4678 
4679 	if (mapping == NULL) {
4680 		mapping = __ns_ldap_getMappedAttributes(
4681 		    "automount", automountmapname);
4682 	}
4683 
4684 	/*
4685 	 * if no mapped attribute is found, return SUCCESS (no op)
4686 	 */
4687 
4688 	if (mapping == NULL)
4689 		return (NS_LDAP_SUCCESS);
4690 
4691 	/*
4692 	 * if the mapped attribute is found and attr is not empty,
4693 	 * copy it
4694 	 */
4695 
4696 	if (mapping[0] != NULL) {
4697 		mapped_attr = strdup(mapping[0]);
4698 		__s_api_free2dArray(mapping);
4699 		if (mapped_attr == NULL) {
4700 			return (NS_LDAP_MEMORY);
4701 		}
4702 	} else {
4703 		__s_api_free2dArray(mapping);
4704 
4705 		(void) snprintf(errstr, (2 * MAXERROR),
4706 		    gettext("Attribute nisMapName is mapped to an "
4707 		    "empty string.\n"));
4708 
4709 		MKERROR(LOG_ERR, *errp, NS_CONFIG_SYNTAX,
4710 		    strdup(errstr), NS_LDAP_MEMORY);
4711 
4712 		return (NS_LDAP_CONFIG);
4713 	}
4714 
4715 	/*
4716 	 * Locate the mapped attribute in the dn
4717 	 * and replace it if it exists
4718 	 */
4719 
4720 	rc = __s_api_replace_mapped_attr_in_dn(
4721 	    (const char *) automountmapname, (const char *) mapped_attr,
4722 	    (const char *) *dn, &buffer);
4723 
4724 	/* clean up */
4725 
4726 	free(mapped_attr);
4727 
4728 	/*
4729 	 * If mapped attr is found(buffer != NULL)
4730 	 *	a new dn is returned
4731 	 * If no mapped attribute is in dn,
4732 	 *	return NS_LDAP_SUCCESS (no op)
4733 	 * If no memory,
4734 	 *	return NS_LDAP_MEMORY (no op)
4735 	 */
4736 
4737 	if (buffer != NULL) {
4738 		free(*dn);
4739 		*dn = buffer;
4740 	}
4741 
4742 	return (rc);
4743 }
4744 
4745 /*
4746  * If the mapped attr is found in the dn,
4747  *	return NS_LDAP_SUCCESS and a new_dn.
4748  * If no mapped attr is found,
4749  *	return NS_LDAP_SUCCESS and *new_dn == NULL
4750  * If there is not enough memory,
4751  *	return NS_LDAP_MEMORY and *new_dn == NULL
4752  */
4753 
4754 int
4755 __s_api_replace_mapped_attr_in_dn(const char *orig_attr,
4756     const char *mapped_attr, const char *dn, char **new_dn)
4757 {
4758 
4759 	char	**dnArray = NULL;
4760 	char	*cur = NULL, *start = NULL;
4761 	int	i = 0;
4762 	boolean_t found = B_FALSE;
4763 	int	len = 0, orig_len = 0, mapped_len = 0;
4764 	int	dn_len = 0, tmp_len = 0;
4765 
4766 	*new_dn = NULL;
4767 
4768 	/*
4769 	 * seperate dn into individual componets
4770 	 * e.g.
4771 	 * "automountKey=user_01" , "automountMapName_test=auto_home", ...
4772 	 */
4773 	dnArray = ldap_explode_dn(dn, 0);
4774 
4775 	/*
4776 	 * This will find "mapped attr=value" in dn.
4777 	 * It won't find match if mapped attr appears
4778 	 * in the value.
4779 	 */
4780 	for (i = 0; dnArray[i] != NULL; i++) {
4781 		/*
4782 		 * This function is called when reading from
4783 		 * the directory so assume each component has "=".
4784 		 * Any ill formatted dn should be rejected
4785 		 * before adding to the directory
4786 		 */
4787 		cur = strchr(dnArray[i], '=');
4788 		*cur = '\0';
4789 		if (strcasecmp(mapped_attr, dnArray[i]) == 0)
4790 			found = B_TRUE;
4791 		*cur = '=';
4792 		if (found)
4793 			break;
4794 	}
4795 
4796 	if (!found) {
4797 		__s_api_free2dArray(dnArray);
4798 		*new_dn = NULL;
4799 		return (NS_LDAP_SUCCESS);
4800 	}
4801 	/*
4802 	 * The new length is *dn length + (difference between
4803 	 * orig attr and mapped attr) + 1 ;
4804 	 * e.g.
4805 	 * automountKey=aa,automountMapName_test=auto_home,dc=foo,dc=com
4806 	 * ==>
4807 	 * automountKey=aa,automountMapName=auto_home,dc=foo,dc=com
4808 	 */
4809 	mapped_len = strlen(mapped_attr);
4810 	orig_len = strlen(orig_attr);
4811 	dn_len = strlen(dn);
4812 	len = dn_len + orig_len - mapped_len + 1;
4813 	*new_dn = (char *)calloc(1, len);
4814 	if (*new_dn == NULL) {
4815 		__s_api_free2dArray(dnArray);
4816 		return (NS_LDAP_MEMORY);
4817 	}
4818 
4819 	/*
4820 	 * Locate the mapped attr in the dn.
4821 	 * Use dnArray[i] instead of mapped_attr
4822 	 * because mapped_attr could appear in
4823 	 * the value
4824 	 */
4825 
4826 	cur = strstr(dn, dnArray[i]);
4827 	__s_api_free2dArray(dnArray);
4828 	/* copy the portion before mapped attr in dn  */
4829 	start = *new_dn;
4830 	tmp_len = cur - dn;
4831 	(void) memcpy(start, dn, tmp_len);
4832 
4833 	/*
4834 	 * Copy the orig_attr. e.g. automountMapName
4835 	 * This replaces mapped attr with orig attr
4836 	 */
4837 	start = start + (cur - dn); /* move cursor in buffer */
4838 	(void) memcpy(start, orig_attr, orig_len);
4839 
4840 	/*
4841 	 * Copy the portion after mapped attr in dn
4842 	 */
4843 	cur = cur + mapped_len; /* move cursor in  dn  */
4844 	start = start + orig_len; /* move cursor in buffer */
4845 	(void) strcpy(start, cur);
4846 
4847 	return (NS_LDAP_SUCCESS);
4848 }
4849 
4850 /*
4851  * Validate Filter functions
4852  */
4853 
4854 /* ***** Start of modified libldap.so.5 filter parser ***** */
4855 
4856 /* filter parsing routine forward references */
4857 static int adj_filter_list(char *str);
4858 static int adj_simple_filter(char *str);
4859 static int unescape_filterval(char *val);
4860 static int hexchar2int(char c);
4861 static int adj_substring_filter(char *val);
4862 
4863 
4864 /*
4865  * assumes string manipulation is in-line
4866  * and all strings are sufficient in size
4867  * return value is the position after 'c'
4868  */
4869 
4870 static char *
4871 resync_str(char *str, char *next, char c)
4872 {
4873 	char	*ret;
4874 
4875 	ret = str + strlen(str);
4876 	*next = c;
4877 	if (ret == next)
4878 		return (ret);
4879 	(void) strcat(str, next);
4880 	return (ret);
4881 }
4882 
4883 static char *
4884 find_right_paren(char *s)
4885 {
4886 	int balance;
4887 	boolean_t escape;
4888 
4889 	balance = 1;
4890 	escape = B_FALSE;
4891 	while (*s && balance) {
4892 		if (escape == B_FALSE) {
4893 			if (*s == '(')
4894 				balance++;
4895 			else if (*s == ')')
4896 				balance--;
4897 		}
4898 		if (*s == '\\' && !escape)
4899 			escape = B_TRUE;
4900 		else
4901 			escape = B_FALSE;
4902 		if (balance)
4903 			s++;
4904 	}
4905 
4906 	return (*s ? s : NULL);
4907 }
4908 
4909 static char *
4910 adj_complex_filter(char	*str)
4911 {
4912 	char	*next;
4913 
4914 	/*
4915 	 * We have (x(filter)...) with str sitting on
4916 	 * the x.  We have to find the paren matching
4917 	 * the one before the x and put the intervening
4918 	 * filters by calling adj_filter_list().
4919 	 */
4920 
4921 	str++;
4922 	if ((next = find_right_paren(str)) == NULL)
4923 		return (NULL);
4924 
4925 	*next = '\0';
4926 	if (adj_filter_list(str) == -1)
4927 		return (NULL);
4928 	next = resync_str(str, next, ')');
4929 	next++;
4930 
4931 	return (next);
4932 }
4933 
4934 static int
4935 adj_filter(char *str)
4936 {
4937 	char *next;
4938 	int parens, balance;
4939 	boolean_t escape;
4940 	char *np, *cp,  *dp;
4941 
4942 	parens = 0;
4943 	while (*str) {
4944 		switch (*str) {
4945 		case '(':
4946 			str++;
4947 			parens++;
4948 			switch (*str) {
4949 			case '&':
4950 				if ((str = adj_complex_filter(str)) == NULL)
4951 					return (-1);
4952 
4953 				parens--;
4954 				break;
4955 
4956 			case '|':
4957 				if ((str = adj_complex_filter(str)) == NULL)
4958 					return (-1);
4959 
4960 				parens--;
4961 				break;
4962 
4963 			case '!':
4964 				if ((str = adj_complex_filter(str)) == NULL)
4965 					return (-1);
4966 
4967 				parens--;
4968 				break;
4969 
4970 			case '(':
4971 				/* illegal ((case - generated by conversion */
4972 
4973 				/* find missing close) */
4974 				np = find_right_paren(str+1);
4975 
4976 				/* error if not found */
4977 				if (np == NULL)
4978 					return (-1);
4979 
4980 				/* remove redundant (and) */
4981 				for (dp = str, cp = str+1; cp < np; ) {
4982 					*dp++ = *cp++;
4983 				}
4984 				cp++;
4985 				while (*cp)
4986 					*dp++ = *cp++;
4987 				*dp = '\0';
4988 
4989 				/* re-start test at original ( */
4990 				parens--;
4991 				str--;
4992 				break;
4993 
4994 			default:
4995 				balance = 1;
4996 				escape = B_FALSE;
4997 				next = str;
4998 				while (*next && balance) {
4999 					if (escape == B_FALSE) {
5000 						if (*next == '(')
5001 							balance++;
5002 						else if (*next == ')')
5003 							balance--;
5004 					}
5005 					if (*next == '\\' && !escape)
5006 						escape = B_TRUE;
5007 					else
5008 						escape = B_FALSE;
5009 					if (balance)
5010 						next++;
5011 				}
5012 				if (balance != 0)
5013 					return (-1);
5014 
5015 				*next = '\0';
5016 				if (adj_simple_filter(str) == -1) {
5017 					return (-1);
5018 				}
5019 				next = resync_str(str, next, ')');
5020 				next++;
5021 				str = next;
5022 				parens--;
5023 				break;
5024 			}
5025 			break;
5026 
5027 		case ')':
5028 			str++;
5029 			parens--;
5030 			break;
5031 
5032 		case ' ':
5033 			str++;
5034 			break;
5035 
5036 		default:	/* assume it's a simple type=value filter */
5037 			next = strchr(str, '\0');
5038 			if (adj_simple_filter(str) == -1) {
5039 				return (-1);
5040 			}
5041 			str = next;
5042 			break;
5043 		}
5044 	}
5045 
5046 	return (parens ? -1 : 0);
5047 }
5048 
5049 
5050 /*
5051  * Put a list of filters like this "(filter1)(filter2)..."
5052  */
5053 
5054 static int
5055 adj_filter_list(char *str)
5056 {
5057 	char	*next;
5058 	char	save;
5059 
5060 	while (*str) {
5061 		while (*str && isspace(*str))
5062 			str++;
5063 		if (*str == '\0')
5064 			break;
5065 
5066 		if ((next = find_right_paren(str + 1)) == NULL)
5067 			return (-1);
5068 		save = *++next;
5069 
5070 		/* now we have "(filter)" with str pointing to it */
5071 		*next = '\0';
5072 		if (adj_filter(str) == -1)
5073 			return (-1);
5074 		next = resync_str(str, next, save);
5075 
5076 		str = next;
5077 	}
5078 
5079 	return (0);
5080 }
5081 
5082 
5083 /*
5084  * is_valid_attr - returns 1 if a is a syntactically valid left-hand side
5085  * of a filter expression, 0 otherwise.  A valid string may contain only
5086  * letters, numbers, hyphens, semi-colons, colons and periods. examples:
5087  *	cn
5088  *	cn;lang-fr
5089  *	1.2.3.4;binary;dynamic
5090  *	mail;dynamic
5091  *	cn:dn:1.2.3.4
5092  *
5093  * For compatibility with older servers, we also allow underscores in
5094  * attribute types, even through they are not allowed by the LDAPv3 RFCs.
5095  */
5096 static int
5097 is_valid_attr(char *a)
5098 {
5099 	for (; *a; a++) {
5100 		if (!isascii(*a)) {
5101 			return (0);
5102 		} else if (!isalnum(*a)) {
5103 			switch (*a) {
5104 			case '-':
5105 			case '.':
5106 			case ';':
5107 			case ':':
5108 			case '_':
5109 				break; /* valid */
5110 			default:
5111 				return (0);
5112 			}
5113 		}
5114 	}
5115 	return (1);
5116 }
5117 
5118 static char *
5119 find_star(char *s)
5120 {
5121 	for (; *s; ++s) {
5122 		switch (*s) {
5123 		case '*':
5124 			return (s);
5125 		case '\\':
5126 			++s;
5127 			if (hexchar2int(s[0]) >= 0 && hexchar2int(s[1]) >= 0)
5128 				++s;
5129 		default:
5130 			break;
5131 		}
5132 	}
5133 	return (NULL);
5134 }
5135 
5136 static int
5137 adj_simple_filter(char *str)
5138 {
5139 	char		*s, *s2, *s3, filterop;
5140 	char		*value;
5141 	int		ftype = 0;
5142 	int		rc;
5143 
5144 	rc = -1;	/* pessimistic */
5145 
5146 	if ((str = strdup(str)) == NULL) {
5147 		return (rc);
5148 	}
5149 
5150 	if ((s = strchr(str, '=')) == NULL) {
5151 		goto free_and_return;
5152 	}
5153 	value = s + 1;
5154 	*s-- = '\0';
5155 	filterop = *s;
5156 	if (filterop == '<' || filterop == '>' || filterop == '~' ||
5157 	    filterop == ':') {
5158 		*s = '\0';
5159 	}
5160 
5161 	if (!is_valid_attr(str)) {
5162 		goto free_and_return;
5163 	}
5164 
5165 	switch (filterop) {
5166 	case '<': /* LDAP_FILTER_LE */
5167 	case '>': /* LDAP_FILTER_GE */
5168 	case '~': /* LDAP_FILTER_APPROX */
5169 		break;
5170 	case ':':	/* extended filter - v3 only */
5171 		/*
5172 		 * extended filter looks like this:
5173 		 *
5174 		 *	[type][':dn'][':'oid]':='value
5175 		 *
5176 		 * where one of type or :oid is required.
5177 		 *
5178 		 */
5179 		s2 = s3 = NULL;
5180 		if ((s2 = strrchr(str, ':')) == NULL) {
5181 			goto free_and_return;
5182 		}
5183 		if (strcasecmp(s2, ":dn") == 0) {
5184 			*s2 = '\0';
5185 		} else {
5186 			*s2 = '\0';
5187 			if ((s3 = strrchr(str, ':')) != NULL) {
5188 				if (strcasecmp(s3, ":dn") != 0) {
5189 					goto free_and_return;
5190 				}
5191 				*s3 = '\0';
5192 			}
5193 		}
5194 		if (unescape_filterval(value) < 0) {
5195 			goto free_and_return;
5196 		}
5197 		rc = 0;
5198 		goto free_and_return;
5199 		/* break; */
5200 	default:
5201 		if (find_star(value) == NULL) {
5202 			ftype = 0; /* LDAP_FILTER_EQUALITY */
5203 		} else if (strcmp(value, "*") == 0) {
5204 			ftype = 1; /* LDAP_FILTER_PRESENT */
5205 		} else {
5206 			rc = adj_substring_filter(value);
5207 			goto free_and_return;
5208 		}
5209 		break;
5210 	}
5211 
5212 	if (ftype != 0) {	/* == LDAP_FILTER_PRESENT */
5213 		rc = 0;
5214 	} else if (unescape_filterval(value) >= 0) {
5215 		rc = 0;
5216 	}
5217 	if (rc != -1) {
5218 		rc = 0;
5219 	}
5220 
5221 free_and_return:
5222 	free(str);
5223 	return (rc);
5224 }
5225 
5226 
5227 /*
5228  * Check in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape
5229  * sequences within the null-terminated string 'val'.
5230  *
5231  * If 'val' contains invalid escape sequences we return -1.
5232  * Otherwise return 1
5233  */
5234 static int
5235 unescape_filterval(char *val)
5236 {
5237 	boolean_t escape, firstdigit;
5238 	char *s;
5239 
5240 	firstdigit = B_FALSE;
5241 	escape = B_FALSE;
5242 	for (s = val; *s; s++) {
5243 		if (escape) {
5244 			/*
5245 			 * first try LDAPv3 escape (hexadecimal) sequence
5246 			 */
5247 			if (hexchar2int(*s) < 0) {
5248 				if (firstdigit) {
5249 					/*
5250 					 * LDAPv2 (RFC1960) escape sequence
5251 					 */
5252 					escape = B_FALSE;
5253 				} else {
5254 					return (-1);
5255 				}
5256 			}
5257 			if (firstdigit) {
5258 				firstdigit = B_FALSE;
5259 			} else {
5260 				escape = B_FALSE;
5261 			}
5262 
5263 		} else if (*s != '\\') {
5264 			escape = B_FALSE;
5265 
5266 		} else {
5267 			escape = B_TRUE;
5268 			firstdigit = B_TRUE;
5269 		}
5270 	}
5271 
5272 	return (1);
5273 }
5274 
5275 
5276 /*
5277  * convert character 'c' that represents a hexadecimal digit to an integer.
5278  * if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned.
5279  * otherwise the converted value is returned.
5280  */
5281 static int
5282 hexchar2int(char c)
5283 {
5284 	if (c >= '0' && c <= '9') {
5285 		return (c - '0');
5286 	}
5287 	if (c >= 'A' && c <= 'F') {
5288 		return (c - 'A' + 10);
5289 	}
5290 	if (c >= 'a' && c <= 'f') {
5291 		return (c - 'a' + 10);
5292 	}
5293 	return (-1);
5294 }
5295 
5296 static int
5297 adj_substring_filter(char *val)
5298 {
5299 	char		*nextstar;
5300 
5301 	for (; val != NULL; val = nextstar) {
5302 		if ((nextstar = find_star(val)) != NULL) {
5303 			*nextstar++ = '\0';
5304 		}
5305 
5306 		if (*val != '\0') {
5307 			if (unescape_filterval(val) < 0) {
5308 				return (-1);
5309 			}
5310 		}
5311 	}
5312 
5313 	return (0);
5314 }
5315 
5316 /* ***** End of modified libldap.so.5 filter parser ***** */
5317 
5318 
5319 /*
5320  * Walk filter, remove redundant parentheses in-line
5321  * verify that the filter is reasonable
5322  */
5323 static int
5324 validate_filter(ns_ldap_cookie_t *cookie)
5325 {
5326 	char			*filter = cookie->filter;
5327 	int			rc;
5328 
5329 	/* Parse filter looking for illegal values */
5330 
5331 	rc = adj_filter(filter);
5332 	if (rc != 0) {
5333 		return (NS_LDAP_OP_FAILED);
5334 	}
5335 
5336 	/* end of filter checking */
5337 
5338 	return (NS_LDAP_SUCCESS);
5339 }
5340 
5341 /*
5342  * Set the account management request control that needs to be sent to server.
5343  * This control is required to get the account management information of
5344  * a user to do local account checking.
5345  */
5346 static int
5347 setup_acctmgmt_params(ns_ldap_cookie_t *cookie)
5348 {
5349 	LDAPControl	*req, **requestctrls;
5350 
5351 	req = calloc(1, sizeof (LDAPControl));
5352 
5353 	if (req == NULL)
5354 		return (NS_LDAP_MEMORY);
5355 
5356 	/* fill in the fields of this new control */
5357 	req->ldctl_iscritical = 1;
5358 	req->ldctl_oid = strdup(NS_LDAP_ACCOUNT_USABLE_CONTROL);
5359 	if (req->ldctl_oid == NULL) {
5360 		free(req);
5361 		return (NS_LDAP_MEMORY);
5362 	}
5363 
5364 	requestctrls = (LDAPControl **)calloc(2, sizeof (LDAPControl *));
5365 	if (requestctrls == NULL) {
5366 		ldap_control_free(req);
5367 		return (NS_LDAP_MEMORY);
5368 	}
5369 
5370 	requestctrls[0] = req;
5371 
5372 	cookie->p_serverctrls = requestctrls;
5373 
5374 	return (NS_LDAP_SUCCESS);
5375 }
5376 
5377 /*
5378  * int get_new_acct_more_info(BerElement *ber,
5379  *     AcctUsableResponse_t *acctResp)
5380  *
5381  * Decode the more_info data from an Account Management control response,
5382  * when the account is not usable and when code style is from recent LDAP
5383  * servers (see below comments for parse_acct_cont_resp_msg() to get more
5384  * details on coding styles and ASN1 description).
5385  *
5386  * Expected BER encoding: {tbtbtbtiti}
5387  *      +t: tag is 0
5388  *	+b: TRUE if inactive due to account inactivation
5389  *      +t: tag is 1
5390  *	+b: TRUE if password has been reset
5391  *      +t: tag is 2
5392  *	+b: TRUE if password is expired
5393  *	+t: tag is 3
5394  *	+i: contains num of remaining grace, 0 means no grace
5395  *	+t: tag is 4
5396  *	+i: contains num of seconds before auto-unlock. -1 means acct is locked
5397  *		forever (i.e. until reset)
5398  *
5399  * Asumptions:
5400  * - ber is not null
5401  * - acctResp is not null and is initialized with default values for the
5402  *   fields in its AcctUsableResp.more_info structure
5403  * - the ber stream is received in the correct order, per the ASN1 description.
5404  *   We do not check this order and make the asumption that it is correct.
5405  *   Note that the ber stream may not (and will not in most cases) contain
5406  *   all fields.
5407  */
5408 static int
5409 get_new_acct_more_info(BerElement *ber, AcctUsableResponse_t *acctResp)
5410 {
5411 	int		rc = NS_LDAP_SUCCESS;
5412 	char		errstr[MAXERROR];
5413 	ber_tag_t	rTag = LBER_DEFAULT;
5414 	ber_len_t	rLen = 0;
5415 	ber_int_t	rValue;
5416 	char		*last;
5417 	int		berRC = 0;
5418 
5419 	/*
5420 	 * Look at what more_info BER element is/are left to be decoded.
5421 	 * look at each of them 1 by 1, without checking on their order
5422 	 * and possible multi values.
5423 	 */
5424 	for (rTag = ber_first_element(ber, &rLen, &last);
5425 	    rTag != LBER_END_OF_SEQORSET;
5426 	    rTag = ber_next_element(ber, &rLen, last)) {
5427 
5428 		berRC = 0;
5429 		switch (rTag) {
5430 		case 0 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5431 			/* inactive */
5432 			berRC = ber_scanf(ber, "b", &rValue);
5433 			if (berRC != LBER_ERROR) {
5434 				(acctResp->AcctUsableResp).more_info.
5435 				    inactive = (rValue != 0) ? 1 : 0;
5436 			}
5437 			break;
5438 
5439 		case 1 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5440 			/* reset */
5441 			berRC = ber_scanf(ber, "b", &rValue);
5442 			if (berRC != LBER_ERROR) {
5443 				(acctResp->AcctUsableResp).more_info.reset
5444 				    = (rValue != 0) ? 1 : 0;
5445 			}
5446 			break;
5447 
5448 		case 2 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5449 			/* expired */
5450 			berRC = ber_scanf(ber, "b", &rValue);
5451 			if (berRC != LBER_ERROR) {
5452 				(acctResp->AcctUsableResp).more_info.expired
5453 				    = (rValue != 0) ? 1 : 0;
5454 			}
5455 			break;
5456 
5457 		case 3 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5458 			/* remaining grace */
5459 			berRC = ber_scanf(ber, "i", &rValue);
5460 			if (berRC != LBER_ERROR) {
5461 				(acctResp->AcctUsableResp).more_info.rem_grace
5462 				    = rValue;
5463 			}
5464 			break;
5465 
5466 		case 4 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
5467 			/* seconds before unlock */
5468 			berRC = ber_scanf(ber, "i", &rValue);
5469 			if (berRC != LBER_ERROR) {
5470 				(acctResp->AcctUsableResp).more_info.
5471 				    sec_b4_unlock = rValue;
5472 			}
5473 			break;
5474 
5475 		default :
5476 			(void) sprintf(errstr,
5477 			    gettext("invalid reason tag 0x%x"), rTag);
5478 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
5479 			rc = NS_LDAP_INTERNAL;
5480 			break;
5481 		}
5482 		if (berRC == LBER_ERROR) {
5483 			(void) sprintf(errstr,
5484 			    gettext("error 0x%x decoding value for "
5485 			    "tag 0x%x"), berRC, rTag);
5486 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
5487 			rc = NS_LDAP_INTERNAL;
5488 		}
5489 		if (rc != NS_LDAP_SUCCESS) {
5490 			/* exit the for loop */
5491 			break;
5492 		}
5493 	}
5494 
5495 	return (rc);
5496 }
5497 
5498 /*
5499  * int get_old_acct_opt_more_info(BerElement *ber,
5500  *     AcctUsableResponse_t *acctResp)
5501  *
5502  * Decode the optional more_info data from an Account Management control
5503  * response, when the account is not usable and when code style is from LDAP
5504  * server 5.2p4 (see below comments for parse_acct_cont_resp_msg() to get more
5505  * details on coding styles and ASN1 description).
5506  *
5507  * Expected BER encoding: titi}
5508  *	+t: tag is 2
5509  *	+i: contains num of remaining grace, 0 means no grace
5510  *	+t: tag is 3
5511  *	+i: contains num of seconds before auto-unlock. -1 means acct is locked
5512  *		forever (i.e. until reset)
5513  *
5514  * Asumptions:
5515  * - ber is a valid BER element
5516  * - acctResp is initialized for the fields in its AcctUsableResp.more_info
5517  *   structure
5518  */
5519 static int
5520 get_old_acct_opt_more_info(ber_tag_t tag, BerElement *ber,
5521     AcctUsableResponse_t *acctResp)
5522 {
5523 	int		rc = NS_LDAP_SUCCESS;
5524 	char		errstr[MAXERROR];
5525 	ber_len_t	len;
5526 	int		rem_grace, sec_b4_unlock;
5527 
5528 	switch (tag) {
5529 	case 2:
5530 		/* decode and maybe 3 is following */
5531 		if ((tag = ber_scanf(ber, "i", &rem_grace)) == LBER_ERROR) {
5532 			(void) sprintf(errstr, gettext("Can not get "
5533 			    "rem_grace"));
5534 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
5535 			rc = NS_LDAP_INTERNAL;
5536 			break;
5537 		}
5538 		(acctResp->AcctUsableResp).more_info.rem_grace = rem_grace;
5539 
5540 		if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5541 			/* this is a success case, break to exit */
5542 			(void) sprintf(errstr, gettext("No more "
5543 			    "optional data"));
5544 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
5545 			break;
5546 		}
5547 
5548 		if (tag == 3) {
5549 			if (ber_scanf(ber, "i", &sec_b4_unlock) == LBER_ERROR) {
5550 				(void) sprintf(errstr,
5551 				    gettext("Can not get sec_b4_unlock "
5552 				    "- 1st case"));
5553 				syslog(LOG_DEBUG, "libsldap: %s", errstr);
5554 				rc = NS_LDAP_INTERNAL;
5555 				break;
5556 			}
5557 			(acctResp->AcctUsableResp).more_info.sec_b4_unlock =
5558 			    sec_b4_unlock;
5559 		} else { /* unknown tag */
5560 			(void) sprintf(errstr, gettext("Unknown tag "
5561 			    "- 1st case"));
5562 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
5563 			rc = NS_LDAP_INTERNAL;
5564 			break;
5565 		}
5566 		break;
5567 
5568 	case 3:
5569 		if (ber_scanf(ber, "i", &sec_b4_unlock) == LBER_ERROR) {
5570 			(void) sprintf(errstr, gettext("Can not get "
5571 			    "sec_b4_unlock - 2nd case"));
5572 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
5573 			rc = NS_LDAP_INTERNAL;
5574 			break;
5575 		}
5576 		(acctResp->AcctUsableResp).more_info.sec_b4_unlock =
5577 		    sec_b4_unlock;
5578 		break;
5579 
5580 	default: /* unknown tag */
5581 		(void) sprintf(errstr, gettext("Unknown tag - 2nd case"));
5582 		syslog(LOG_DEBUG, "libsldap: %s", errstr);
5583 		rc = NS_LDAP_INTERNAL;
5584 		break;
5585 	}
5586 
5587 	return (rc);
5588 }
5589 
5590 /*
5591  * **** This function needs to be moved to libldap library ****
5592  * parse_acct_cont_resp_msg() parses the message received by server according to
5593  * following format (ASN1 notation):
5594  *
5595  *	ACCOUNT_USABLE_RESPONSE::= CHOICE {
5596  *		is_available		[0] INTEGER,
5597  *				** seconds before expiration **
5598  *		is_not_available	[1] more_info
5599  *	}
5600  *	more_info::= SEQUENCE {
5601  *		inactive		[0] BOOLEAN DEFAULT FALSE,
5602  *		reset			[1] BOOLEAN DEFAULT FALSE,
5603  *		expired			[2] BOOLEAN DEFAULT FALSE,
5604  *		remaining_grace		[3] INTEGER OPTIONAL,
5605  *		seconds_before_unlock	[4] INTEGER OPTIONAL
5606  *	}
5607  */
5608 /*
5609  * #define used to make the difference between coding style as done
5610  * by LDAP server 5.2p4 and newer LDAP servers. There are 4 values:
5611  * - DS52p4_USABLE: 5.2p4 coding style, account is usable
5612  * - DS52p4_NOT_USABLE: 5.2p4 coding style, account is not usable
5613  * - NEW_USABLE: newer LDAP servers coding style, account is usable
5614  * - NEW_NOT_USABLE: newer LDAP servers coding style, account is not usable
5615  *
5616  * An account would be considered not usable if for instance:
5617  * - it's been made inactive in the LDAP server
5618  * - or its password was reset in the LDAP server database
5619  * - or its password expired
5620  * - or the account has been locked, possibly forever
5621  */
5622 #define	DS52p4_USABLE		0x00
5623 #define	DS52p4_NOT_USABLE	0x01
5624 #define	NEW_USABLE		0x00 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE
5625 #define	NEW_NOT_USABLE		0x01 | LBER_CLASS_CONTEXT | LBER_CONSTRUCTED
5626 static int
5627 parse_acct_cont_resp_msg(LDAPControl **ectrls, AcctUsableResponse_t *acctResp)
5628 {
5629 	int		rc = NS_LDAP_SUCCESS;
5630 	BerElement	*ber;
5631 	ber_tag_t	tag;
5632 	ber_len_t	len;
5633 	int		i;
5634 	char		errstr[MAXERROR];
5635 	/* used for any coding style when account is usable */
5636 	int		seconds_before_expiry;
5637 	/* used for 5.2p4 coding style when account is not usable */
5638 	int		inactive, reset, expired;
5639 
5640 	if (ectrls == NULL) {
5641 		(void) sprintf(errstr, gettext("Invalid ectrls parameter"));
5642 		syslog(LOG_DEBUG, "libsldap: %s", errstr);
5643 		return (NS_LDAP_INVALID_PARAM);
5644 	}
5645 
5646 	for (i = 0; ectrls[i] != NULL; i++) {
5647 		if (strcmp(ectrls[i]->ldctl_oid, NS_LDAP_ACCOUNT_USABLE_CONTROL)
5648 		    == 0) {
5649 			break;
5650 		}
5651 	}
5652 
5653 	if (ectrls[i] == NULL) {
5654 		/* Ldap control is not found */
5655 		(void) sprintf(errstr, gettext("Account Usable Control "
5656 		    "not found"));
5657 		syslog(LOG_DEBUG, "libsldap: %s", errstr);
5658 		return (NS_LDAP_NOTFOUND);
5659 	}
5660 
5661 	/* Allocate a BER element from the control value and parse it. */
5662 	if ((ber = ber_init(&ectrls[i]->ldctl_value)) == NULL)
5663 		return (NS_LDAP_MEMORY);
5664 
5665 	if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5666 		/* Ldap decoding error */
5667 		(void) sprintf(errstr, gettext("Error decoding 1st tag"));
5668 		syslog(LOG_DEBUG, "libsldap: %s", errstr);
5669 		ber_free(ber, 1);
5670 		return (NS_LDAP_INTERNAL);
5671 	}
5672 
5673 	switch (tag) {
5674 	case DS52p4_USABLE:
5675 	case NEW_USABLE:
5676 		acctResp->choice = 0;
5677 		if (ber_scanf(ber, "i", &seconds_before_expiry)
5678 		    == LBER_ERROR) {
5679 			/* Ldap decoding error */
5680 			(void) sprintf(errstr, gettext("Can not get "
5681 			    "seconds_before_expiry"));
5682 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
5683 			rc = NS_LDAP_INTERNAL;
5684 			break;
5685 		}
5686 		/* ber_scanf() succeeded */
5687 		(acctResp->AcctUsableResp).seconds_before_expiry =
5688 		    seconds_before_expiry;
5689 		break;
5690 
5691 	case DS52p4_NOT_USABLE:
5692 		acctResp->choice = 1;
5693 		if (ber_scanf(ber, "{bbb", &inactive, &reset, &expired)
5694 		    == LBER_ERROR) {
5695 			/* Ldap decoding error */
5696 			(void) sprintf(errstr, gettext("Can not get "
5697 			    "inactive/reset/expired"));
5698 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
5699 			rc = NS_LDAP_INTERNAL;
5700 			break;
5701 		}
5702 		/* ber_scanf() succeeded */
5703 		(acctResp->AcctUsableResp).more_info.inactive =
5704 		    ((inactive == 0) ? 0 : 1);
5705 		(acctResp->AcctUsableResp).more_info.reset =
5706 		    ((reset == 0) ? 0 : 1);
5707 		(acctResp->AcctUsableResp).more_info.expired =
5708 		    ((expired == 0) ? 0 : 1);
5709 		(acctResp->AcctUsableResp).more_info.rem_grace = 0;
5710 		(acctResp->AcctUsableResp).more_info.sec_b4_unlock = 0;
5711 
5712 		if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
5713 			/* this is a success case, break to exit */
5714 			(void) sprintf(errstr, gettext("No optional data"));
5715 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
5716 			break;
5717 		}
5718 
5719 		/*
5720 		 * Look at what optional more_info BER element is/are
5721 		 * left to be decoded.
5722 		 */
5723 		rc = get_old_acct_opt_more_info(tag, ber, acctResp);
5724 		break;
5725 
5726 	case NEW_NOT_USABLE:
5727 		acctResp->choice = 1;
5728 		/*
5729 		 * Recent LDAP servers won't code more_info data for default
5730 		 * values (see above comments on ASN1 description for what
5731 		 * fields have default values & what fields are optional).
5732 		 */
5733 		(acctResp->AcctUsableResp).more_info.inactive = 0;
5734 		(acctResp->AcctUsableResp).more_info.reset = 0;
5735 		(acctResp->AcctUsableResp).more_info.expired = 0;
5736 		(acctResp->AcctUsableResp).more_info.rem_grace = 0;
5737 		(acctResp->AcctUsableResp).more_info.sec_b4_unlock = 0;
5738 
5739 		if (len == 0) {
5740 			/*
5741 			 * Nothing else to decode; this is valid and we
5742 			 * use default values set above.
5743 			 */
5744 			(void) sprintf(errstr, gettext("more_info is "
5745 			    "empty, using default values"));
5746 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
5747 			break;
5748 		}
5749 
5750 		/*
5751 		 * Look at what more_info BER element is/are left to
5752 		 * be decoded.
5753 		 */
5754 		rc = get_new_acct_more_info(ber, acctResp);
5755 		break;
5756 
5757 	default:
5758 		(void) sprintf(errstr, gettext("unknwon coding style "
5759 		    "(tag: 0x%x)"), tag);
5760 		syslog(LOG_DEBUG, "libsldap: %s", errstr);
5761 		rc = NS_LDAP_INTERNAL;
5762 		break;
5763 	}
5764 
5765 	ber_free(ber, 1);
5766 	return (rc);
5767 }
5768 
5769 /*
5770  * internal function for __ns_ldap_getAcctMgmt()
5771  */
5772 static int
5773 getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp,
5774     ns_conn_user_t *conn_user)
5775 {
5776 	int		scope, rc;
5777 	ns_ldap_cookie_t	*cookie;
5778 	ns_ldap_search_desc_t	**sdlist = NULL;
5779 	ns_ldap_search_desc_t	*dptr;
5780 	ns_ldap_error_t		*error = NULL;
5781 	char			**dns = NULL;
5782 	char		service[] = "shadow";
5783 
5784 	if (user == NULL || acctResp == NULL)
5785 		return (NS_LDAP_INVALID_PARAM);
5786 
5787 	/* Initialize State machine cookie */
5788 	cookie = init_search_state_machine();
5789 	if (cookie == NULL)
5790 		return (NS_LDAP_MEMORY);
5791 	cookie->conn_user = conn_user;
5792 
5793 	/* see if need to follow referrals */
5794 	rc = __s_api_toFollowReferrals(0,
5795 	    &cookie->followRef, &error);
5796 	if (rc != NS_LDAP_SUCCESS) {
5797 		(void) __ns_ldap_freeError(&error);
5798 		goto out;
5799 	}
5800 
5801 	/* get the service descriptor - or create a default one */
5802 	rc = __s_api_get_SSD_from_SSDtoUse_service(service,
5803 	    &sdlist, &error);
5804 	if (rc != NS_LDAP_SUCCESS) {
5805 		(void) __ns_ldap_freeError(&error);
5806 		goto out;
5807 	}
5808 
5809 	if (sdlist == NULL) {
5810 		/* Create default service Desc */
5811 		sdlist = (ns_ldap_search_desc_t **)calloc(2,
5812 		    sizeof (ns_ldap_search_desc_t *));
5813 		if (sdlist == NULL) {
5814 			rc = NS_LDAP_MEMORY;
5815 			goto out;
5816 		}
5817 		dptr = (ns_ldap_search_desc_t *)
5818 		    calloc(1, sizeof (ns_ldap_search_desc_t));
5819 		if (dptr == NULL) {
5820 			free(sdlist);
5821 			rc = NS_LDAP_MEMORY;
5822 			goto out;
5823 		}
5824 		sdlist[0] = dptr;
5825 
5826 		/* default base */
5827 		rc = __s_api_getDNs(&dns, service, &cookie->errorp);
5828 		if (rc != NS_LDAP_SUCCESS) {
5829 			if (dns) {
5830 				__s_api_free2dArray(dns);
5831 				dns = NULL;
5832 			}
5833 			(void) __ns_ldap_freeError(&(cookie->errorp));
5834 			cookie->errorp = NULL;
5835 			goto out;
5836 		}
5837 		dptr->basedn = strdup(dns[0]);
5838 		if (dptr->basedn == NULL) {
5839 			free(sdlist);
5840 			free(dptr);
5841 			if (dns) {
5842 				__s_api_free2dArray(dns);
5843 				dns = NULL;
5844 			}
5845 			rc = NS_LDAP_MEMORY;
5846 			goto out;
5847 		}
5848 		__s_api_free2dArray(dns);
5849 		dns = NULL;
5850 
5851 		/* default scope */
5852 		scope = 0;
5853 		rc = __s_api_getSearchScope(&scope, &cookie->errorp);
5854 		dptr->scope = scope;
5855 	}
5856 
5857 	cookie->sdlist = sdlist;
5858 
5859 	cookie->service = strdup(service);
5860 	if (cookie->service == NULL) {
5861 		rc = NS_LDAP_MEMORY;
5862 		goto out;
5863 	}
5864 
5865 	/* search for entries for this particular uid */
5866 	(void) asprintf(&cookie->i_filter, "(uid=%s)", user);
5867 	if (cookie->i_filter == NULL) {
5868 		rc = NS_LDAP_MEMORY;
5869 		goto out;
5870 	}
5871 
5872 	/* create the control request */
5873 	if ((rc = setup_acctmgmt_params(cookie)) != NS_LDAP_SUCCESS)
5874 		goto out;
5875 
5876 	/* Process search */
5877 	rc = search_state_machine(cookie, GET_ACCT_MGMT_INFO, 0);
5878 
5879 	/* Copy results back to user */
5880 	rc = cookie->err_rc;
5881 	if (rc != NS_LDAP_SUCCESS)
5882 			(void) __ns_ldap_freeError(&(cookie->errorp));
5883 
5884 	if (cookie->result == NULL)
5885 			goto out;
5886 
5887 	if ((rc = parse_acct_cont_resp_msg(cookie->resultctrl, acctResp))
5888 	    != NS_LDAP_SUCCESS)
5889 		goto out;
5890 
5891 	rc = NS_LDAP_SUCCESS;
5892 
5893 out:
5894 	delete_search_cookie(cookie);
5895 
5896 	return (rc);
5897 }
5898 
5899 /*
5900  * __ns_ldap_getAcctMgmt() is called from pam account management stack
5901  * for retrieving accounting information of users with no user password -
5902  * eg. rlogin, rsh, etc. This function uses the account management control
5903  * request to do a search on the server for the user in question. The
5904  * response control returned from the server is got from the cookie.
5905  * Input params: username of whose account mgmt information is to be got
5906  *		 pointer to hold the parsed account management information
5907  * Return values: NS_LDAP_SUCCESS on success or appropriate error
5908  *		code on failure
5909  */
5910 int
5911 __ns_ldap_getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp)
5912 {
5913 	ns_conn_user_t	*cu = NULL;
5914 	int		try_cnt = 0;
5915 	int		rc = NS_LDAP_SUCCESS;
5916 	ns_ldap_error_t	*error = NULL;
5917 
5918 	for (;;) {
5919 		if (__s_api_setup_retry_search(&cu, NS_CONN_USER_SEARCH,
5920 		    &try_cnt, &rc, &error) == 0)
5921 			break;
5922 		rc = getAcctMgmt(user, acctResp, cu);
5923 	}
5924 	return (rc);
5925 }
5926