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