xref: /titanic_44/usr/src/lib/libsldap/common/ns_reads.c (revision 4a634bb80136cc001d14ab96addd9915105e5223)
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  * clear_results(ns_ldap_cookie_t):
2042  *
2043  * Attempt to obtain remnants of ldap responses and free them.  If remnants are
2044  * not obtained within a certain time period tell the server we wish to abandon
2045  * the request.
2046  *
2047  * Note that we do not initially tell the server to abandon the request as that
2048  * can be an expensive operation for the server, while it is cheap for us to
2049  * just flush the input.
2050  *
2051  * If something was to remain in libldap queue as a result of some error then
2052  * it would be freed later during drop connection call or when no other
2053  * requests share the connection.
2054  */
2055 static void
2056 clear_results(ns_ldap_cookie_t *cookie)
2057 {
2058 	int rc;
2059 	if (cookie->conn != NULL && cookie->conn->ld != NULL &&
2060 	    cookie->connectionId != -1 && cookie->msgId != 0) {
2061 		/*
2062 		 * We need to cleanup the rest of response (if there is such)
2063 		 * and LDAP abandon is too heavy for LDAP servers, so we will
2064 		 * wait for the rest of response till timeout and "process" it.
2065 		 */
2066 		rc = ldap_result(cookie->conn->ld, cookie->msgId, LDAP_MSG_ALL,
2067 		    (struct timeval *)&cookie->search_timeout,
2068 		    &cookie->resultMsg);
2069 		if (rc != -1 && rc != 0 && cookie->resultMsg != NULL)
2070 			(void) ldap_msgfree(cookie->resultMsg);
2071 		/*
2072 		 * If there was timeout then we will send  ABANDON request to
2073 		 * LDAP server to decrease load.
2074 		 */
2075 		if (rc == 0)
2076 			(void) ldap_abandon_ext(cookie->conn->ld, cookie->msgId,
2077 			    NULL, NULL);
2078 		/* Disassociate cookie with msgId */
2079 		cookie->msgId = 0;
2080 	}
2081 }
2082 
2083 /*
2084  * This state machine performs one or more LDAP searches to a given
2085  * directory server using service search descriptors and schema
2086  * mapping as appropriate.  The approximate pseudocode for
2087  * this routine is the following:
2088  *    Given the current configuration [set/reset connection etc.]
2089  *    and the current service search descriptor list
2090  *        or default search filter parameters
2091  *    foreach (service search filter) {
2092  *        initialize the filter [via filter_init if appropriate]
2093  *		  get a valid session/connection (preferably the current one)
2094  *					Recover if the connection is lost
2095  *        perform the search
2096  *        foreach (result entry) {
2097  *            process result [via callback if appropriate]
2098  *                save result for caller if accepted.
2099  *                exit and return all collected if allResults found;
2100  *        }
2101  *    }
2102  *    return collected results and exit
2103  */
2104 
2105 static
2106 ns_state_t
2107 search_state_machine(ns_ldap_cookie_t *cookie, ns_state_t state, int cycle)
2108 {
2109 	char		errstr[MAXERROR];
2110 	char		*err;
2111 	int		rc, ret;
2112 	ns_ldap_entry_t	*nextEntry;
2113 	ns_ldap_error_t *error = NULL;
2114 	ns_ldap_error_t **errorp;
2115 	struct timeval	tv;
2116 
2117 	errorp = &error;
2118 	cookie->state = state;
2119 	errstr[0] = '\0';
2120 
2121 	for (;;) {
2122 		switch (cookie->state) {
2123 		case CLEAR_RESULTS:
2124 			clear_results(cookie);
2125 			cookie->new_state = EXIT;
2126 			break;
2127 		case GET_ACCT_MGMT_INFO:
2128 			/*
2129 			 * Set the flag to get ldap account management controls.
2130 			 */
2131 			cookie->nopasswd_acct_mgmt = 1;
2132 			cookie->new_state = INIT;
2133 			break;
2134 		case EXIT:
2135 			/* state engine/connection cleaned up in delete */
2136 			if (cookie->attribute) {
2137 				__s_api_free2dArray(cookie->attribute);
2138 				cookie->attribute = NULL;
2139 			}
2140 			if (cookie->reflist) {
2141 				__s_api_deleteRefInfo(cookie->reflist);
2142 				cookie->reflist = NULL;
2143 			}
2144 			return (EXIT);
2145 		case INIT:
2146 			cookie->sdpos = NULL;
2147 			cookie->new_state = NEXT_SEARCH_DESCRIPTOR;
2148 			if (cookie->attribute) {
2149 				__s_api_free2dArray(cookie->attribute);
2150 				cookie->attribute = NULL;
2151 			}
2152 			if ((cookie->i_flags & NS_LDAP_NOMAP) == 0 &&
2153 			    cookie->i_attr) {
2154 				cookie->attribute =
2155 				    __ns_ldap_mapAttributeList(
2156 				    cookie->service,
2157 				    cookie->i_attr);
2158 			}
2159 			break;
2160 		case NEXT_SEARCH_DESCRIPTOR:
2161 			/* get next search descriptor */
2162 			if (cookie->sdpos == NULL) {
2163 				cookie->sdpos = cookie->sdlist;
2164 				cookie->new_state = GET_SESSION;
2165 			} else {
2166 				cookie->sdpos++;
2167 				cookie->new_state = NEXT_SEARCH;
2168 			}
2169 			if (*cookie->sdpos == NULL)
2170 				cookie->new_state = EXIT;
2171 			break;
2172 		case GET_SESSION:
2173 			if (get_current_session(cookie) < 0)
2174 				cookie->new_state = NEXT_SESSION;
2175 			else
2176 				cookie->new_state = NEXT_SEARCH;
2177 			break;
2178 		case NEXT_SESSION:
2179 			if (get_next_session(cookie) < 0)
2180 				cookie->new_state = RESTART_SESSION;
2181 			else
2182 				cookie->new_state = NEXT_SEARCH;
2183 			break;
2184 		case RESTART_SESSION:
2185 			if (cookie->i_flags & NS_LDAP_HARD) {
2186 				cookie->new_state = NEXT_SESSION;
2187 				break;
2188 			}
2189 			(void) sprintf(errstr,
2190 			    gettext("Session error no available conn.\n"),
2191 			    state);
2192 			err = strdup(errstr);
2193 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2194 			    NULL);
2195 			cookie->err_rc = NS_LDAP_INTERNAL;
2196 			cookie->errorp = *errorp;
2197 			cookie->new_state = EXIT;
2198 			break;
2199 		case NEXT_SEARCH:
2200 			/* setup referrals search if necessary */
2201 			if (cookie->refpos) {
2202 				if (setup_referral_search(cookie) < 0) {
2203 					cookie->new_state = EXIT;
2204 					break;
2205 				}
2206 			} else if (setup_next_search(cookie) < 0) {
2207 				cookie->new_state = EXIT;
2208 				break;
2209 			}
2210 			/* only do VLV/PAGE on scopes onelevel/subtree */
2211 			if (paging_supported(cookie)) {
2212 				if (cookie->use_paging &&
2213 				    (cookie->scope != LDAP_SCOPE_BASE)) {
2214 					cookie->index = 1;
2215 					if (cookie->listType == VLVCTRLFLAG)
2216 						cookie->new_state = NEXT_VLV;
2217 					else
2218 						cookie->new_state = NEXT_PAGE;
2219 					break;
2220 				}
2221 			}
2222 			cookie->new_state = ONE_SEARCH;
2223 			break;
2224 		case NEXT_VLV:
2225 			rc = setup_vlv_params(cookie);
2226 			if (rc != LDAP_SUCCESS) {
2227 				cookie->err_rc = rc;
2228 				cookie->new_state = LDAP_ERROR;
2229 				break;
2230 			}
2231 			cookie->next_state = MULTI_RESULT;
2232 			cookie->new_state = DO_SEARCH;
2233 			break;
2234 		case NEXT_PAGE:
2235 			rc = setup_simplepg_params(cookie);
2236 			if (rc != LDAP_SUCCESS) {
2237 				cookie->err_rc = rc;
2238 				cookie->new_state = LDAP_ERROR;
2239 				break;
2240 			}
2241 			cookie->next_state = MULTI_RESULT;
2242 			cookie->new_state = DO_SEARCH;
2243 			break;
2244 		case ONE_SEARCH:
2245 			cookie->next_state = NEXT_RESULT;
2246 			cookie->new_state = DO_SEARCH;
2247 			break;
2248 		case DO_SEARCH:
2249 			rc = ldap_search_ext(cookie->conn->ld,
2250 			    cookie->basedn,
2251 			    cookie->scope,
2252 			    cookie->filter,
2253 			    cookie->attribute,
2254 			    0,
2255 			    cookie->p_serverctrls,
2256 			    NULL,
2257 			    &cookie->search_timeout, 0,
2258 			    &cookie->msgId);
2259 			if (rc != LDAP_SUCCESS) {
2260 				if (rc == LDAP_BUSY ||
2261 				    rc == LDAP_UNAVAILABLE ||
2262 				    rc == LDAP_UNWILLING_TO_PERFORM ||
2263 				    rc == LDAP_CONNECT_ERROR ||
2264 				    rc == LDAP_SERVER_DOWN) {
2265 
2266 					cookie->new_state = NEXT_SESSION;
2267 
2268 					/*
2269 					 * If not able to reach the
2270 					 * server, inform the ldap
2271 					 * cache manager that the
2272 					 * server should be removed
2273 					 * from it's server list.
2274 					 * Thus, the manager will not
2275 					 * return this server on the next
2276 					 * get-server request and will
2277 					 * also reduce the server list
2278 					 * refresh TTL, so that it will
2279 					 * find out sooner when the server
2280 					 * is up again.
2281 					 */
2282 					if (rc == LDAP_CONNECT_ERROR ||
2283 					    rc == LDAP_SERVER_DOWN) {
2284 						ret = __s_api_removeServer(
2285 						    cookie->conn->serverAddr);
2286 						if (ret == NOSERVER &&
2287 						    cookie->conn_auth_type
2288 						    == NS_LDAP_AUTH_NONE) {
2289 							/*
2290 							 * Couldn't remove
2291 							 * server from server
2292 							 * list.
2293 							 * Exit to avoid
2294 							 * potential infinite
2295 							 * loop.
2296 							 */
2297 							cookie->err_rc = rc;
2298 							cookie->new_state =
2299 							    LDAP_ERROR;
2300 						}
2301 						if (cookie->connectionId > -1) {
2302 							/*
2303 							 * NS_LDAP_NEW_CONN
2304 							 * indicates that the
2305 							 * connection should
2306 							 * be deleted, not
2307 							 * kept alive
2308 							 */
2309 							DropConnection(
2310 							    cookie->
2311 							    connectionId,
2312 							    NS_LDAP_NEW_CONN);
2313 							cookie->connectionId =
2314 							    -1;
2315 						}
2316 					}
2317 					break;
2318 				}
2319 				cookie->err_rc = rc;
2320 				cookie->new_state = LDAP_ERROR;
2321 				break;
2322 			}
2323 			cookie->new_state = cookie->next_state;
2324 			break;
2325 		case NEXT_RESULT:
2326 			/*
2327 			 * Caller (e.g. __ns_ldap_list_batch_add)
2328 			 * does not want to block on ldap_result().
2329 			 * Therefore we execute ldap_result() with
2330 			 * a zeroed timeval.
2331 			 */
2332 			if (cookie->no_wait == B_TRUE)
2333 				(void) memset(&tv, 0, sizeof (tv));
2334 			else
2335 				tv = cookie->search_timeout;
2336 			rc = ldap_result(cookie->conn->ld, cookie->msgId,
2337 			    LDAP_MSG_ONE,
2338 			    &tv,
2339 			    &cookie->resultMsg);
2340 			if (rc == LDAP_RES_SEARCH_RESULT) {
2341 				cookie->new_state = END_RESULT;
2342 				/* check and process referrals info */
2343 				if (cookie->followRef)
2344 					proc_result_referrals(
2345 					    cookie);
2346 				(void) ldap_msgfree(cookie->resultMsg);
2347 				cookie->resultMsg = NULL;
2348 				break;
2349 			}
2350 			/* handle referrals if necessary */
2351 			if (rc == LDAP_RES_SEARCH_REFERENCE) {
2352 				if (cookie->followRef)
2353 					proc_search_references(cookie);
2354 				(void) ldap_msgfree(cookie->resultMsg);
2355 				cookie->resultMsg = NULL;
2356 				break;
2357 			}
2358 			if (rc != LDAP_RES_SEARCH_ENTRY) {
2359 				switch (rc) {
2360 				case 0:
2361 					if (cookie->no_wait == B_TRUE) {
2362 						(void) ldap_msgfree(
2363 						    cookie->resultMsg);
2364 						cookie->resultMsg = NULL;
2365 						return (cookie->new_state);
2366 					}
2367 					rc = LDAP_TIMEOUT;
2368 					break;
2369 				case -1:
2370 					rc = ldap_get_lderrno(cookie->conn->ld,
2371 					    NULL, NULL);
2372 					break;
2373 				default:
2374 					rc = ldap_result2error(cookie->conn->ld,
2375 					    cookie->resultMsg, 1);
2376 					break;
2377 				}
2378 				if (rc == LDAP_TIMEOUT ||
2379 				    rc == LDAP_SERVER_DOWN) {
2380 					if (rc == LDAP_TIMEOUT)
2381 						(void) __s_api_removeServer(
2382 						    cookie->conn->serverAddr);
2383 					if (cookie->connectionId > -1) {
2384 						DropConnection(
2385 						    cookie->connectionId,
2386 						    NS_LDAP_NEW_CONN);
2387 						cookie->connectionId = -1;
2388 					}
2389 					cookie->err_from_result = 1;
2390 				}
2391 				(void) ldap_msgfree(cookie->resultMsg);
2392 				cookie->resultMsg = NULL;
2393 				if (rc == LDAP_BUSY ||
2394 				    rc == LDAP_UNAVAILABLE ||
2395 				    rc == LDAP_UNWILLING_TO_PERFORM) {
2396 					cookie->new_state = NEXT_SESSION;
2397 					break;
2398 				}
2399 				cookie->err_rc = rc;
2400 				cookie->new_state = LDAP_ERROR;
2401 				break;
2402 			}
2403 			/* else LDAP_RES_SEARCH_ENTRY */
2404 			/* get account management response control */
2405 			if (cookie->nopasswd_acct_mgmt == 1) {
2406 				rc = ldap_get_entry_controls(cookie->conn->ld,
2407 				    cookie->resultMsg,
2408 				    &(cookie->resultctrl));
2409 				if (rc != LDAP_SUCCESS) {
2410 					cookie->new_state = LDAP_ERROR;
2411 					cookie->err_rc = rc;
2412 					break;
2413 				}
2414 			}
2415 			rc = __s_api_getEntry(cookie);
2416 			(void) ldap_msgfree(cookie->resultMsg);
2417 			cookie->resultMsg = NULL;
2418 			if (rc != NS_LDAP_SUCCESS) {
2419 				cookie->new_state = LDAP_ERROR;
2420 				break;
2421 			}
2422 			cookie->new_state = PROCESS_RESULT;
2423 			cookie->next_state = NEXT_RESULT;
2424 			break;
2425 		case MULTI_RESULT:
2426 			if (cookie->no_wait == B_TRUE)
2427 				(void) memset(&tv, 0, sizeof (tv));
2428 			else
2429 				tv = cookie->search_timeout;
2430 			rc = ldap_result(cookie->conn->ld, cookie->msgId,
2431 			    LDAP_MSG_ONE,
2432 			    &tv,
2433 			    &cookie->resultMsg);
2434 			if (rc == LDAP_RES_SEARCH_RESULT) {
2435 				rc = ldap_result2error(cookie->conn->ld,
2436 				    cookie->resultMsg, 0);
2437 				if (rc != LDAP_SUCCESS) {
2438 					cookie->err_rc = rc;
2439 					cookie->new_state = LDAP_ERROR;
2440 					(void) ldap_msgfree(cookie->resultMsg);
2441 					break;
2442 				}
2443 				cookie->new_state = multi_result(cookie);
2444 				(void) ldap_msgfree(cookie->resultMsg);
2445 				cookie->resultMsg = NULL;
2446 				break;
2447 			}
2448 			/* handle referrals if necessary */
2449 			if (rc == LDAP_RES_SEARCH_REFERENCE &&
2450 			    cookie->followRef) {
2451 				proc_search_references(cookie);
2452 				(void) ldap_msgfree(cookie->resultMsg);
2453 				cookie->resultMsg = NULL;
2454 				break;
2455 			}
2456 			if (rc != LDAP_RES_SEARCH_ENTRY) {
2457 				switch (rc) {
2458 				case 0:
2459 					if (cookie->no_wait == B_TRUE) {
2460 						(void) ldap_msgfree(
2461 						    cookie->resultMsg);
2462 						cookie->resultMsg = NULL;
2463 						return (cookie->new_state);
2464 					}
2465 					rc = LDAP_TIMEOUT;
2466 					break;
2467 				case -1:
2468 					rc = ldap_get_lderrno(cookie->conn->ld,
2469 					    NULL, NULL);
2470 					break;
2471 				default:
2472 					rc = ldap_result2error(cookie->conn->ld,
2473 					    cookie->resultMsg, 1);
2474 					break;
2475 				}
2476 				if (rc == LDAP_TIMEOUT ||
2477 				    rc == LDAP_SERVER_DOWN) {
2478 					if (rc == LDAP_TIMEOUT)
2479 						(void) __s_api_removeServer(
2480 						    cookie->conn->serverAddr);
2481 					if (cookie->connectionId > -1) {
2482 						DropConnection(
2483 						    cookie->connectionId,
2484 						    NS_LDAP_NEW_CONN);
2485 						cookie->connectionId = -1;
2486 					}
2487 					cookie->err_from_result = 1;
2488 				}
2489 				(void) ldap_msgfree(cookie->resultMsg);
2490 				cookie->resultMsg = NULL;
2491 				if (rc == LDAP_BUSY ||
2492 				    rc == LDAP_UNAVAILABLE ||
2493 				    rc == LDAP_UNWILLING_TO_PERFORM) {
2494 					cookie->new_state = NEXT_SESSION;
2495 					break;
2496 				}
2497 				cookie->err_rc = rc;
2498 				cookie->new_state = LDAP_ERROR;
2499 				break;
2500 			}
2501 			/* else LDAP_RES_SEARCH_ENTRY */
2502 			rc = __s_api_getEntry(cookie);
2503 			(void) ldap_msgfree(cookie->resultMsg);
2504 			cookie->resultMsg = NULL;
2505 			if (rc != NS_LDAP_SUCCESS) {
2506 				cookie->new_state = LDAP_ERROR;
2507 				break;
2508 			}
2509 			cookie->new_state = PROCESS_RESULT;
2510 			cookie->next_state = MULTI_RESULT;
2511 			break;
2512 		case PROCESS_RESULT:
2513 			/* NOTE THIS STATE MAY BE PROCESSED BY CALLER */
2514 			if (cookie->use_usercb && cookie->callback) {
2515 				rc = 0;
2516 				for (nextEntry = cookie->result->entry;
2517 				    nextEntry != NULL;
2518 				    nextEntry = nextEntry->next) {
2519 					rc = (*cookie->callback)(nextEntry,
2520 					    cookie->userdata);
2521 
2522 					if (rc == NS_LDAP_CB_DONE) {
2523 					/* cb doesn't want any more data */
2524 						rc = NS_LDAP_PARTIAL;
2525 						cookie->err_rc = rc;
2526 						break;
2527 					} else if (rc != NS_LDAP_CB_NEXT) {
2528 					/* invalid return code */
2529 						rc = NS_LDAP_OP_FAILED;
2530 						cookie->err_rc = rc;
2531 						break;
2532 					}
2533 				}
2534 				(void) __ns_ldap_freeResult(&cookie->result);
2535 				cookie->result = NULL;
2536 			}
2537 			if (rc != 0) {
2538 				cookie->new_state = EXIT;
2539 				break;
2540 			}
2541 			/* NOTE PREVIOUS STATE SPECIFIES NEXT STATE */
2542 			cookie->new_state = cookie->next_state;
2543 			break;
2544 		case END_PROCESS_RESULT:
2545 			cookie->new_state = cookie->next_state;
2546 			break;
2547 		case END_RESULT:
2548 			/*
2549 			 * XXX DO WE NEED THIS CASE?
2550 			 * if (search is complete) {
2551 			 * 	cookie->new_state = EXIT;
2552 			 * } else
2553 			 */
2554 				/*
2555 				 * entering referral mode if necessary
2556 				 */
2557 				if (cookie->followRef && cookie->reflist)
2558 					cookie->new_state =
2559 					    NEXT_REFERRAL;
2560 				else
2561 					cookie->new_state =
2562 					    NEXT_SEARCH_DESCRIPTOR;
2563 			break;
2564 		case NEXT_REFERRAL:
2565 			/* get next referral info */
2566 			if (cookie->refpos == NULL)
2567 				cookie->refpos =
2568 				    cookie->reflist;
2569 			else
2570 				cookie->refpos =
2571 				    cookie->refpos->next;
2572 			/* check see if done with all referrals */
2573 			if (cookie->refpos != NULL)
2574 				cookie->new_state =
2575 				    GET_REFERRAL_SESSION;
2576 			else {
2577 				__s_api_deleteRefInfo(cookie->reflist);
2578 				cookie->reflist = NULL;
2579 				cookie->new_state =
2580 				    NEXT_SEARCH_DESCRIPTOR;
2581 			}
2582 			break;
2583 		case GET_REFERRAL_SESSION:
2584 			if (get_referral_session(cookie) < 0)
2585 				cookie->new_state = EXIT;
2586 			else {
2587 				cookie->new_state = NEXT_SEARCH;
2588 			}
2589 			break;
2590 		case LDAP_ERROR:
2591 			if (cookie->err_from_result) {
2592 				if (cookie->err_rc == LDAP_SERVER_DOWN) {
2593 					(void) sprintf(errstr,
2594 					    gettext("LDAP ERROR (%d): "
2595 					    "Error occurred during"
2596 					    " receiving results. "
2597 					    "Connection to server lost."),
2598 					    cookie->err_rc);
2599 				} else if (cookie->err_rc == LDAP_TIMEOUT) {
2600 					(void) sprintf(errstr,
2601 					    gettext("LDAP ERROR (%d): "
2602 					    "Error occurred during"
2603 					    " receiving results. %s"
2604 					    "."), cookie->err_rc,
2605 					    ldap_err2string(
2606 					    cookie->err_rc));
2607 				}
2608 			} else
2609 				(void) sprintf(errstr,
2610 				    gettext("LDAP ERROR (%d): %s."),
2611 				    cookie->err_rc,
2612 				    ldap_err2string(cookie->err_rc));
2613 			err = strdup(errstr);
2614 			if (cookie->err_from_result) {
2615 				if (cookie->err_rc == LDAP_SERVER_DOWN) {
2616 					MKERROR(LOG_INFO, *errorp,
2617 					    cookie->err_rc, err, NULL);
2618 				} else {
2619 					MKERROR(LOG_WARNING, *errorp,
2620 					    cookie->err_rc, err, NULL);
2621 				}
2622 			} else {
2623 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2624 				    err, NULL);
2625 			}
2626 			cookie->err_rc = NS_LDAP_INTERNAL;
2627 			cookie->errorp = *errorp;
2628 			return (ERROR);
2629 		default:
2630 		case ERROR:
2631 			(void) sprintf(errstr,
2632 			    gettext("Internal State machine exit (%d).\n"),
2633 			    cookie->state);
2634 			err = strdup(errstr);
2635 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err,
2636 			    NULL);
2637 			cookie->err_rc = NS_LDAP_INTERNAL;
2638 			cookie->errorp = *errorp;
2639 			return (ERROR);
2640 		}
2641 
2642 		if (cycle == ONE_STEP) {
2643 			return (cookie->new_state);
2644 		}
2645 		cookie->state = cookie->new_state;
2646 	}
2647 	/*NOTREACHED*/
2648 #if 0
2649 	(void) sprintf(errstr,
2650 	    gettext("Unexpected State machine error.\n"));
2651 	err = strdup(errstr);
2652 	MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, err, NULL);
2653 	cookie->err_rc = NS_LDAP_INTERNAL;
2654 	cookie->errorp = *errorp;
2655 	return (ERROR);
2656 #endif
2657 }
2658 
2659 
2660 /*
2661  * internal function for __ns_ldap_list
2662  */
2663 static int
2664 ldap_list(
2665 	ns_ldap_list_batch_t *batch,
2666 	const char *service,
2667 	const char *filter,
2668 	int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
2669 	char **realfilter, const void *userdata),
2670 	const char * const *attribute,
2671 	const ns_cred_t *auth,
2672 	const int flags,
2673 	ns_ldap_result_t **rResult, /* return result entries */
2674 	ns_ldap_error_t **errorp,
2675 	int *rcp,
2676 	int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
2677 	const void *userdata)
2678 {
2679 	ns_ldap_cookie_t	*cookie;
2680 	ns_ldap_search_desc_t	**sdlist = NULL;
2681 	ns_ldap_search_desc_t	*dptr;
2682 	ns_ldap_error_t		*error = NULL;
2683 	char			**dns = NULL;
2684 	int			scope;
2685 	int			rc;
2686 	int			from_result;
2687 
2688 	*errorp = NULL;
2689 	*rResult = NULL;
2690 	*rcp = NS_LDAP_SUCCESS;
2691 
2692 	/* Initialize State machine cookie */
2693 	cookie = init_search_state_machine();
2694 	if (cookie == NULL) {
2695 		*rcp = NS_LDAP_MEMORY;
2696 		return (NS_LDAP_MEMORY);
2697 	}
2698 
2699 	/* see if need to follow referrals */
2700 	rc = __s_api_toFollowReferrals(flags,
2701 	    &cookie->followRef, errorp);
2702 	if (rc != NS_LDAP_SUCCESS) {
2703 		delete_search_cookie(cookie);
2704 		*rcp = rc;
2705 		return (rc);
2706 	}
2707 
2708 	/* get the service descriptor - or create a default one */
2709 	rc = __s_api_get_SSD_from_SSDtoUse_service(service,
2710 	    &sdlist, errorp);
2711 	if (rc != NS_LDAP_SUCCESS) {
2712 		delete_search_cookie(cookie);
2713 		*errorp = error;
2714 		*rcp = rc;
2715 		return (rc);
2716 	}
2717 
2718 	if (sdlist == NULL) {
2719 		/* Create default service Desc */
2720 		sdlist = (ns_ldap_search_desc_t **)calloc(2,
2721 		    sizeof (ns_ldap_search_desc_t *));
2722 		if (sdlist == NULL) {
2723 			delete_search_cookie(cookie);
2724 			cookie = NULL;
2725 			*rcp = NS_LDAP_MEMORY;
2726 			return (NS_LDAP_MEMORY);
2727 		}
2728 		dptr = (ns_ldap_search_desc_t *)
2729 		    calloc(1, sizeof (ns_ldap_search_desc_t));
2730 		if (dptr == NULL) {
2731 			free(sdlist);
2732 			delete_search_cookie(cookie);
2733 			cookie = NULL;
2734 			*rcp = NS_LDAP_MEMORY;
2735 			return (NS_LDAP_MEMORY);
2736 		}
2737 		sdlist[0] = dptr;
2738 
2739 		/* default base */
2740 		rc = __s_api_getDNs(&dns, service, &cookie->errorp);
2741 		if (rc != NS_LDAP_SUCCESS) {
2742 			if (dns) {
2743 				__s_api_free2dArray(dns);
2744 				dns = NULL;
2745 			}
2746 			*errorp = cookie->errorp;
2747 			cookie->errorp = NULL;
2748 			delete_search_cookie(cookie);
2749 			cookie = NULL;
2750 			*rcp = rc;
2751 			return (rc);
2752 		}
2753 		dptr->basedn = strdup(dns[0]);
2754 		__s_api_free2dArray(dns);
2755 		dns = NULL;
2756 
2757 		/* default scope */
2758 		scope = 0;
2759 		rc = __s_api_getSearchScope(&scope, &cookie->errorp);
2760 		dptr->scope = scope;
2761 	}
2762 
2763 	cookie->sdlist = sdlist;
2764 
2765 	/*
2766 	 * use VLV/PAGE control only if NS_LDAP_PAGE_CTRL is set
2767 	 */
2768 	if (flags & NS_LDAP_PAGE_CTRL)
2769 		cookie->use_paging = TRUE;
2770 	else
2771 		cookie->use_paging = FALSE;
2772 
2773 	/* Set up other arguments */
2774 	cookie->userdata = userdata;
2775 	if (init_filter_cb != NULL) {
2776 		cookie->init_filter_cb = init_filter_cb;
2777 		cookie->use_filtercb = 1;
2778 	}
2779 	if (callback != NULL) {
2780 		cookie->callback = callback;
2781 		cookie->use_usercb = 1;
2782 	}
2783 	if (service) {
2784 		cookie->service = strdup(service);
2785 		if (cookie->service == NULL) {
2786 			delete_search_cookie(cookie);
2787 			cookie = NULL;
2788 			*rcp = NS_LDAP_MEMORY;
2789 			return (NS_LDAP_MEMORY);
2790 		}
2791 	}
2792 
2793 	cookie->i_filter = strdup(filter);
2794 	cookie->i_attr = attribute;
2795 	cookie->i_auth = auth;
2796 	cookie->i_flags = flags;
2797 
2798 	if (batch != NULL) {
2799 		cookie->batch = batch;
2800 		cookie->no_wait = B_TRUE;
2801 		(void) search_state_machine(cookie, INIT, 0);
2802 		cookie->no_wait = B_FALSE;
2803 		rc = cookie->err_rc;
2804 
2805 		if (rc == NS_LDAP_SUCCESS) {
2806 			/*
2807 			 * Here rc == NS_LDAP_SUCCESS means that the state
2808 			 * machine init'ed successfully. The actual status
2809 			 * of the search will be determined by
2810 			 * __ns_ldap_list_batch_end(). Add the cookie to our
2811 			 * batch.
2812 			 */
2813 			cookie->caller_result = rResult;
2814 			cookie->caller_errorp = errorp;
2815 			cookie->caller_rc = rcp;
2816 			cookie->next_cookie_in_batch = batch->cookie_list;
2817 			batch->cookie_list = cookie;
2818 			batch->nactive++;
2819 			return (rc);
2820 		}
2821 		/*
2822 		 * If state machine init failed then copy error to the caller
2823 		 * and delete the cookie.
2824 		 */
2825 	} else {
2826 		(void) search_state_machine(cookie, INIT, 0);
2827 	}
2828 
2829 	/* Copy results back to user */
2830 	rc = cookie->err_rc;
2831 	if (rc != NS_LDAP_SUCCESS)
2832 		*errorp = cookie->errorp;
2833 	*rResult = cookie->result;
2834 	from_result = cookie->err_from_result;
2835 
2836 	cookie->errorp = NULL;
2837 	cookie->result = NULL;
2838 	delete_search_cookie(cookie);
2839 	cookie = NULL;
2840 
2841 	if (from_result == 0 && *rResult == NULL)
2842 		rc = NS_LDAP_NOTFOUND;
2843 	*rcp = rc;
2844 	return (rc);
2845 }
2846 
2847 
2848 /*
2849  * __ns_ldap_list performs one or more LDAP searches to a given
2850  * directory server using service search descriptors and schema
2851  * mapping as appropriate.
2852  */
2853 
2854 int
2855 __ns_ldap_list(
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 (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
2866 	const void *userdata)
2867 {
2868 	int	rc;
2869 	return (ldap_list(NULL, service, filter,
2870 	    init_filter_cb, attribute, auth, flags, rResult, errorp, &rc,
2871 	    callback, userdata));
2872 }
2873 
2874 
2875 /*
2876  * Create and initialize batch for native LDAP lookups
2877  */
2878 int
2879 __ns_ldap_list_batch_start(ns_ldap_list_batch_t **batch)
2880 {
2881 	*batch = calloc(1, sizeof (ns_ldap_list_batch_t));
2882 	if (*batch == NULL)
2883 		return (NS_LDAP_MEMORY);
2884 	return (NS_LDAP_SUCCESS);
2885 }
2886 
2887 
2888 /*
2889  * Add a LDAP search request to the batch.
2890  */
2891 int
2892 __ns_ldap_list_batch_add(
2893 	ns_ldap_list_batch_t *batch,
2894 	const char *service,
2895 	const char *filter,
2896 	int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
2897 	char **realfilter, const void *userdata),
2898 	const char * const *attribute,
2899 	const ns_cred_t *auth,
2900 	const int flags,
2901 	ns_ldap_result_t **rResult, /* return result entries */
2902 	ns_ldap_error_t **errorp,
2903 	int *rcp,
2904 	int (*callback)(const ns_ldap_entry_t *entry, const void *userdata),
2905 	const void *userdata)
2906 {
2907 	return (ldap_list(batch, service, filter,
2908 	    init_filter_cb, attribute, auth, flags, rResult, errorp, rcp,
2909 	    callback, userdata));
2910 }
2911 
2912 
2913 /*
2914  * Free batch.
2915  */
2916 void
2917 __ns_ldap_list_batch_release(ns_ldap_list_batch_t *batch)
2918 {
2919 	ns_ldap_cookie_t	*c, *next;
2920 
2921 	for (c = batch->cookie_list; c != NULL; c = next) {
2922 		next = c->next_cookie_in_batch;
2923 		delete_search_cookie(c);
2924 	}
2925 	free(batch);
2926 }
2927 
2928 
2929 /*
2930  * Process batch. Everytime this function is called it selects an
2931  * active cookie from the batch and single steps through the
2932  * search_state_machine for the selected cookie. If lookup associated
2933  * with the cookie is complete (success or error) then the cookie is
2934  * removed from the batch and its memory freed.
2935  *
2936  * Returns 1 (if batch still has active cookies)
2937  *         0 (if batch has no more active cookies)
2938  *        -1 (on errors, *rcp will contain the error code)
2939  *
2940  * The caller should call this function in a loop as long as it returns 1
2941  * to process all the requests added to the batch. The results (and errors)
2942  * will be available in the locations provided by the caller at the time of
2943  * __ns_ldap_list_batch_add().
2944  */
2945 static
2946 int
2947 __ns_ldap_list_batch_process(ns_ldap_list_batch_t *batch, int *rcp)
2948 {
2949 	ns_ldap_cookie_t	*c, *ptr, **prev;
2950 	ns_state_t		state;
2951 	int			rc;
2952 
2953 	/* Check if are already done */
2954 	if (batch->nactive == 0)
2955 		return (0);
2956 
2957 	/* Get the next cookie from the batch */
2958 	c = (batch->next_cookie == NULL) ?
2959 	    batch->cookie_list : batch->next_cookie;
2960 
2961 	batch->next_cookie = c->next_cookie_in_batch;
2962 
2963 	if (c->conn->shared > 0) {
2964 		rc = __s_api_check_MTC_tsd();
2965 		if (rc != NS_LDAP_SUCCESS) {
2966 			if (rcp != NULL)
2967 				*rcp = rc;
2968 			return (-1);
2969 		}
2970 	}
2971 
2972 	for (;;) {
2973 		/* Single step through the search_state_machine */
2974 		state = search_state_machine(c, c->new_state, ONE_STEP);
2975 		switch (state) {
2976 		case LDAP_ERROR:
2977 			(void) search_state_machine(c, state, ONE_STEP);
2978 			(void) search_state_machine(c, CLEAR_RESULTS, ONE_STEP);
2979 			/* FALLTHROUGH */
2980 		case ERROR:
2981 		case EXIT:
2982 			*c->caller_result = c->result;
2983 			*c->caller_errorp = c->errorp;
2984 			*c->caller_rc =
2985 			    (c->result == NULL && c->err_from_result == 0)
2986 			    ? NS_LDAP_NOTFOUND : c->err_rc;
2987 			c->result = NULL;
2988 			c->errorp = NULL;
2989 			/* Remove the cookie from the batch */
2990 			ptr = batch->cookie_list;
2991 			prev = &batch->cookie_list;
2992 			while (ptr != NULL) {
2993 				if (ptr == c) {
2994 					*prev = ptr->next_cookie_in_batch;
2995 					break;
2996 				}
2997 				prev = &ptr->next_cookie_in_batch;
2998 				ptr = ptr->next_cookie_in_batch;
2999 			}
3000 			/* Delete cookie and decrement active cookie count */
3001 			delete_search_cookie(c);
3002 			batch->nactive--;
3003 			break;
3004 		case NEXT_RESULT:
3005 		case MULTI_RESULT:
3006 			/*
3007 			 * This means that search_state_machine needs to do
3008 			 * another ldap_result() for the cookie in question.
3009 			 * We only do at most one ldap_result() per call in
3010 			 * this function and therefore we return. This allows
3011 			 * the caller to process results from other cookies
3012 			 * in the batch without getting tied up on just one
3013 			 * cookie.
3014 			 */
3015 			break;
3016 		default:
3017 			/*
3018 			 * This includes states that follow NEXT_RESULT or
3019 			 * MULTI_RESULT such as PROCESS_RESULT and
3020 			 * END_PROCESS_RESULT. We continue processing
3021 			 * this cookie till we reach either the error, exit
3022 			 * or the result states.
3023 			 */
3024 			continue;
3025 		}
3026 		break;
3027 	}
3028 
3029 	/* Return 0 if no more cookies left otherwise 1 */
3030 	return ((batch->nactive > 0) ? 1 : 0);
3031 }
3032 
3033 
3034 /*
3035  * Process all the active cookies in the batch and when none
3036  * remains finalize the batch.
3037  */
3038 int
3039 __ns_ldap_list_batch_end(ns_ldap_list_batch_t *batch)
3040 {
3041 	int rc = NS_LDAP_SUCCESS;
3042 	while (__ns_ldap_list_batch_process(batch, &rc) > 0)
3043 		;
3044 	__ns_ldap_list_batch_release(batch);
3045 	return (rc);
3046 }
3047 
3048 
3049 /*
3050  * __s_api_find_domainname performs one or more LDAP searches to
3051  * find the value of the nisdomain attribute associated with
3052  * the input DN
3053  */
3054 
3055 static int
3056 __s_api_find_domainname(
3057 	const char *dn,
3058 	char **domainname,
3059 	const ns_cred_t *cred,
3060 	ns_ldap_error_t **errorp)
3061 {
3062 
3063 	ns_ldap_cookie_t	*cookie;
3064 	ns_ldap_search_desc_t	**sdlist;
3065 	ns_ldap_search_desc_t	*dptr;
3066 	int			rc;
3067 	char			**value;
3068 	int			flags = 0;
3069 
3070 	*domainname = NULL;
3071 	*errorp = NULL;
3072 
3073 	/* Initialize State machine cookie */
3074 	cookie = init_search_state_machine();
3075 	if (cookie == NULL) {
3076 		return (NS_LDAP_MEMORY);
3077 	}
3078 
3079 	/* see if need to follow referrals */
3080 	rc = __s_api_toFollowReferrals(flags,
3081 	    &cookie->followRef, errorp);
3082 	if (rc != NS_LDAP_SUCCESS) {
3083 		delete_search_cookie(cookie);
3084 		return (rc);
3085 	}
3086 
3087 	/* Create default service Desc */
3088 	sdlist = (ns_ldap_search_desc_t **)calloc(2,
3089 	    sizeof (ns_ldap_search_desc_t *));
3090 	if (sdlist == NULL) {
3091 		delete_search_cookie(cookie);
3092 		cookie = NULL;
3093 		return (NS_LDAP_MEMORY);
3094 	}
3095 	dptr = (ns_ldap_search_desc_t *)
3096 	    calloc(1, sizeof (ns_ldap_search_desc_t));
3097 	if (dptr == NULL) {
3098 		free(sdlist);
3099 		delete_search_cookie(cookie);
3100 		cookie = NULL;
3101 		return (NS_LDAP_MEMORY);
3102 	}
3103 	sdlist[0] = dptr;
3104 
3105 	/* search base is dn */
3106 	dptr->basedn = strdup(dn);
3107 
3108 	/* search scope is base */
3109 	dptr->scope = NS_LDAP_SCOPE_BASE;
3110 
3111 	/* search filter is "nisdomain=*" */
3112 	dptr->filter = strdup(_NIS_FILTER);
3113 
3114 	cookie->sdlist = sdlist;
3115 	cookie->i_filter = strdup(dptr->filter);
3116 	cookie->i_attr = nis_domain_attrs;
3117 	cookie->i_auth = cred;
3118 	cookie->i_flags = 0;
3119 
3120 	/* Process search */
3121 	rc = search_state_machine(cookie, INIT, 0);
3122 
3123 	/* Copy domain name if found */
3124 	rc = cookie->err_rc;
3125 	if (rc != NS_LDAP_SUCCESS)
3126 		*errorp = cookie->errorp;
3127 	if (cookie->result == NULL)
3128 		rc = NS_LDAP_NOTFOUND;
3129 	if (rc == NS_LDAP_SUCCESS) {
3130 		value = __ns_ldap_getAttr(cookie->result->entry,
3131 		    _NIS_DOMAIN);
3132 		if (value[0])
3133 			*domainname = strdup(value[0]);
3134 		else
3135 			rc = NS_LDAP_NOTFOUND;
3136 	}
3137 	if (cookie->result != NULL)
3138 		(void) __ns_ldap_freeResult(&cookie->result);
3139 	cookie->errorp = NULL;
3140 	delete_search_cookie(cookie);
3141 	cookie = NULL;
3142 	return (rc);
3143 }
3144 
3145 int
3146 __ns_ldap_firstEntry(
3147 	const char *service,
3148 	const char *filter,
3149 	int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
3150 	char **realfilter, const void *userdata),
3151 	const char * const *attribute,
3152 	const ns_cred_t *auth,
3153 	const int flags,
3154 	void **vcookie,
3155 	ns_ldap_result_t **result,
3156 	ns_ldap_error_t ** errorp,
3157 	const void *userdata)
3158 {
3159 	ns_ldap_cookie_t	*cookie = NULL;
3160 	ns_ldap_error_t		*error = NULL;
3161 	ns_state_t		state;
3162 	ns_ldap_search_desc_t	**sdlist;
3163 	ns_ldap_search_desc_t	*dptr;
3164 	char			**dns = NULL;
3165 	int			scope;
3166 	int			rc;
3167 
3168 	*errorp = NULL;
3169 	*result = NULL;
3170 
3171 	/* get the service descriptor - or create a default one */
3172 	rc = __s_api_get_SSD_from_SSDtoUse_service(service,
3173 	    &sdlist, errorp);
3174 	if (rc != NS_LDAP_SUCCESS) {
3175 		*errorp = error;
3176 		return (rc);
3177 	}
3178 	if (sdlist == NULL) {
3179 		/* Create default service Desc */
3180 		sdlist = (ns_ldap_search_desc_t **)calloc(2,
3181 		    sizeof (ns_ldap_search_desc_t *));
3182 		if (sdlist == NULL) {
3183 			return (NS_LDAP_MEMORY);
3184 		}
3185 		dptr = (ns_ldap_search_desc_t *)
3186 		    calloc(1, sizeof (ns_ldap_search_desc_t));
3187 		if (dptr == NULL) {
3188 			free(sdlist);
3189 			return (NS_LDAP_MEMORY);
3190 		}
3191 		sdlist[0] = dptr;
3192 
3193 		/* default base */
3194 		rc = __s_api_getDNs(&dns, service, &error);
3195 		if (rc != NS_LDAP_SUCCESS) {
3196 			if (dns) {
3197 				__s_api_free2dArray(dns);
3198 				dns = NULL;
3199 			}
3200 			if (sdlist) {
3201 				(void) __ns_ldap_freeSearchDescriptors(
3202 				    &sdlist);
3203 
3204 				sdlist = NULL;
3205 			}
3206 			*errorp = error;
3207 			return (rc);
3208 		}
3209 		dptr->basedn = strdup(dns[0]);
3210 		__s_api_free2dArray(dns);
3211 		dns = NULL;
3212 
3213 		/* default scope */
3214 		scope = 0;
3215 		cookie = init_search_state_machine();
3216 		if (cookie == NULL) {
3217 			if (sdlist) {
3218 				(void) __ns_ldap_freeSearchDescriptors(&sdlist);
3219 				sdlist = NULL;
3220 			}
3221 			return (NS_LDAP_MEMORY);
3222 		}
3223 		rc = __s_api_getSearchScope(&scope, &cookie->errorp);
3224 		dptr->scope = scope;
3225 	}
3226 
3227 	/* Initialize State machine cookie */
3228 	if (cookie == NULL)
3229 		cookie = init_search_state_machine();
3230 	if (cookie == NULL) {
3231 		if (sdlist) {
3232 			(void) __ns_ldap_freeSearchDescriptors(&sdlist);
3233 			sdlist = NULL;
3234 		}
3235 		return (NS_LDAP_MEMORY);
3236 	}
3237 
3238 	cookie->sdlist = sdlist;
3239 
3240 	/* see if need to follow referrals */
3241 	rc = __s_api_toFollowReferrals(flags,
3242 	    &cookie->followRef, errorp);
3243 	if (rc != NS_LDAP_SUCCESS) {
3244 		delete_search_cookie(cookie);
3245 		return (rc);
3246 	}
3247 
3248 	/*
3249 	 * use VLV/PAGE control only if NS_LDAP_NO_PAGE_CTRL is not set
3250 	 */
3251 	if (flags & NS_LDAP_NO_PAGE_CTRL)
3252 		cookie->use_paging = FALSE;
3253 	else
3254 		cookie->use_paging = TRUE;
3255 
3256 	/* Set up other arguments */
3257 	cookie->userdata = userdata;
3258 	if (init_filter_cb != NULL) {
3259 		cookie->init_filter_cb = init_filter_cb;
3260 		cookie->use_filtercb = 1;
3261 	}
3262 	cookie->use_usercb = 0;
3263 	if (service) {
3264 		cookie->service = strdup(service);
3265 		if (cookie->service == NULL) {
3266 			delete_search_cookie(cookie);
3267 			return (NS_LDAP_MEMORY);
3268 		}
3269 	}
3270 
3271 	cookie->i_filter = strdup(filter);
3272 	cookie->i_attr = attribute;
3273 	cookie->i_auth = auth;
3274 	cookie->i_flags = flags;
3275 
3276 	state = INIT;
3277 	for (;;) {
3278 		state = search_state_machine(cookie, state, ONE_STEP);
3279 		switch (state) {
3280 		case PROCESS_RESULT:
3281 			*result = cookie->result;
3282 			cookie->result = NULL;
3283 			*vcookie = (void *)cookie;
3284 			return (NS_LDAP_SUCCESS);
3285 		case LDAP_ERROR:
3286 			state = search_state_machine(cookie, state, ONE_STEP);
3287 			state = search_state_machine(cookie, CLEAR_RESULTS,
3288 			    ONE_STEP);
3289 			/* FALLTHROUGH */
3290 		case ERROR:
3291 			rc = cookie->err_rc;
3292 			*errorp = cookie->errorp;
3293 			cookie->errorp = NULL;
3294 			delete_search_cookie(cookie);
3295 			return (rc);
3296 		case EXIT:
3297 			rc = cookie->err_rc;
3298 			if (rc != NS_LDAP_SUCCESS) {
3299 				*errorp = cookie->errorp;
3300 				cookie->errorp = NULL;
3301 			} else {
3302 				rc = NS_LDAP_NOTFOUND;
3303 			}
3304 
3305 			delete_search_cookie(cookie);
3306 			return (rc);
3307 
3308 		default:
3309 			break;
3310 		}
3311 	}
3312 }
3313 
3314 /*ARGSUSED2*/
3315 int
3316 __ns_ldap_nextEntry(
3317 	void *vcookie,
3318 	ns_ldap_result_t **result,
3319 	ns_ldap_error_t ** errorp)
3320 {
3321 	ns_ldap_cookie_t	*cookie;
3322 	ns_state_t		state;
3323 	int			rc;
3324 
3325 	cookie = (ns_ldap_cookie_t *)vcookie;
3326 	cookie->result = NULL;
3327 	*result = NULL;
3328 
3329 	state = END_PROCESS_RESULT;
3330 	for (;;) {
3331 		/*
3332 		 * if the ldap connection being used is shared,
3333 		 * ensure the thread-specific data area for setting
3334 		 * status is allocated
3335 		 */
3336 		if (cookie->conn->shared > 0) {
3337 			rc = __s_api_check_MTC_tsd();
3338 			if (rc != NS_LDAP_SUCCESS)
3339 				return (rc);
3340 		}
3341 
3342 		state = search_state_machine(cookie, state, ONE_STEP);
3343 		switch (state) {
3344 		case PROCESS_RESULT:
3345 			*result = cookie->result;
3346 			cookie->result = NULL;
3347 			return (NS_LDAP_SUCCESS);
3348 		case LDAP_ERROR:
3349 			state = search_state_machine(cookie, state, ONE_STEP);
3350 			state = search_state_machine(cookie, CLEAR_RESULTS,
3351 			    ONE_STEP);
3352 			/* FALLTHROUGH */
3353 		case ERROR:
3354 			rc = cookie->err_rc;
3355 			*errorp = cookie->errorp;
3356 			cookie->errorp = NULL;
3357 			return (rc);
3358 		case EXIT:
3359 			return (NS_LDAP_SUCCESS);
3360 		}
3361 	}
3362 }
3363 
3364 int
3365 __ns_ldap_endEntry(
3366 	void **vcookie,
3367 	ns_ldap_error_t ** errorp)
3368 {
3369 	ns_ldap_cookie_t	*cookie;
3370 	int			rc;
3371 
3372 	if (*vcookie == NULL)
3373 		return (NS_LDAP_INVALID_PARAM);
3374 
3375 	cookie = (ns_ldap_cookie_t *)(*vcookie);
3376 	cookie->result = NULL;
3377 
3378 	/* Complete search */
3379 	rc = search_state_machine(cookie, CLEAR_RESULTS, 0);
3380 
3381 	/* Copy results back to user */
3382 	rc = cookie->err_rc;
3383 	if (rc != NS_LDAP_SUCCESS)
3384 		*errorp = cookie->errorp;
3385 
3386 	cookie->errorp = NULL;
3387 	delete_search_cookie(cookie);
3388 	cookie = NULL;
3389 	*vcookie = NULL;
3390 
3391 	return (rc);
3392 }
3393 
3394 
3395 int
3396 __ns_ldap_freeResult(ns_ldap_result_t **result)
3397 {
3398 
3399 	ns_ldap_entry_t	*curEntry = NULL;
3400 	ns_ldap_entry_t	*delEntry = NULL;
3401 	int		i;
3402 	ns_ldap_result_t	*res = *result;
3403 
3404 #ifdef DEBUG
3405 	(void) fprintf(stderr, "__ns_ldap_freeResult START\n");
3406 #endif
3407 	if (res == NULL)
3408 		return (NS_LDAP_INVALID_PARAM);
3409 
3410 	if (res->entry != NULL)
3411 		curEntry = res->entry;
3412 
3413 	for (i = 0; i < res->entries_count; i++) {
3414 		if (curEntry != NULL) {
3415 			delEntry = curEntry;
3416 			curEntry = curEntry->next;
3417 			__ns_ldap_freeEntry(delEntry);
3418 		}
3419 	}
3420 
3421 	free(res);
3422 	*result = NULL;
3423 	return (NS_LDAP_SUCCESS);
3424 }
3425 
3426 /*ARGSUSED*/
3427 int
3428 __ns_ldap_auth(const ns_cred_t *auth,
3429 		    const int flags,
3430 		    ns_ldap_error_t **errorp,
3431 		    LDAPControl **serverctrls,
3432 		    LDAPControl **clientctrls)
3433 {
3434 
3435 	ConnectionID	connectionId = -1;
3436 	Connection	*conp;
3437 	int		rc = 0;
3438 	int		do_not_fail_if_new_pwd_reqd = 0;
3439 	int		nopasswd_acct_mgmt = 0;
3440 
3441 
3442 #ifdef DEBUG
3443 	(void) fprintf(stderr, "__ns_ldap_auth START\n");
3444 #endif
3445 
3446 	*errorp = NULL;
3447 	if (!auth)
3448 		return (NS_LDAP_INVALID_PARAM);
3449 
3450 	rc = __s_api_getConnection(NULL, flags | NS_LDAP_NEW_CONN,
3451 	    auth, &connectionId, &conp, errorp,
3452 	    do_not_fail_if_new_pwd_reqd, nopasswd_acct_mgmt);
3453 	if (rc == NS_LDAP_OP_FAILED && *errorp)
3454 		(void) __ns_ldap_freeError(errorp);
3455 
3456 	if (connectionId > -1)
3457 		DropConnection(connectionId, flags);
3458 	return (rc);
3459 }
3460 
3461 char **
3462 __ns_ldap_getAttr(const ns_ldap_entry_t *entry, const char *attrname)
3463 {
3464 	int	i;
3465 
3466 	if (entry == NULL)
3467 		return (NULL);
3468 	for (i = 0; i < entry->attr_count; i++) {
3469 		if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == NULL)
3470 			return (entry->attr_pair[i]->attrvalue);
3471 	}
3472 	return (NULL);
3473 }
3474 
3475 ns_ldap_attr_t *
3476 __ns_ldap_getAttrStruct(const ns_ldap_entry_t *entry, const char *attrname)
3477 {
3478 	int	i;
3479 
3480 	if (entry == NULL)
3481 		return (NULL);
3482 	for (i = 0; i < entry->attr_count; i++) {
3483 		if (strcasecmp(entry->attr_pair[i]->attrname, attrname) == NULL)
3484 			return (entry->attr_pair[i]);
3485 	}
3486 	return (NULL);
3487 }
3488 
3489 
3490 /*ARGSUSED*/
3491 int
3492 __ns_ldap_uid2dn(const char *uid,
3493 		char **userDN,
3494 		const ns_cred_t *cred,	/* cred is ignored */
3495 		ns_ldap_error_t **errorp)
3496 {
3497 	ns_ldap_result_t	*result = NULL;
3498 	char		*filter, *userdata;
3499 	char		errstr[MAXERROR];
3500 	char		**value;
3501 	int		rc = 0;
3502 	int		i = 0;
3503 	size_t		len;
3504 
3505 	*errorp = NULL;
3506 	*userDN = NULL;
3507 	if ((uid == NULL) || (uid[0] == '\0'))
3508 		return (NS_LDAP_INVALID_PARAM);
3509 
3510 	while (uid[i] != '\0') {
3511 		if (uid[i] == '=') {
3512 			*userDN = strdup(uid);
3513 			return (NS_LDAP_SUCCESS);
3514 		}
3515 		i++;
3516 	}
3517 	i = 0;
3518 	while ((uid[i] != '\0') && (isdigit(uid[i])))
3519 		i++;
3520 	if (uid[i] == '\0') {
3521 		len = strlen(UIDNUMFILTER) + strlen(uid) + 1;
3522 		filter = (char *)malloc(len);
3523 		if (filter == NULL) {
3524 			*userDN = NULL;
3525 			return (NS_LDAP_MEMORY);
3526 		}
3527 		(void) snprintf(filter, len, UIDNUMFILTER, uid);
3528 
3529 		len = strlen(UIDNUMFILTER_SSD) + strlen(uid) + 1;
3530 		userdata = (char *)malloc(len);
3531 		if (userdata == NULL) {
3532 			*userDN = NULL;
3533 			return (NS_LDAP_MEMORY);
3534 		}
3535 		(void) snprintf(userdata, len, UIDNUMFILTER_SSD, uid);
3536 	} else {
3537 		len = strlen(UIDFILTER) + strlen(uid) + 1;
3538 		filter = (char *)malloc(len);
3539 		if (filter == NULL) {
3540 			*userDN = NULL;
3541 			return (NS_LDAP_MEMORY);
3542 		}
3543 		(void) snprintf(filter, len, UIDFILTER, uid);
3544 
3545 		len = strlen(UIDFILTER_SSD) + strlen(uid) + 1;
3546 		userdata = (char *)malloc(len);
3547 		if (userdata == NULL) {
3548 			*userDN = NULL;
3549 			return (NS_LDAP_MEMORY);
3550 		}
3551 		(void) snprintf(userdata, len, UIDFILTER_SSD, uid);
3552 	}
3553 
3554 	/*
3555 	 * we want to retrieve the DN as it appears in LDAP
3556 	 * hence the use of NS_LDAP_NOT_CVT_DN in flags
3557 	 */
3558 	rc = __ns_ldap_list("passwd", filter,
3559 	    __s_api_merge_SSD_filter,
3560 	    NULL, cred, NS_LDAP_NOT_CVT_DN,
3561 	    &result, errorp, NULL,
3562 	    userdata);
3563 	free(filter);
3564 	filter = NULL;
3565 	free(userdata);
3566 	userdata = NULL;
3567 	if (rc != NS_LDAP_SUCCESS) {
3568 		if (result) {
3569 			(void) __ns_ldap_freeResult(&result);
3570 			result = NULL;
3571 		}
3572 		return (rc);
3573 	}
3574 	if (result->entries_count > 1) {
3575 		(void) __ns_ldap_freeResult(&result);
3576 		result = NULL;
3577 		*userDN = NULL;
3578 		(void) sprintf(errstr,
3579 		    gettext("Too many entries are returned for %s"), uid);
3580 		MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
3581 		    NULL);
3582 		return (NS_LDAP_INTERNAL);
3583 	}
3584 
3585 	value = __ns_ldap_getAttr(result->entry, "dn");
3586 	*userDN = strdup(value[0]);
3587 	(void) __ns_ldap_freeResult(&result);
3588 	result = NULL;
3589 	return (NS_LDAP_SUCCESS);
3590 }
3591 
3592 
3593 /*ARGSUSED*/
3594 int
3595 __ns_ldap_host2dn(const char *host,
3596 		const char *domain,
3597 		char **hostDN,
3598 		const ns_cred_t *cred,	/* cred is ignored */
3599 		ns_ldap_error_t **errorp)
3600 {
3601 	ns_ldap_result_t	*result = NULL;
3602 	char		*filter, *userdata;
3603 	char		errstr[MAXERROR];
3604 	char		**value;
3605 	int		rc;
3606 	size_t		len;
3607 
3608 /*
3609  * XXX
3610  * the domain parameter needs to be used in case domain is not local, if
3611  * this routine is to support multi domain setups, it needs lots of work...
3612  */
3613 	*errorp = NULL;
3614 	*hostDN = NULL;
3615 	if ((host == NULL) || (host[0] == '\0'))
3616 		return (NS_LDAP_INVALID_PARAM);
3617 
3618 	len = strlen(HOSTFILTER) + strlen(host) + 1;
3619 	filter = (char *)malloc(len);
3620 	if (filter == NULL) {
3621 		return (NS_LDAP_MEMORY);
3622 	}
3623 	(void) snprintf(filter,	len, HOSTFILTER, host);
3624 
3625 	len = strlen(HOSTFILTER_SSD) + strlen(host) + 1;
3626 	userdata = (char *)malloc(len);
3627 	if (userdata == NULL) {
3628 		return (NS_LDAP_MEMORY);
3629 	}
3630 	(void) snprintf(userdata, len, HOSTFILTER_SSD, host);
3631 
3632 	/*
3633 	 * we want to retrieve the DN as it appears in LDAP
3634 	 * hence the use of NS_LDAP_NOT_CVT_DN in flags
3635 	 */
3636 	rc = __ns_ldap_list("hosts", filter,
3637 	    __s_api_merge_SSD_filter,
3638 	    NULL, cred, NS_LDAP_NOT_CVT_DN, &result,
3639 	    errorp, NULL,
3640 	    userdata);
3641 	free(filter);
3642 	filter = NULL;
3643 	free(userdata);
3644 	userdata = NULL;
3645 	if (rc != NS_LDAP_SUCCESS) {
3646 		if (result) {
3647 			(void) __ns_ldap_freeResult(&result);
3648 			result = NULL;
3649 		}
3650 		return (rc);
3651 	}
3652 
3653 	if (result->entries_count > 1) {
3654 		(void) __ns_ldap_freeResult(&result);
3655 		result = NULL;
3656 		*hostDN = NULL;
3657 		(void) sprintf(errstr,
3658 		    gettext("Too many entries are returned for %s"), host);
3659 		MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL, strdup(errstr),
3660 		    NULL);
3661 		return (NS_LDAP_INTERNAL);
3662 	}
3663 
3664 	value = __ns_ldap_getAttr(result->entry, "dn");
3665 	*hostDN = strdup(value[0]);
3666 	(void) __ns_ldap_freeResult(&result);
3667 	result = NULL;
3668 	return (NS_LDAP_SUCCESS);
3669 }
3670 
3671 /*ARGSUSED*/
3672 int
3673 __ns_ldap_dn2domain(const char *dn,
3674 			char **domain,
3675 			const ns_cred_t *cred,
3676 			ns_ldap_error_t **errorp)
3677 {
3678 	int		rc, pnum, i, j, len = 0;
3679 	char		*newdn, **rdns = NULL;
3680 	char		**dns, *dn1;
3681 
3682 	*errorp = NULL;
3683 
3684 	if (domain == NULL)
3685 		return (NS_LDAP_INVALID_PARAM);
3686 	else
3687 		*domain = NULL;
3688 
3689 	if ((dn == NULL) || (dn[0] == '\0'))
3690 		return (NS_LDAP_INVALID_PARAM);
3691 
3692 	/*
3693 	 * break dn into rdns
3694 	 */
3695 	dn1 = strdup(dn);
3696 	if (dn1 == NULL)
3697 		return (NS_LDAP_MEMORY);
3698 	rdns = ldap_explode_dn(dn1, 0);
3699 	free(dn1);
3700 	if (rdns == NULL || *rdns == NULL)
3701 		return (NS_LDAP_INVALID_PARAM);
3702 
3703 	for (i = 0; rdns[i]; i++)
3704 		len += strlen(rdns[i]) + 1;
3705 	pnum = i;
3706 
3707 	newdn = (char *)malloc(len + 1);
3708 	dns = (char **)calloc(pnum, sizeof (char *));
3709 	if (newdn == NULL || dns == NULL) {
3710 		if (newdn)
3711 			free(newdn);
3712 		ldap_value_free(rdns);
3713 		return (NS_LDAP_MEMORY);
3714 	}
3715 
3716 	/* construct a semi-normalized dn, newdn */
3717 	*newdn = '\0';
3718 	for (i = 0; rdns[i]; i++) {
3719 		dns[i] = newdn + strlen(newdn);
3720 		(void) strcat(newdn,
3721 		    __s_api_remove_rdn_space(rdns[i]));
3722 		(void) strcat(newdn, ",");
3723 	}
3724 	/* remove the last ',' */
3725 	newdn[strlen(newdn) - 1] = '\0';
3726 	ldap_value_free(rdns);
3727 
3728 	/*
3729 	 * loop and find the domain name associated with newdn,
3730 	 * removing rdn one by one from left to right
3731 	 */
3732 	for (i = 0; i < pnum; i++) {
3733 
3734 		if (*errorp)
3735 			(void) __ns_ldap_freeError(errorp);
3736 
3737 		/*
3738 		 *  try cache manager first
3739 		 */
3740 		rc = __s_api_get_cachemgr_data(NS_CACHE_DN2DOMAIN,
3741 		    dns[i], domain);
3742 		if (rc != NS_LDAP_SUCCESS) {
3743 			/*
3744 			 *  try ldap server second
3745 			 */
3746 			rc = __s_api_find_domainname(dns[i], domain,
3747 			    cred, errorp);
3748 		} else {
3749 			/*
3750 			 * skip the last one,
3751 			 * since it is already cached by ldap_cachemgr
3752 			 */
3753 			i--;
3754 		}
3755 		if (rc == NS_LDAP_SUCCESS) {
3756 			if (__s_api_nscd_proc()) {
3757 				/*
3758 				 * If it's nscd, ask cache manager to save the
3759 				 * dn to domain mapping(s)
3760 				 */
3761 				for (j = 0; j <= i; j++) {
3762 					(void) __s_api_set_cachemgr_data(
3763 					    NS_CACHE_DN2DOMAIN,
3764 					    dns[j],
3765 					    *domain);
3766 				}
3767 			}
3768 			break;
3769 		}
3770 	}
3771 
3772 	free(dns);
3773 	free(newdn);
3774 	if (rc != NS_LDAP_SUCCESS)
3775 		rc = NS_LDAP_NOTFOUND;
3776 	return (rc);
3777 }
3778 
3779 /*ARGSUSED*/
3780 int
3781 __ns_ldap_getServiceAuthMethods(const char *service,
3782 		ns_auth_t ***auth,
3783 		ns_ldap_error_t **errorp)
3784 {
3785 	char		errstr[MAXERROR];
3786 	int		rc, i, done = 0;
3787 	int		slen;
3788 	void		**param;
3789 	char		**sam, *srv, *send;
3790 	ns_auth_t	**authpp = NULL, *ap;
3791 	int		cnt, max;
3792 	ns_config_t	*cfg;
3793 	ns_ldap_error_t	*error = NULL;
3794 
3795 	if (errorp == NULL)
3796 		return (NS_LDAP_INVALID_PARAM);
3797 	*errorp = NULL;
3798 
3799 	if ((service == NULL) || (service[0] == '\0') ||
3800 	    (auth == NULL))
3801 		return (NS_LDAP_INVALID_PARAM);
3802 
3803 	*auth = NULL;
3804 	rc = __ns_ldap_getParam(NS_LDAP_SERVICE_AUTH_METHOD_P, &param, &error);
3805 	if (rc != NS_LDAP_SUCCESS || param == NULL) {
3806 		*errorp = error;
3807 		return (rc);
3808 	}
3809 	sam = (char **)param;
3810 
3811 	cfg = __s_api_get_default_config();
3812 	cnt = 0;
3813 
3814 	slen = strlen(service);
3815 
3816 	for (; *sam; sam++) {
3817 		srv = *sam;
3818 		if (strncasecmp(service, srv, slen) != 0)
3819 			continue;
3820 		srv += slen;
3821 		if (*srv != COLONTOK)
3822 			continue;
3823 		send = srv;
3824 		srv++;
3825 		for (max = 1; (send = strchr(++send, SEMITOK)) != NULL;
3826 		    max++) {}
3827 		authpp = (ns_auth_t **)calloc(++max, sizeof (ns_auth_t *));
3828 		if (authpp == NULL) {
3829 			(void) __ns_ldap_freeParam(&param);
3830 			__s_api_release_config(cfg);
3831 			return (NS_LDAP_MEMORY);
3832 		}
3833 		while (!done) {
3834 			send = strchr(srv, SEMITOK);
3835 			if (send != NULL) {
3836 				*send = '\0';
3837 				send++;
3838 			}
3839 			i = __s_get_enum_value(cfg, srv, NS_LDAP_AUTH_P);
3840 			if (i == -1) {
3841 				(void) __ns_ldap_freeParam(&param);
3842 				(void) sprintf(errstr,
3843 				gettext("Unsupported "
3844 				    "serviceAuthenticationMethod: %s.\n"), srv);
3845 				MKERROR(LOG_WARNING, *errorp, NS_CONFIG_SYNTAX,
3846 				    strdup(errstr), NULL);
3847 				__s_api_release_config(cfg);
3848 				return (NS_LDAP_CONFIG);
3849 			}
3850 			ap = __s_api_AuthEnumtoStruct((EnumAuthType_t)i);
3851 			if (ap == NULL) {
3852 				(void) __ns_ldap_freeParam(&param);
3853 				__s_api_release_config(cfg);
3854 				return (NS_LDAP_MEMORY);
3855 			}
3856 			authpp[cnt++] = ap;
3857 			if (send == NULL)
3858 				done = TRUE;
3859 			else
3860 				srv = send;
3861 		}
3862 	}
3863 
3864 	*auth = authpp;
3865 	(void) __ns_ldap_freeParam(&param);
3866 	__s_api_release_config(cfg);
3867 	return (NS_LDAP_SUCCESS);
3868 }
3869 
3870 /*
3871  * This routine is called when certain scenario occurs
3872  * e.g.
3873  * service == auto_home
3874  * SSD = automount: ou = mytest,
3875  * NS_LDAP_MAPATTRIBUTE= auto_home: automountMapName=AAA
3876  * NS_LDAP_OBJECTCLASSMAP= auto_home:automountMap=MynisMap
3877  * NS_LDAP_OBJECTCLASSMAP= auto_home:automount=MynisObject
3878  *
3879  * The automountMapName is prepended implicitely but is mapped
3880  * to AAA. So dn could appers as
3881  * dn: AAA=auto_home,ou=bar,dc=foo,dc=com
3882  * dn: automountKey=user_01,AAA=auto_home,ou=bar,dc=foo,dc=com
3883  * dn: automountKey=user_02,AAA=auto_home,ou=bar,dc=foo,dc=com
3884  * in the directory.
3885  * This function is called to covert the mapped attr back to
3886  * orig attr when the entries are searched and returned
3887  */
3888 
3889 int
3890 __s_api_convert_automountmapname(const char *service, char **dn,
3891 		ns_ldap_error_t **errp) {
3892 
3893 	char	**mapping = NULL;
3894 	char	*mapped_attr = NULL;
3895 	char	*automountmapname = "automountMapName";
3896 	char	*buffer = NULL;
3897 	int	rc = NS_LDAP_SUCCESS;
3898 	char	errstr[MAXERROR];
3899 
3900 	/*
3901 	 * dn is an input/out parameter, check it first
3902 	 */
3903 
3904 	if (service == NULL || dn == NULL || *dn == NULL)
3905 		return (NS_LDAP_INVALID_PARAM);
3906 
3907 	/*
3908 	 * Check to see if there is a mapped attribute for auto_xxx
3909 	 */
3910 
3911 	mapping = __ns_ldap_getMappedAttributes(service, automountmapname);
3912 
3913 	/*
3914 	 * if no mapped attribute for auto_xxx, try automount
3915 	 */
3916 
3917 	if (mapping == NULL)
3918 		mapping = __ns_ldap_getMappedAttributes(
3919 			"automount", automountmapname);
3920 
3921 	/*
3922 	 * if no mapped attribute is found, return SUCCESS (no op)
3923 	 */
3924 
3925 	if (mapping == NULL)
3926 		return (NS_LDAP_SUCCESS);
3927 
3928 	/*
3929 	 * if the mapped attribute is found and attr is not empty,
3930 	 * copy it
3931 	 */
3932 
3933 	if (mapping[0] != NULL) {
3934 		mapped_attr = strdup(mapping[0]);
3935 		__s_api_free2dArray(mapping);
3936 		if (mapped_attr == NULL) {
3937 			return (NS_LDAP_MEMORY);
3938 		}
3939 	} else {
3940 		__s_api_free2dArray(mapping);
3941 
3942 		(void) snprintf(errstr, (2 * MAXERROR),
3943 			gettext(
3944 			"Attribute nisMapName is mapped to an "
3945 			"empty string.\n"));
3946 
3947 		MKERROR(LOG_ERR, *errp, NS_CONFIG_SYNTAX,
3948 			strdup(errstr), NULL);
3949 
3950 		return (NS_LDAP_CONFIG);
3951 	}
3952 
3953 	/*
3954 	 * Locate the mapped attribute in the dn
3955 	 * and replace it if it exists
3956 	 */
3957 
3958 	rc = __s_api_replace_mapped_attr_in_dn(
3959 		(const char *) automountmapname, (const char *) mapped_attr,
3960 		(const char *) *dn, &buffer);
3961 
3962 	/* clean up */
3963 
3964 	free(mapped_attr);
3965 
3966 	/*
3967 	 * If mapped attr is found(buffer != NULL)
3968 	 *	a new dn is returned
3969 	 * If no mapped attribute is in dn,
3970 	 *	return NS_LDAP_SUCCESS (no op)
3971 	 * If no memory,
3972 	 *	return NS_LDAP_MEMORY (no op)
3973 	 */
3974 
3975 	if (buffer != NULL) {
3976 		free(*dn);
3977 		*dn = buffer;
3978 	}
3979 
3980 	return (rc);
3981 }
3982 
3983 /*
3984  * If the mapped attr is found in the dn,
3985  * 	return NS_LDAP_SUCCESS and a new_dn.
3986  * If no mapped attr is found,
3987  * 	return NS_LDAP_SUCCESS and *new_dn == NULL
3988  * If there is not enough memory,
3989  * 	return NS_LDAP_MEMORY and *new_dn == NULL
3990  */
3991 
3992 int
3993 __s_api_replace_mapped_attr_in_dn(
3994 	const char *orig_attr, const char *mapped_attr,
3995 	const char *dn, char **new_dn) {
3996 
3997 	char	**dnArray = NULL;
3998 	char	*cur = NULL, *start = NULL;
3999 	int	i = 0, found = 0;
4000 	int	len = 0, orig_len = 0, mapped_len = 0;
4001 	int	dn_len = 0, tmp_len = 0;
4002 
4003 	*new_dn = NULL;
4004 
4005 	/*
4006 	 * seperate dn into individual componets
4007 	 * e.g.
4008 	 * "automountKey=user_01" , "automountMapName_test=auto_home", ...
4009 	 */
4010 	dnArray = ldap_explode_dn(dn, 0);
4011 
4012 	/*
4013 	 * This will find "mapped attr=value" in dn.
4014 	 * It won't find match if mapped attr appears
4015 	 * in the value.
4016 	 */
4017 	for (i = 0; dnArray[i] != NULL; i++) {
4018 		/*
4019 		 * This function is called when reading from
4020 		 * the directory so assume each component has "=".
4021 		 * Any ill formatted dn should be rejected
4022 		 * before adding to the directory
4023 		 */
4024 		cur = strchr(dnArray[i], '=');
4025 		*cur = '\0';
4026 		if (strcasecmp(mapped_attr, dnArray[i]) == 0)
4027 			found = 1;
4028 		*cur = '=';
4029 		if (found) break;
4030 	}
4031 
4032 	if (!found) {
4033 		__s_api_free2dArray(dnArray);
4034 		*new_dn = NULL;
4035 		return (NS_LDAP_SUCCESS);
4036 	}
4037 	/*
4038 	 * The new length is *dn length + (difference between
4039 	 * orig attr and mapped attr) + 1 ;
4040 	 * e.g.
4041 	 * automountKey=aa,automountMapName_test=auto_home,dc=foo,dc=com
4042 	 * ==>
4043 	 * automountKey=aa,automountMapName=auto_home,dc=foo,dc=com
4044 	 */
4045 	mapped_len = strlen(mapped_attr);
4046 	orig_len = strlen(orig_attr);
4047 	dn_len = strlen(dn);
4048 	len = dn_len + orig_len - mapped_len + 1;
4049 	*new_dn = (char *)calloc(1, len);
4050 	if (*new_dn == NULL) {
4051 		__s_api_free2dArray(dnArray);
4052 		return (NS_LDAP_MEMORY);
4053 	}
4054 
4055 	/*
4056 	 * Locate the mapped attr in the dn.
4057 	 * Use dnArray[i] instead of mapped_attr
4058 	 * because mapped_attr could appear in
4059 	 * the value
4060 	 */
4061 
4062 	cur = strstr(dn, dnArray[i]);
4063 	__s_api_free2dArray(dnArray);
4064 	/* copy the portion before mapped attr in dn  */
4065 	start = *new_dn;
4066 	tmp_len = cur - dn;
4067 	(void) memcpy((void *) start, (const void*) dn, tmp_len);
4068 
4069 	/*
4070 	 * Copy the orig_attr. e.g. automountMapName
4071 	 * This replaces mapped attr with orig attr
4072 	 */
4073 	start = start + (cur - dn); /* move cursor in buffer */
4074 	(void) memcpy((void *) start, (const void*) orig_attr, orig_len);
4075 
4076 	/*
4077 	 * Copy the portion after mapped attr in dn
4078 	 */
4079 	cur = cur + mapped_len; /* move cursor in  dn  */
4080 	start = start + orig_len; /* move cursor in buffer */
4081 	(void) strcpy(start, cur);
4082 
4083 	return (NS_LDAP_SUCCESS);
4084 }
4085 
4086 /*
4087  * Validate Filter functions
4088  */
4089 
4090 /* ***** Start of modified libldap.so.5 filter parser ***** */
4091 
4092 /* filter parsing routine forward references */
4093 static int adj_filter_list(char *str);
4094 static int adj_simple_filter(char *str);
4095 static int unescape_filterval(char *val);
4096 static int hexchar2int(char c);
4097 static int adj_substring_filter(char *val);
4098 
4099 
4100 /*
4101  * assumes string manipulation is in-line
4102  * and all strings are sufficient in size
4103  * return value is the position after 'c'
4104  */
4105 
4106 static char *
4107 resync_str(char *str, char *next, char c)
4108 {
4109 	char	*ret;
4110 
4111 	ret = str + strlen(str);
4112 	*next = c;
4113 	if (ret == next)
4114 		return (ret);
4115 	(void) strcat(str, next);
4116 	return (ret);
4117 }
4118 
4119 static char *
4120 find_right_paren(char *s)
4121 {
4122 	int	balance, escape;
4123 
4124 	balance = 1;
4125 	escape = 0;
4126 	while (*s && balance) {
4127 		if (escape == 0) {
4128 			if (*s == '(')
4129 				balance++;
4130 			else if (*s == ')')
4131 				balance--;
4132 		}
4133 		if (*s == '\\' && ! escape)
4134 			escape = 1;
4135 		else
4136 			escape = 0;
4137 		if (balance)
4138 			s++;
4139 	}
4140 
4141 	return (*s ? s : NULL);
4142 }
4143 
4144 static char *
4145 adj_complex_filter(char	*str)
4146 {
4147 	char	*next;
4148 
4149 	/*
4150 	 * We have (x(filter)...) with str sitting on
4151 	 * the x.  We have to find the paren matching
4152 	 * the one before the x and put the intervening
4153 	 * filters by calling adj_filter_list().
4154 	 */
4155 
4156 	str++;
4157 	if ((next = find_right_paren(str)) == NULL)
4158 		return (NULL);
4159 
4160 	*next = '\0';
4161 	if (adj_filter_list(str) == -1)
4162 		return (NULL);
4163 	next = resync_str(str, next, ')');
4164 	next++;
4165 
4166 	return (next);
4167 }
4168 
4169 static int
4170 adj_filter(char *str)
4171 {
4172 	char	*next;
4173 	int	parens, balance, escape;
4174 	char	*np, *cp,  *dp;
4175 
4176 	parens = 0;
4177 	while (*str) {
4178 		switch (*str) {
4179 		case '(':
4180 			str++;
4181 			parens++;
4182 			switch (*str) {
4183 			case '&':
4184 				if ((str = adj_complex_filter(str)) == NULL)
4185 					return (-1);
4186 
4187 				parens--;
4188 				break;
4189 
4190 			case '|':
4191 				if ((str = adj_complex_filter(str)) == NULL)
4192 					return (-1);
4193 
4194 				parens--;
4195 				break;
4196 
4197 			case '!':
4198 				if ((str = adj_complex_filter(str)) == NULL)
4199 					return (-1);
4200 
4201 				parens--;
4202 				break;
4203 
4204 			case '(':
4205 				/* illegal ((case - generated by conversion */
4206 
4207 				/* find missing close) */
4208 				np = find_right_paren(str+1);
4209 
4210 				/* error if not found */
4211 				if (np == NULL)
4212 					return (-1);
4213 
4214 				/* remove redundant (and) */
4215 				for (dp = str, cp = str+1; cp < np; ) {
4216 					*dp++ = *cp++;
4217 				}
4218 				cp++;
4219 				while (*cp)
4220 					*dp++ = *cp++;
4221 				*dp = '\0';
4222 
4223 				/* re-start test at original ( */
4224 				parens--;
4225 				str--;
4226 				break;
4227 
4228 			default:
4229 				balance = 1;
4230 				escape = 0;
4231 				next = str;
4232 				while (*next && balance) {
4233 					if (escape == 0) {
4234 						if (*next == '(')
4235 							balance++;
4236 						else if (*next == ')')
4237 							balance--;
4238 					}
4239 					if (*next == '\\' && ! escape)
4240 						escape = 1;
4241 					else
4242 						escape = 0;
4243 					if (balance)
4244 						next++;
4245 				}
4246 				if (balance != 0)
4247 					return (-1);
4248 
4249 				*next = '\0';
4250 				if (adj_simple_filter(str) == -1) {
4251 					return (-1);
4252 				}
4253 				next = resync_str(str, next, ')');
4254 				next++;
4255 				str = next;
4256 				parens--;
4257 				break;
4258 			}
4259 			break;
4260 
4261 		case ')':
4262 			str++;
4263 			parens--;
4264 			break;
4265 
4266 		case ' ':
4267 			str++;
4268 			break;
4269 
4270 		default:	/* assume it's a simple type=value filter */
4271 			next = strchr(str, '\0');
4272 			if (adj_simple_filter(str) == -1) {
4273 				return (-1);
4274 			}
4275 			str = next;
4276 			break;
4277 		}
4278 	}
4279 
4280 	return (parens ? -1 : 0);
4281 }
4282 
4283 
4284 /*
4285  * Put a list of filters like this "(filter1)(filter2)..."
4286  */
4287 
4288 static int
4289 adj_filter_list(char *str)
4290 {
4291 	char	*next;
4292 	char	save;
4293 
4294 	while (*str) {
4295 		while (*str && isspace(*str))
4296 			str++;
4297 		if (*str == '\0')
4298 			break;
4299 
4300 		if ((next = find_right_paren(str + 1)) == NULL)
4301 			return (-1);
4302 		save = *++next;
4303 
4304 		/* now we have "(filter)" with str pointing to it */
4305 		*next = '\0';
4306 		if (adj_filter(str) == -1)
4307 			return (-1);
4308 		next = resync_str(str, next, save);
4309 
4310 		str = next;
4311 	}
4312 
4313 	return (0);
4314 }
4315 
4316 
4317 /*
4318  * is_valid_attr - returns 1 if a is a syntactically valid left-hand side
4319  * of a filter expression, 0 otherwise.  A valid string may contain only
4320  * letters, numbers, hyphens, semi-colons, colons and periods. examples:
4321  *	cn
4322  *	cn;lang-fr
4323  *	1.2.3.4;binary;dynamic
4324  *	mail;dynamic
4325  *	cn:dn:1.2.3.4
4326  *
4327  * For compatibility with older servers, we also allow underscores in
4328  * attribute types, even through they are not allowed by the LDAPv3 RFCs.
4329  */
4330 static int
4331 is_valid_attr(char *a)
4332 {
4333 	for (; *a; a++) {
4334 		if (!isascii(*a)) {
4335 			return (0);
4336 		} else if (!isalnum(*a)) {
4337 			switch (*a) {
4338 			case '-':
4339 			case '.':
4340 			case ';':
4341 			case ':':
4342 			case '_':
4343 				break; /* valid */
4344 			default:
4345 				return (0);
4346 			}
4347 		}
4348 	}
4349 	return (1);
4350 }
4351 
4352 static char *
4353 find_star(char *s)
4354 {
4355 	for (; *s; ++s) {
4356 		switch (*s) {
4357 		case '*':
4358 			return (s);
4359 		case '\\':
4360 			++s;
4361 			if (hexchar2int(s[0]) >= 0 && hexchar2int(s[1]) >= 0)
4362 				++s;
4363 		default:
4364 			break;
4365 		}
4366 	}
4367 	return (NULL);
4368 }
4369 
4370 static int
4371 adj_simple_filter(char *str)
4372 {
4373 	char		*s, *s2, *s3, filterop;
4374 	char		*value;
4375 	int		ftype = 0;
4376 	int		rc;
4377 
4378 	rc = -1;	/* pessimistic */
4379 
4380 	if ((str = strdup(str)) == NULL) {
4381 		return (rc);
4382 	}
4383 
4384 	if ((s = strchr(str, '=')) == NULL) {
4385 		goto free_and_return;
4386 	}
4387 	value = s + 1;
4388 	*s-- = '\0';
4389 	filterop = *s;
4390 	if (filterop == '<' || filterop == '>' || filterop == '~' ||
4391 	    filterop == ':') {
4392 		*s = '\0';
4393 	}
4394 
4395 	if (! is_valid_attr(str)) {
4396 		goto free_and_return;
4397 	}
4398 
4399 	switch (filterop) {
4400 	case '<': /* LDAP_FILTER_LE */
4401 	case '>': /* LDAP_FILTER_GE */
4402 	case '~': /* LDAP_FILTER_APPROX */
4403 		break;
4404 	case ':':	/* extended filter - v3 only */
4405 		/*
4406 		 * extended filter looks like this:
4407 		 *
4408 		 *	[type][':dn'][':'oid]':='value
4409 		 *
4410 		 * where one of type or :oid is required.
4411 		 *
4412 		 */
4413 		s2 = s3 = NULL;
4414 		if ((s2 = strrchr(str, ':')) == NULL) {
4415 			goto free_and_return;
4416 		}
4417 		if (strcasecmp(s2, ":dn") == 0) {
4418 			*s2 = '\0';
4419 		} else {
4420 			*s2 = '\0';
4421 			if ((s3 = strrchr(str, ':')) != NULL) {
4422 				if (strcasecmp(s3, ":dn") != 0) {
4423 					goto free_and_return;
4424 				}
4425 				*s3 = '\0';
4426 			}
4427 		}
4428 		if (unescape_filterval(value) < 0) {
4429 			goto free_and_return;
4430 		}
4431 		rc = 0;
4432 		goto free_and_return;
4433 		/* break; */
4434 	default:
4435 		if (find_star(value) == NULL) {
4436 			ftype = 0; /* LDAP_FILTER_EQUALITY */
4437 		} else if (strcmp(value, "*") == 0) {
4438 			ftype = 1; /* LDAP_FILTER_PRESENT */
4439 		} else {
4440 			rc = adj_substring_filter(value);
4441 			goto free_and_return;
4442 		}
4443 		break;
4444 	}
4445 
4446 	if (ftype != 0) {	/* == LDAP_FILTER_PRESENT */
4447 		rc = 0;
4448 	} else if (unescape_filterval(value) >= 0) {
4449 		rc = 0;
4450 	}
4451 	if (rc != -1) {
4452 		rc = 0;
4453 	}
4454 
4455 free_and_return:
4456 	free(str);
4457 	return (rc);
4458 }
4459 
4460 
4461 /*
4462  * Check in place both LDAPv2 (RFC-1960) and LDAPv3 (hexadecimal) escape
4463  * sequences within the null-terminated string 'val'.
4464  *
4465  * If 'val' contains invalid escape sequences we return -1.
4466  * Otherwise return 1
4467  */
4468 static int
4469 unescape_filterval(char *val)
4470 {
4471 	int	escape, firstdigit;
4472 	char	*s;
4473 
4474 	firstdigit = 0;
4475 	escape = 0;
4476 	for (s = val; *s; s++) {
4477 		if (escape) {
4478 			/*
4479 			 * first try LDAPv3 escape (hexadecimal) sequence
4480 			 */
4481 			if (hexchar2int(*s) < 0) {
4482 				if (firstdigit) {
4483 					/*
4484 					 * LDAPv2 (RFC1960) escape sequence
4485 					 */
4486 					escape = 0;
4487 				} else {
4488 					return (-1);
4489 				}
4490 			}
4491 			if (firstdigit) {
4492 				firstdigit = 0;
4493 			} else {
4494 				escape = 0;
4495 			}
4496 
4497 		} else if (*s != '\\') {
4498 			escape = 0;
4499 
4500 		} else {
4501 			escape = 1;
4502 			firstdigit = 1;
4503 		}
4504 	}
4505 
4506 	return (1);
4507 }
4508 
4509 
4510 /*
4511  * convert character 'c' that represents a hexadecimal digit to an integer.
4512  * if 'c' is not a hexidecimal digit [0-9A-Fa-f], -1 is returned.
4513  * otherwise the converted value is returned.
4514  */
4515 static int
4516 hexchar2int(char c)
4517 {
4518 	if (c >= '0' && c <= '9') {
4519 		return (c - '0');
4520 	}
4521 	if (c >= 'A' && c <= 'F') {
4522 		return (c - 'A' + 10);
4523 	}
4524 	if (c >= 'a' && c <= 'f') {
4525 		return (c - 'a' + 10);
4526 	}
4527 	return (-1);
4528 }
4529 
4530 static int
4531 adj_substring_filter(char *val)
4532 {
4533 	char		*nextstar;
4534 
4535 	for (; val != NULL; val = nextstar) {
4536 		if ((nextstar = find_star(val)) != NULL) {
4537 			*nextstar++ = '\0';
4538 		}
4539 
4540 		if (*val != '\0') {
4541 			if (unescape_filterval(val) < 0) {
4542 				return (-1);
4543 			}
4544 		}
4545 	}
4546 
4547 	return (0);
4548 }
4549 
4550 /* ***** End of modified libldap.so.5 filter parser ***** */
4551 
4552 
4553 /*
4554  * Walk filter, remove redundant parentheses in-line
4555  * verify that the filter is reasonable
4556  */
4557 static int
4558 validate_filter(ns_ldap_cookie_t *cookie)
4559 {
4560 	char			*filter = cookie->filter;
4561 	int			rc;
4562 
4563 	/* Parse filter looking for illegal values */
4564 
4565 	rc = adj_filter(filter);
4566 	if (rc != 0) {
4567 		return (NS_LDAP_OP_FAILED);
4568 	}
4569 
4570 	/* end of filter checking */
4571 
4572 	return (NS_LDAP_SUCCESS);
4573 }
4574 
4575 /*
4576  * Set the account management request control that needs to be sent to server.
4577  * This control is required to get the account management information of
4578  * a user to do local account checking.
4579  */
4580 static int
4581 setup_acctmgmt_params(ns_ldap_cookie_t *cookie)
4582 {
4583 	LDAPControl	*req = NULL, **requestctrls;
4584 
4585 	req = (LDAPControl *)malloc(sizeof (LDAPControl));
4586 
4587 	if (req == NULL)
4588 		return (NS_LDAP_MEMORY);
4589 
4590 	/* fill in the fields of this new control */
4591 	req->ldctl_iscritical = 1;
4592 	req->ldctl_oid = strdup(NS_LDAP_ACCOUNT_USABLE_CONTROL);
4593 	if (req->ldctl_oid == NULL) {
4594 		free(req);
4595 		return (NS_LDAP_MEMORY);
4596 	}
4597 	req->ldctl_value.bv_len = 0;
4598 	req->ldctl_value.bv_val = NULL;
4599 
4600 	requestctrls = (LDAPControl **)calloc(2, sizeof (LDAPControl *));
4601 	if (requestctrls == NULL) {
4602 		ldap_control_free(req);
4603 		return (NS_LDAP_MEMORY);
4604 	}
4605 
4606 	requestctrls[0] = req;
4607 
4608 	cookie->p_serverctrls = requestctrls;
4609 
4610 	return (NS_LDAP_SUCCESS);
4611 }
4612 
4613 /*
4614  * int get_new_acct_more_info(BerElement *ber,
4615  *     AcctUsableResponse_t *acctResp)
4616  *
4617  * Decode the more_info data from an Account Management control response,
4618  * when the account is not usable and when code style is from recent LDAP
4619  * servers (see below comments for parse_acct_cont_resp_msg() to get more
4620  * details on coding styles and ASN1 description).
4621  *
4622  * Expected BER encoding: {tbtbtbtiti}
4623  *      +t: tag is 0
4624  *	+b: TRUE if inactive due to account inactivation
4625  *      +t: tag is 1
4626  * 	+b: TRUE if password has been reset
4627  *      +t: tag is 2
4628  * 	+b: TRUE if password is expired
4629  *	+t: tag is 3
4630  *	+i: contains num of remaining grace, 0 means no grace
4631  *	+t: tag is 4
4632  *	+i: contains num of seconds before auto-unlock. -1 means acct is locked
4633  *		forever (i.e. until reset)
4634  *
4635  * Asumptions:
4636  * - ber is not null
4637  * - acctResp is not null and is initialized with default values for the
4638  *   fields in its AcctUsableResp.more_info structure
4639  * - the ber stream is received in the correct order, per the ASN1 description.
4640  *   We do not check this order and make the asumption that it is correct.
4641  *   Note that the ber stream may not (and will not in most cases) contain
4642  *   all fields.
4643  */
4644 static int
4645 get_new_acct_more_info(BerElement *ber, AcctUsableResponse_t *acctResp)
4646 {
4647 	int		rc = NS_LDAP_SUCCESS;
4648 	char		errstr[MAXERROR];
4649 	ber_tag_t	rTag = LBER_DEFAULT;
4650 	ber_len_t	rLen = 0;
4651 	ber_int_t	rValue;
4652 	char		*last;
4653 	int		berRC = 0;
4654 
4655 	/*
4656 	 * Look at what more_info BER element is/are left to be decoded.
4657 	 * look at each of them 1 by 1, without checking on their order
4658 	 * and possible multi values.
4659 	 */
4660 	for (rTag = ber_first_element(ber, &rLen, &last);
4661 	    rTag != LBER_END_OF_SEQORSET;
4662 	    rTag = ber_next_element(ber, &rLen, last)) {
4663 
4664 		berRC = 0;
4665 		switch (rTag) {
4666 		case 0 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
4667 			/* inactive */
4668 			berRC = ber_scanf(ber, "b", &rValue);
4669 			if (berRC != LBER_ERROR) {
4670 				(acctResp->AcctUsableResp).more_info.
4671 				    inactive = (rValue != 0) ? 1 : 0;
4672 			}
4673 			break;
4674 
4675 		case 1 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
4676 			/* reset */
4677 			berRC = ber_scanf(ber, "b", &rValue);
4678 			if (berRC != LBER_ERROR) {
4679 				(acctResp->AcctUsableResp).more_info.reset
4680 				    = (rValue != 0) ? 1 : 0;
4681 			}
4682 			break;
4683 
4684 		case 2 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
4685 			/* expired */
4686 			berRC = ber_scanf(ber, "b", &rValue);
4687 			if (berRC != LBER_ERROR) {
4688 				(acctResp->AcctUsableResp).more_info.expired
4689 				    = (rValue != 0) ? 1 : 0;
4690 			}
4691 			break;
4692 
4693 		case 3 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
4694 			/* remaining grace */
4695 			berRC = ber_scanf(ber, "i", &rValue);
4696 			if (berRC != LBER_ERROR) {
4697 				(acctResp->AcctUsableResp).more_info.rem_grace
4698 				    = rValue;
4699 			}
4700 			break;
4701 
4702 		case 4 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE:
4703 			/* seconds before unlock */
4704 			berRC = ber_scanf(ber, "i", &rValue);
4705 			if (berRC != LBER_ERROR) {
4706 				(acctResp->AcctUsableResp).more_info.
4707 				    sec_b4_unlock = rValue;
4708 			}
4709 			break;
4710 
4711 		default :
4712 			(void) sprintf(errstr,
4713 			    gettext("invalid reason tag 0x%x"), rTag);
4714 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
4715 			rc = NS_LDAP_INTERNAL;
4716 			break;
4717 		}
4718 		if (berRC == LBER_ERROR) {
4719 			(void) sprintf(errstr,
4720 			    gettext("error 0x%x decoding value for "
4721 			    "tag 0x%x"), berRC, rTag);
4722 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
4723 			rc = NS_LDAP_INTERNAL;
4724 		}
4725 		if (rc != NS_LDAP_SUCCESS) {
4726 			/* exit the for loop */
4727 			break;
4728 		}
4729 	}
4730 
4731 	return (rc);
4732 }
4733 
4734 /*
4735  * int get_old_acct_opt_more_info(BerElement *ber,
4736  *     AcctUsableResponse_t *acctResp)
4737  *
4738  * Decode the optional more_info data from an Account Management control
4739  * response, when the account is not usable and when code style is from LDAP
4740  * server 5.2p4 (see below comments for parse_acct_cont_resp_msg() to get more
4741  * details on coding styles and ASN1 description).
4742  *
4743  * Expected BER encoding: titi}
4744  *	+t: tag is 2
4745  *	+i: contains num of remaining grace, 0 means no grace
4746  *	+t: tag is 3
4747  *	+i: contains num of seconds before auto-unlock. -1 means acct is locked
4748  *		forever (i.e. until reset)
4749  *
4750  * Asumptions:
4751  * - ber is a valid BER element
4752  * - acctResp is initialized for the fields in its AcctUsableResp.more_info
4753  *   structure
4754  */
4755 static int
4756 get_old_acct_opt_more_info(ber_tag_t tag, BerElement *ber,
4757     AcctUsableResponse_t *acctResp)
4758 {
4759 	int		rc = NS_LDAP_SUCCESS;
4760 	char		errstr[MAXERROR];
4761 	ber_len_t	len;
4762 	int		rem_grace, sec_b4_unlock;
4763 
4764 	switch (tag) {
4765 	case 2:
4766 		/* decode and maybe 3 is following */
4767 		if ((tag = ber_scanf(ber, "i", &rem_grace)) == LBER_ERROR) {
4768 			(void) sprintf(errstr, gettext("Can not get "
4769 			    "rem_grace"));
4770 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
4771 			rc = NS_LDAP_INTERNAL;
4772 			break;
4773 		}
4774 		(acctResp->AcctUsableResp).more_info.rem_grace = rem_grace;
4775 
4776 		if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
4777 			/* this is a success case, break to exit */
4778 			(void) sprintf(errstr, gettext("No more "
4779 			    "optional data"));
4780 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
4781 			break;
4782 		}
4783 
4784 		if (tag == 3) {
4785 			if (ber_scanf(ber, "i", &sec_b4_unlock) == LBER_ERROR) {
4786 				(void) sprintf(errstr,
4787 				    gettext("Can not get sec_b4_unlock "
4788 				    "- 1st case"));
4789 				syslog(LOG_DEBUG, "libsldap: %s", errstr);
4790 				rc = NS_LDAP_INTERNAL;
4791 				break;
4792 			}
4793 			(acctResp->AcctUsableResp).more_info.sec_b4_unlock =
4794 			    sec_b4_unlock;
4795 		} else { /* unknown tag */
4796 			(void) sprintf(errstr, gettext("Unknown tag "
4797 			    "- 1st case"));
4798 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
4799 			rc = NS_LDAP_INTERNAL;
4800 			break;
4801 		}
4802 		break;
4803 
4804 	case 3:
4805 		if (ber_scanf(ber, "i", &sec_b4_unlock) == LBER_ERROR) {
4806 			(void) sprintf(errstr, gettext("Can not get "
4807 			    "sec_b4_unlock - 2nd case"));
4808 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
4809 			rc = NS_LDAP_INTERNAL;
4810 			break;
4811 		}
4812 		(acctResp->AcctUsableResp).more_info.sec_b4_unlock =
4813 		    sec_b4_unlock;
4814 		break;
4815 
4816 	default: /* unknown tag */
4817 		(void) sprintf(errstr, gettext("Unknown tag - 2nd case"));
4818 		syslog(LOG_DEBUG, "libsldap: %s", errstr);
4819 		rc = NS_LDAP_INTERNAL;
4820 		break;
4821 	}
4822 
4823 	return (rc);
4824 }
4825 
4826 /*
4827  * **** This function needs to be moved to libldap library ****
4828  * parse_acct_cont_resp_msg() parses the message received by server according to
4829  * following format (ASN1 notation):
4830  *
4831  *	ACCOUNT_USABLE_RESPONSE::= CHOICE {
4832  *		is_available		[0] INTEGER,
4833  *				** seconds before expiration **
4834  *		is_not_available	[1] more_info
4835  *	}
4836  *	more_info::= SEQUENCE {
4837  *		inactive		[0] BOOLEAN DEFAULT FALSE,
4838  *		reset			[1] BOOLEAN DEFAULT FALSE,
4839  *		expired			[2] BOOLEAN DEFAULT FALSE,
4840  *		remaining_grace		[3] INTEGER OPTIONAL,
4841  *		seconds_before_unlock	[4] INTEGER OPTIONAL
4842  *	}
4843  */
4844 /*
4845  * #define used to make the difference between coding style as done
4846  * by LDAP server 5.2p4 and newer LDAP servers. There are 4 values:
4847  * - DS52p4_USABLE: 5.2p4 coding style, account is usable
4848  * - DS52p4_NOT_USABLE: 5.2p4 coding style, account is not usable
4849  * - NEW_USABLE: newer LDAP servers coding style, account is usable
4850  * - NEW_NOT_USABLE: newer LDAP servers coding style, account is not usable
4851  *
4852  * An account would be considered not usable if for instance:
4853  * - it's been made inactive in the LDAP server
4854  * - or its password was reset in the LDAP server database
4855  * - or its password expired
4856  * - or the account has been locked, possibly forever
4857  */
4858 #define	DS52p4_USABLE		0x00
4859 #define	DS52p4_NOT_USABLE	0x01
4860 #define	NEW_USABLE		0x00 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE
4861 #define	NEW_NOT_USABLE		0x01 | LBER_CLASS_CONTEXT | LBER_CONSTRUCTED
4862 static int
4863 parse_acct_cont_resp_msg(LDAPControl **ectrls, AcctUsableResponse_t *acctResp)
4864 {
4865 	int		rc = NS_LDAP_SUCCESS;
4866 	BerElement	*ber;
4867 	ber_tag_t 	tag;
4868 	ber_len_t	len;
4869 	int		i;
4870 	char		errstr[MAXERROR];
4871 	/* used for any coding style when account is usable */
4872 	int		seconds_before_expiry;
4873 	/* used for 5.2p4 coding style when account is not usable */
4874 	int		inactive, reset, expired;
4875 
4876 	if (ectrls == NULL) {
4877 		(void) sprintf(errstr, gettext("Invalid ectrls parameter"));
4878 		syslog(LOG_DEBUG, "libsldap: %s", errstr);
4879 		return (NS_LDAP_INVALID_PARAM);
4880 	}
4881 
4882 	for (i = 0; ectrls[i] != NULL; i++) {
4883 		if (strcmp(ectrls[i]->ldctl_oid, NS_LDAP_ACCOUNT_USABLE_CONTROL)
4884 		    == 0) {
4885 			break;
4886 		}
4887 	}
4888 
4889 	if (ectrls[i] == NULL) {
4890 		/* Ldap control is not found */
4891 		(void) sprintf(errstr, gettext("Account Usable Control "
4892 		    "not found"));
4893 		syslog(LOG_DEBUG, "libsldap: %s", errstr);
4894 		return (NS_LDAP_NOTFOUND);
4895 	}
4896 
4897 	/* Allocate a BER element from the control value and parse it. */
4898 	if ((ber = ber_init(&ectrls[i]->ldctl_value)) == NULL)
4899 		return (NS_LDAP_MEMORY);
4900 
4901 	if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
4902 		/* Ldap decoding error */
4903 		(void) sprintf(errstr, gettext("Error decoding 1st tag"));
4904 		syslog(LOG_DEBUG, "libsldap: %s", errstr);
4905 		ber_free(ber, 1);
4906 		return (NS_LDAP_INTERNAL);
4907 	}
4908 
4909 	switch (tag) {
4910 	case DS52p4_USABLE:
4911 	case NEW_USABLE:
4912 		acctResp->choice = 0;
4913 		if (ber_scanf(ber, "i", &seconds_before_expiry)
4914 		    == LBER_ERROR) {
4915 			/* Ldap decoding error */
4916 			(void) sprintf(errstr, gettext("Can not get "
4917 			    "seconds_before_expiry"));
4918 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
4919 			rc = NS_LDAP_INTERNAL;
4920 			break;
4921 		}
4922 		/* ber_scanf() succeeded */
4923 		(acctResp->AcctUsableResp).seconds_before_expiry =
4924 		    seconds_before_expiry;
4925 		break;
4926 
4927 	case DS52p4_NOT_USABLE:
4928 		acctResp->choice = 1;
4929 		if (ber_scanf(ber, "{bbb", &inactive, &reset, &expired)
4930 		    == LBER_ERROR) {
4931 			/* Ldap decoding error */
4932 			(void) sprintf(errstr, gettext("Can not get "
4933 			    "inactive/reset/expired"));
4934 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
4935 			rc = NS_LDAP_INTERNAL;
4936 			break;
4937 		}
4938 		/* ber_scanf() succeeded */
4939 		(acctResp->AcctUsableResp).more_info.inactive =
4940 		    ((inactive == 0) ? 0 : 1);
4941 		(acctResp->AcctUsableResp).more_info.reset =
4942 		    ((reset == 0) ? 0 : 1);
4943 		(acctResp->AcctUsableResp).more_info.expired =
4944 		    ((expired == 0) ? 0 : 1);
4945 		(acctResp->AcctUsableResp).more_info.rem_grace = 0;
4946 		(acctResp->AcctUsableResp).more_info.sec_b4_unlock = 0;
4947 
4948 		if ((tag = ber_peek_tag(ber, &len)) == LBER_ERROR) {
4949 			/* this is a success case, break to exit */
4950 			(void) sprintf(errstr, gettext("No optional data"));
4951 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
4952 			break;
4953 		}
4954 
4955 		/*
4956 		 * Look at what optional more_info BER element is/are
4957 		 * left to be decoded.
4958 		 */
4959 		rc = get_old_acct_opt_more_info(tag, ber, acctResp);
4960 		break;
4961 
4962 	case NEW_NOT_USABLE:
4963 		acctResp->choice = 1;
4964 		/*
4965 		 * Recent LDAP servers won't code more_info data for default
4966 		 * values (see above comments on ASN1 description for what
4967 		 * fields have default values & what fields are optional).
4968 		 */
4969 		(acctResp->AcctUsableResp).more_info.inactive = 0;
4970 		(acctResp->AcctUsableResp).more_info.reset = 0;
4971 		(acctResp->AcctUsableResp).more_info.expired = 0;
4972 		(acctResp->AcctUsableResp).more_info.rem_grace = 0;
4973 		(acctResp->AcctUsableResp).more_info.sec_b4_unlock = 0;
4974 
4975 		if (len == 0) {
4976 			/*
4977 			 * Nothing else to decode; this is valid and we
4978 			 * use default values set above.
4979 			 */
4980 			(void) sprintf(errstr, gettext("more_info is "
4981 			    "empty, using default values"));
4982 			syslog(LOG_DEBUG, "libsldap: %s", errstr);
4983 			break;
4984 		}
4985 
4986 		/*
4987 		 * Look at what more_info BER element is/are left to
4988 		 * be decoded.
4989 		 */
4990 		rc = get_new_acct_more_info(ber, acctResp);
4991 		break;
4992 
4993 	default:
4994 		(void) sprintf(errstr, gettext("unknwon coding style "
4995 		    "(tag: 0x%x)"), tag);
4996 		syslog(LOG_DEBUG, "libsldap: %s", errstr);
4997 		rc = NS_LDAP_INTERNAL;
4998 		break;
4999 	}
5000 
5001 	ber_free(ber, 1);
5002 	return (rc);
5003 }
5004 
5005 /*
5006  * __ns_ldap_getAcctMgmt() is called from pam account management stack
5007  * for retrieving accounting information of users with no user password -
5008  * eg. rlogin, rsh, etc. This function uses the account management control
5009  * request to do a search on the server for the user in question. The
5010  * response control returned from the server is got from the cookie.
5011  * Input params: username of whose account mgmt information is to be got
5012  *		 pointer to hold the parsed account management information
5013  * Return values: NS_LDAP_SUCCESS on success or appropriate error
5014  *		code on failure
5015  */
5016 int
5017 __ns_ldap_getAcctMgmt(const char *user, AcctUsableResponse_t *acctResp)
5018 {
5019 	int		scope, rc;
5020 	char		ldapfilter[1024];
5021 	ns_ldap_cookie_t	*cookie;
5022 	ns_ldap_search_desc_t	**sdlist = NULL;
5023 	ns_ldap_search_desc_t	*dptr;
5024 	ns_ldap_error_t		*error = NULL;
5025 	char			**dns = NULL;
5026 	char		service[] = "shadow";
5027 
5028 	if (user == NULL || acctResp == NULL)
5029 		return (NS_LDAP_INVALID_PARAM);
5030 
5031 	/* Initialize State machine cookie */
5032 	cookie = init_search_state_machine();
5033 	if (cookie == NULL)
5034 		return (NS_LDAP_MEMORY);
5035 
5036 	/* see if need to follow referrals */
5037 	rc = __s_api_toFollowReferrals(0,
5038 	    &cookie->followRef, &error);
5039 	if (rc != NS_LDAP_SUCCESS) {
5040 		(void) __ns_ldap_freeError(&error);
5041 		goto out;
5042 	}
5043 
5044 	/* get the service descriptor - or create a default one */
5045 	rc = __s_api_get_SSD_from_SSDtoUse_service(service,
5046 	    &sdlist, &error);
5047 	if (rc != NS_LDAP_SUCCESS) {
5048 		(void) __ns_ldap_freeError(&error);
5049 		goto out;
5050 	}
5051 
5052 	if (sdlist == NULL) {
5053 		/* Create default service Desc */
5054 		sdlist = (ns_ldap_search_desc_t **)calloc(2,
5055 		    sizeof (ns_ldap_search_desc_t *));
5056 		if (sdlist == NULL) {
5057 			rc = NS_LDAP_MEMORY;
5058 			goto out;
5059 		}
5060 		dptr = (ns_ldap_search_desc_t *)
5061 		    calloc(1, sizeof (ns_ldap_search_desc_t));
5062 		if (dptr == NULL) {
5063 			free(sdlist);
5064 			rc = NS_LDAP_MEMORY;
5065 			goto out;
5066 		}
5067 		sdlist[0] = dptr;
5068 
5069 		/* default base */
5070 		rc = __s_api_getDNs(&dns, service, &cookie->errorp);
5071 		if (rc != NS_LDAP_SUCCESS) {
5072 			if (dns) {
5073 				__s_api_free2dArray(dns);
5074 				dns = NULL;
5075 			}
5076 			(void) __ns_ldap_freeError(&(cookie->errorp));
5077 			cookie->errorp = NULL;
5078 			goto out;
5079 		}
5080 		dptr->basedn = strdup(dns[0]);
5081 		if (dptr->basedn == NULL) {
5082 			free(sdlist);
5083 			free(dptr);
5084 			if (dns) {
5085 				__s_api_free2dArray(dns);
5086 				dns = NULL;
5087 			}
5088 			rc = NS_LDAP_MEMORY;
5089 			goto out;
5090 		}
5091 		__s_api_free2dArray(dns);
5092 		dns = NULL;
5093 
5094 		/* default scope */
5095 		scope = 0;
5096 		rc = __s_api_getSearchScope(&scope, &cookie->errorp);
5097 		dptr->scope = scope;
5098 	}
5099 
5100 	cookie->sdlist = sdlist;
5101 
5102 	cookie->service = strdup(service);
5103 	if (cookie->service == NULL) {
5104 		rc = NS_LDAP_MEMORY;
5105 		goto out;
5106 	}
5107 
5108 	/* search for entries for this particular uid */
5109 	(void) snprintf(ldapfilter, sizeof (ldapfilter), "(uid=%s)", user);
5110 	cookie->i_filter = strdup(ldapfilter);
5111 	if (cookie->i_filter == NULL) {
5112 		rc = NS_LDAP_MEMORY;
5113 		goto out;
5114 	}
5115 
5116 	/* create the control request */
5117 	if ((rc = setup_acctmgmt_params(cookie)) != NS_LDAP_SUCCESS)
5118 		goto out;
5119 
5120 	/* Process search */
5121 	rc = search_state_machine(cookie, GET_ACCT_MGMT_INFO, 0);
5122 
5123 	/* Copy results back to user */
5124 	rc = cookie->err_rc;
5125 	if (rc != NS_LDAP_SUCCESS)
5126 			(void) __ns_ldap_freeError(&(cookie->errorp));
5127 
5128 	if (cookie->result == NULL)
5129 			goto out;
5130 
5131 	if ((rc = parse_acct_cont_resp_msg(cookie->resultctrl, acctResp))
5132 	    != NS_LDAP_SUCCESS)
5133 		goto out;
5134 
5135 	rc = NS_LDAP_SUCCESS;
5136 
5137 out:
5138 	delete_search_cookie(cookie);
5139 
5140 	return (rc);
5141 }
5142