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