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