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