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