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