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