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