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