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