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