xref: /illumos-gate/usr/src/cmd/fs.d/autofs/ns_ldap.c (revision dd23d762c65e503874085a3893fbd3df9688da30)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  *	ns_ldap.c
23  *
24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <syslog.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <nsswitch.h>
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <rpc/rpc.h>
37 #include <rpcsvc/nfs_prot.h>
38 #include <sys/errno.h>
39 #include <libintl.h>
40 #include "automount.h"
41 #include "../../../lib/libsldap/common/ns_sldap.h"
42 
43 /*
44  * LDAP schema used for automounter:
45  *
46  * automountMapName: mapname i.e. auto_home, etc.
47  * automountKey: contains the key i.e. the mount point
48  * automountInformation: contains the mount options and remote mount location
49  * description: an optional description (not used by automounter)
50  *
51  * For example, if auto_direct has the following line of data:
52  *
53  * 		/work -rw,intr,nosuid,noquota hosta:/export/work
54  *
55  * Then this would map to the the following LDAP entry:
56  *
57  *	dn: automountKey=/work,automountMapName=auto_direct,...
58  * 	automountKey: /work
59  * 	automountInformation: -rw,intr,nosuid,noquota hosta:/export/work
60  *	objectclass: top
61  *	objectclass: automount
62  *
63  * In this container:
64  *
65  *	dn: automountMapName=auto_direct,...
66  *	automountMapName: auto_direct
67  *	objectClass: top
68  *	objectClass: automountMap
69  *
70  * Note that the schema can be mapped and SSD's can be used to relocate
71  * the default location of these entries.
72  *
73  */
74 
75 #define	CAPCHAR '%'
76 #define	MAXERROR 4000
77 
78 static char *automountKey = NULL;
79 static char *automountInformation = NULL;
80 static char *defaultFilter = NULL;
81 static int encode = 0;
82 
83 static int mastermap_callback_ldap();
84 static int directmap_callback();
85 static int ldap_err(int);
86 static int ldap_match();
87 static int readdir_callback();
88 
89 struct loadmaster_cbdata {
90 	char *ptr1;
91 	char **ptr2;
92 	char ***ptr3;
93 };
94 
95 struct loaddirect_cbdata {
96 	char *ptr1;
97 	char *ptr2;
98 	char **ptr3;
99 	char ***ptr4;
100 };
101 
102 struct dir_cbdata {
103 	struct dir_entry **list;
104 	struct dir_entry *last;
105 	int error;
106 };
107 
108 static char *tosunds_str(char *);
109 static char *tounix_str(char *);
110 
111 static int
112 isAttrMapped(char *orig, char *mapped)
113 {
114 	char **s;
115 	char **mappedschema = NULL;
116 
117 	mappedschema = __ns_ldap_getMappedAttributes("automount", orig);
118 	if (mappedschema == NULL)
119 		return (0);
120 	if (strcasecmp(mappedschema[0], mapped) != 0) {
121 		for (s = mappedschema; *s != NULL; s++)
122 			free(*s);
123 		free(mappedschema);
124 		return (0);
125 	}
126 	for (s = mappedschema; *s != NULL; s++)
127 		free(*s);
128 	free(mappedschema);
129 	return (1);
130 }
131 
132 static int
133 isObjectMapped(char *orig, char *mapped)
134 {
135 	char **s;
136 	char **mappedschema = NULL;
137 
138 	mappedschema = __ns_ldap_getMappedObjectClass("automount", orig);
139 	if (mappedschema == NULL)
140 		return (0);
141 	if (strcasecmp(mappedschema[0], mapped) != 0) {
142 		for (s = mappedschema; *s != NULL; s++)
143 			free(*s);
144 		free(mappedschema);
145 		return (0);
146 	}
147 	for (s = mappedschema; *s != NULL; s++)
148 		free(*s);
149 	free(mappedschema);
150 	return (1);
151 }
152 
153 void
154 init_ldap(char **stack, char ***stkptr)
155 {
156 	/*
157 	 * Check for version of the profile the client is using
158 	 *
159 	 * For version 1 profiles we do encoding of attributes
160 	 * and use nisMap and nisObject schema for backward compatibility.
161 	 *
162 	 * For version 2 profiles we don't do encoding and use
163 	 * automountMap and automount as default attributes (which can
164 	 * then be overridden in libsldap if schema mapping is configured
165 	 * in the profile).
166 	 *
167 	 * If profile version is not available, use version 2 as default
168 	 * and syslog message.
169 	 */
170 	int rc, v2 = 1;
171 	void **paramVal = NULL;
172 	ns_ldap_error_t *errorp = NULL;
173 	struct __nsw_switchconfig *conf = NULL;
174 	struct __nsw_lookup *lkp = NULL;
175 	enum __nsw_parse_err pserr;
176 	int	ldap_configured = 0;
177 
178 #ifdef lint
179 	stack = stack;
180 	stkptr = stkptr;
181 #endif /* lint */
182 
183 	/* get nsswitch info of "automount */
184 	conf = __nsw_getconfig("automount", &pserr);
185 
186 	/* find out if LDAP backend is configured */
187 	if (conf != NULL) {
188 		for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
189 			if (strcmp(lkp->service_name, "ldap") == 0) {
190 				ldap_configured = 1;
191 				break;
192 			}
193 		}
194 		/* free conf at the end of "if"  bracket */
195 		(void) __nsw_freeconfig(conf);
196 	}
197 
198 	/* if ldap is not configured, init_ldap is a no op */
199 	if (!ldap_configured)
200 		return;
201 
202 	rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, &paramVal, &errorp);
203 	if (rc != NS_LDAP_SUCCESS || !paramVal || !*paramVal) {
204 		syslog(LOG_ERR, "Can not determine version of LDAP profile"
205 		    " that is used (%d, %s).  Using version 2 profile"
206 		    " defaults", rc, (errorp && errorp->message ?
207 		    errorp->message : ""));
208 		(void) __ns_ldap_freeError(&errorp);
209 	} else {
210 		if (strcasecmp(*paramVal, NS_LDAP_VERSION_1) == 0)
211 			v2 = 0;
212 		(void) __ns_ldap_freeParam(&paramVal);
213 	}
214 
215 	if (v2) {
216 		if (trace > 1)
217 			trace_prt(1, "init_ldap: setting up for version 2\n");
218 		automountKey = "automountKey";
219 		automountInformation = "automountInformation";
220 		defaultFilter = "(&(objectClass=automount)(automountKey=%s))";
221 
222 		/* check for automountMapName mapped to nisMapName */
223 		if (!isAttrMapped("automountMapName", "nisMapName"))
224 			return;
225 
226 		/* check for automountKey mapped to cn */
227 		if (!isAttrMapped("automountKey", "cn"))
228 			return;
229 
230 		/* check for automountInformation mapped to nisMapEntry */
231 		if (!isAttrMapped("automountInformation", "nisMapEntry"))
232 			return;
233 
234 		/* check for automountMap mapped to nisMap */
235 		if (!isObjectMapped("automountMap", "nisMap"))
236 			return;
237 
238 		/* check for automount mapped to nisObject */
239 		if (!isObjectMapped("automount", "nisObject"))
240 			return;
241 
242 		if (trace > 1)
243 			trace_prt(1, "init_ldap: encode = TRUE\n");
244 		encode = 1;
245 	} else {
246 		if (trace > 1) {
247 			trace_prt(1, "init_ldap: setting up for version 1\n");
248 			trace_prt(1, "init_ldap: encode = TRUE\n");
249 		}
250 		encode = 1;
251 		automountKey = "cn";
252 		automountInformation = "nisMapEntry";
253 		defaultFilter = "(&(objectClass=nisObject)(cn=%s))";
254 	}
255 }
256 
257 /*ARGSUSED*/
258 int
259 getmapent_ldap(char *key, char *map, struct mapline *ml,
260 char **stack, char ***stkptr, bool_t *iswildcard, bool_t isrestricted)
261 {
262 	char *ldap_line = NULL;
263 	char *lp;
264 	int ldap_len, len;
265 	int nserr;
266 
267 	if (trace > 1)
268 		trace_prt(1, "getmapent_ldap called\n");
269 
270 	if (trace > 1) {
271 		trace_prt(1, "getmapent_ldap: key=[ %s ]\n", key);
272 	}
273 
274 	if (iswildcard)
275 		*iswildcard = FALSE;
276 	nserr = ldap_match(map, key, &ldap_line, &ldap_len);
277 	if (nserr) {
278 		if (nserr == __NSW_NOTFOUND) {
279 			/* Try the default entry "*" */
280 			if ((nserr = ldap_match(map, "\\2a", &ldap_line,
281 			    &ldap_len)))
282 				goto done;
283 			else {
284 				if (iswildcard)
285 					*iswildcard = TRUE;
286 			}
287 		} else
288 			goto done;
289 	}
290 
291 	/*
292 	 * at this point we are sure that ldap_match
293 	 * succeeded so massage the entry by
294 	 * 1. ignoring # and beyond
295 	 * 2. trim the trailing whitespace
296 	 */
297 	if (lp = strchr(ldap_line, '#'))
298 		*lp = '\0';
299 	len = strlen(ldap_line);
300 	if (len == 0) {
301 		nserr = __NSW_NOTFOUND;
302 		goto done;
303 	}
304 	lp = &ldap_line[len - 1];
305 	while (lp > ldap_line && isspace(*lp))
306 		*lp-- = '\0';
307 	if (lp == ldap_line) {
308 		nserr = __NSW_NOTFOUND;
309 		goto done;
310 	}
311 	(void) strncpy(ml->linebuf, ldap_line, LINESZ);
312 	unquote(ml->linebuf, ml->lineqbuf);
313 	nserr = __NSW_SUCCESS;
314 done:
315 	if (ldap_line)
316 		free((char *)ldap_line);
317 
318 	if (trace > 1)
319 		trace_prt(1, "getmapent_ldap: exiting ...\n");
320 
321 	return (nserr);
322 }
323 
324 static int
325 ldap_match(char *map, char *key, char **ldap_line, int *ldap_len)
326 {
327 	char searchfilter[LDAP_FILT_MAXSIZ];
328 	int res, attr_found;
329 	ns_ldap_result_t *result = NULL;
330 	ns_ldap_error_t *errp = NULL;
331 	ns_ldap_entry_t *entry = NULL;
332 	char *ldapkey;
333 	int i;
334 
335 	if (trace > 1) {
336 		trace_prt(1, "ldap_match called\n");
337 		trace_prt(1, "ldap_match: key =[ %s ]\n", key);
338 	}
339 
340 	/*
341 	 * need to handle uppercase characters in the key because LDAP
342 	 * searches are case insensitive.  Note, key = attribute automountKey.
343 	 */
344 	if (encode)
345 		ldapkey = tosunds_str(key);
346 	else
347 		ldapkey = key;
348 
349 	if (trace > 1) {
350 		trace_prt(1, "ldap_match: ldapkey =[ %s ]\n", ldapkey);
351 	}
352 
353 	(void) sprintf(searchfilter, defaultFilter, ldapkey);
354 
355 	if (trace > 1)
356 		trace_prt(1, "  ldap_match: Requesting list for %s in %s\n",
357 		    searchfilter, map);
358 
359 	res = __ns_ldap_list(map, searchfilter, NULL,
360 	    NULL, NULL, 0, &result, &errp, NULL, NULL);
361 
362 	if (trace > 1) {
363 		if (res != NS_LDAP_SUCCESS)
364 			trace_prt(1,
365 			    "  ldap_match: __ns_ldap_list FAILED (%d)\n", res);
366 		else
367 			trace_prt(1, "  ldap_match: __ns_ldap_list OK\n");
368 	}
369 
370 	if (res != NS_LDAP_SUCCESS && res != NS_LDAP_NOTFOUND) {
371 		if (errp) {
372 			if (verbose) {
373 				char errstr[MAXERROR];
374 				(void) sprintf(errstr,
375 				    gettext("ldap server can't list map,"
376 				    " '%s': '%s' - '%d'."),
377 				    map, errp->message, errp->status);
378 				syslog(LOG_ERR, errstr);
379 			}
380 			__ns_ldap_freeError(&errp);
381 		} else {
382 			if (verbose) {
383 				char *errmsg;
384 				__ns_ldap_err2str(res, &errmsg);
385 				syslog(LOG_ERR, errmsg);
386 			}
387 		}
388 		if (result)
389 			__ns_ldap_freeResult(&result);
390 		return (ldap_err(res));
391 	}
392 
393 	if (res == NS_LDAP_NOTFOUND || result == NULL ||
394 	    result->entries_count == 0 || result->entry->attr_count == 0) {
395 		if (trace > 1)
396 			trace_prt(1, "  ldap_match: no entries found\n");
397 		if (errp)
398 			__ns_ldap_freeError(&errp);
399 		if (result)
400 			__ns_ldap_freeResult(&result);
401 		return (__NSW_NOTFOUND);
402 	}
403 
404 	/*
405 	 * get value of attribute nisMapEntry.  This attribute contains a
406 	 * list of mount options AND mount location for a particular mount
407 	 * point (key).
408 	 * For example:
409 	 *
410 	 * key: /work
411 	 *	^^^^^
412 	 *	(mount point)
413 	 *
414 	 * nisMapEntry: -rw,intr,nosuid,noquota hosta:/export/work
415 	 *		^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
416 	 *		(    mount options    ) (remote mount location)
417 	 *
418 	 */
419 	attr_found = 0;
420 	entry = result->entry;
421 	for (i = 0; i < entry->attr_count; i++) {
422 		ns_ldap_attr_t *attr;
423 
424 		attr = entry->attr_pair[i];
425 		if (strcasecmp(attr->attrname, automountInformation) == 0) {
426 			char *attrval;
427 
428 			attr_found = 1;
429 			if (encode)
430 				attrval = tounix_str(attr->attrvalue[0]);
431 			else
432 				attrval = attr->attrvalue[0];
433 			*ldap_len = strlen(key) + strlen(attrval);
434 
435 			/*
436 			 * so check for the length; it should be less than
437 			 * LINESZ
438 			 */
439 			if ((*ldap_len + 2) > LINESZ) {
440 				syslog(LOG_ERR,
441 				    "ldap server map %s, entry for %s"
442 				    " is too long %d chars (max %d)",
443 				    map, key, (*ldap_len + 2), LINESZ);
444 				__ns_ldap_freeResult(&result);
445 				return (__NSW_UNAVAIL);
446 			}
447 			*ldap_line = (char *)malloc(*ldap_len + 2);
448 			if (*ldap_line == NULL) {
449 				syslog(LOG_ERR, "ldap_match: malloc failed");
450 				__ns_ldap_freeResult(&result);
451 				return (__NSW_UNAVAIL);
452 			}
453 
454 			(void) sprintf(*ldap_line, "%s", attrval);
455 
456 			break;
457 		}
458 	}
459 
460 	__ns_ldap_freeError(&errp);
461 	__ns_ldap_freeResult(&result);
462 
463 	if (!attr_found)
464 		return (__NSW_NOTFOUND);
465 
466 	if (trace > 1)
467 		trace_prt(1, "  ldap_match: found: %s\n", *ldap_line);
468 
469 	return (__NSW_SUCCESS);
470 }
471 
472 int
473 loadmaster_ldap(char *mapname, char *defopts, char **stack, char ***stkptr)
474 {
475 	char searchfilter[LDAP_FILT_MAXSIZ];
476 	int res;
477 	ns_ldap_result_t *result = NULL;
478 	ns_ldap_error_t *errp = NULL;
479 	struct loadmaster_cbdata master_cbdata;
480 
481 	if (trace > 1)
482 		trace_prt(1, "loadmaster_ldap called\n");
483 
484 	master_cbdata.ptr1 = defopts;
485 	master_cbdata.ptr2 = stack;
486 	master_cbdata.ptr3 = stkptr;
487 
488 	/* filter gets all the entries for the specified mapname */
489 	(void) sprintf(searchfilter, defaultFilter, "*");
490 
491 	if (trace > 1)
492 		trace_prt(1, "loadmaster_ldap: Requesting list for %s in %s\n",
493 		    searchfilter, mapname);
494 
495 	res = __ns_ldap_list(mapname, searchfilter, NULL, NULL, NULL,
496 	    0, &result, &errp, mastermap_callback_ldap,
497 	    (void *) &master_cbdata);
498 
499 	if (trace > 1)
500 		trace_prt(1,
501 		    "loadmaster_ldap: __ns_ldap_list just returned: %d\n",
502 		    res);
503 
504 	if (res != NS_LDAP_SUCCESS) {
505 		if (errp) {
506 			char errstr[MAXERROR];
507 			if (verbose) {
508 				(void) sprintf(errstr, gettext(
509 				    "ldap server can't list map,"
510 				    "'%s': '%s' - '%d'."),
511 				    mapname, errp->message, errp->status);
512 				syslog(LOG_ERR, errstr);
513 			}
514 			__ns_ldap_freeError(&errp);
515 		} else {
516 			if (verbose) {
517 				char *errmsg;
518 				__ns_ldap_err2str(res, &errmsg);
519 				syslog(LOG_ERR, errmsg);
520 			}
521 		}
522 		if (result)
523 			__ns_ldap_freeResult(&result);
524 		return (ldap_err(res));
525 	}
526 
527 	if (trace > 1)
528 		trace_prt(1,
529 		    "loadmaster_ldap: calling __ns_ldap_freeResult...\n");
530 
531 	__ns_ldap_freeResult(&result);
532 
533 	if (trace > 1)
534 		trace_prt(1,
535 		    "loadmaster_ldap: about to return __NSW_SUCCESS...\n");
536 
537 	return (__NSW_SUCCESS);
538 }
539 
540 int
541 loaddirect_ldap(char *nsmap, char *localmap, char *opts,
542 char **stack, char ***stkptr)
543 {
544 	char searchfilter[LDAP_FILT_MAXSIZ];
545 	int res;
546 	ns_ldap_result_t *result = NULL;
547 	ns_ldap_error_t *errp = NULL;
548 	struct loaddirect_cbdata direct_cbdata;
549 
550 	if (trace > 1) {
551 		trace_prt(1, "loaddirect_ldap called\n");
552 	}
553 
554 	direct_cbdata.ptr1 = opts;
555 	direct_cbdata.ptr2 = localmap;
556 	direct_cbdata.ptr3 = stack;
557 	direct_cbdata.ptr4 = stkptr;
558 
559 	/* filter gets all the entries for the specified mapname */
560 	(void) sprintf(searchfilter, defaultFilter, "*");
561 
562 	if (trace > 1)
563 		trace_prt(1, "loaddirect_ldap: Requesting list for %s in %s\n",
564 		    searchfilter, nsmap);
565 
566 	res = __ns_ldap_list(nsmap, searchfilter, NULL, NULL,
567 	    NULL, 0, &result, &errp,
568 	    directmap_callback, (void *) &direct_cbdata);
569 
570 
571 	if (res != NS_LDAP_SUCCESS) {
572 		if (errp) {
573 			char errstr[MAXERROR];
574 			if (verbose) {
575 				(void) sprintf(errstr,
576 				    gettext("ldap server can't list map,"
577 				    " '%s': '%s' - '%d'."),
578 				    nsmap, errp->message, errp->status);
579 				syslog(LOG_ERR, errstr);
580 			}
581 			__ns_ldap_freeError(&errp);
582 		} else {
583 			if (verbose) {
584 				char *errmsg;
585 				__ns_ldap_err2str(res, &errmsg);
586 				syslog(LOG_ERR, errmsg);
587 			}
588 		}
589 		if (result)
590 			__ns_ldap_freeResult(&result);
591 		return (ldap_err(res));
592 	}
593 
594 	__ns_ldap_freeResult(&result);
595 	return (__NSW_SUCCESS);
596 }
597 
598 static int
599 ldap_err(int err)
600 {
601 	if (trace > 1)
602 		trace_prt(1, "ldap_err called\n");
603 
604 	switch (err) {
605 
606 	case NS_LDAP_SUCCESS:
607 		return (__NSW_SUCCESS);
608 
609 	case NS_LDAP_NOTFOUND:
610 		return (__NSW_NOTFOUND);
611 
612 	case NS_LDAP_PARTIAL:
613 		return (__NSW_TRYAGAIN);
614 
615 	default:
616 		return (__NSW_UNAVAIL);
617 	}
618 }
619 
620 static int
621 mastermap_callback_ldap(ns_ldap_entry_t *entry, void *udata)
622 {
623 	char *key, *contents, *pmap, *opts;
624 	char dir[LINESZ], map[LINESZ], qbuff[LINESZ];
625 	char cont_temp[LINESZ], key_temp[LINESZ];
626 	int  key_len, contents_len;
627 	struct loadmaster_cbdata *temp = (struct loadmaster_cbdata *)udata;
628 	char *defopts = temp->ptr1;
629 	char **stack = temp->ptr2;
630 	char ***stkptr = temp->ptr3;
631 	int i;
632 
633 	if (trace > 1) {
634 		trace_prt(1, "mastermap_callback_ldap called\n");
635 		trace_prt(1, "mastermap_callback_ldap: entry=%x\n", entry);
636 		if (entry) {
637 			trace_prt(1,
638 			"mastermap_callback_ldap: entry->attr_count=[ %d ]\n",
639 			    entry->attr_count);
640 		}
641 	}
642 
643 	/*
644 	 * For the current entry, obtain the values of the cn and the
645 	 * nisMapEntry attributes and the length of each value (cn=key,
646 	 * nisMapEntry=contents).
647 	 * We skip the description.  Even though LDAP allows for multiple
648 	 * values per attribute, we take only the 1st value for each
649 	 * attribute because the automount data is organized as such.
650 	 */
651 	key_len = 0;
652 	contents_len = 0;
653 	key = NULL;
654 	contents = NULL;
655 	for (i = 0; i < entry->attr_count; i++) {
656 		ns_ldap_attr_t *attr;
657 
658 		attr = entry->attr_pair[i];
659 		if (trace > 1) {
660 			trace_prt(1,
661 			"mastermap_callback_ldap: attr[%d]: %s=%s\n",
662 			    i, attr->attrname, attr->attrvalue[0]);
663 		}
664 		if (strcasecmp(attr->attrname, automountInformation) == 0) {
665 			if (encode)
666 				(void) strncpy(cont_temp,
667 				    tounix_str(attr->attrvalue[0]), LINESZ);
668 			else
669 				(void) strncpy(cont_temp, attr->attrvalue[0],
670 				    LINESZ);
671 			contents = cont_temp;
672 			contents_len = strlen(contents);
673 			if (trace > 1) {
674 				trace_prt(1,
675 				    "mastermap_callback_ldap: contents=[ %s ],"
676 				    " contents_len=[ %d ]\n",
677 				    contents, contents_len);
678 			}
679 		}
680 		if (strcasecmp(attr->attrname, automountKey) == 0) {
681 			if (encode)
682 				(void) strncpy(key_temp,
683 				    tounix_str(attr->attrvalue[0]), LINESZ);
684 			else
685 				(void) strncpy(key_temp, attr->attrvalue[0],
686 				    LINESZ);
687 			key = key_temp;
688 			key_len = strlen(key);
689 			if (trace > 1) {
690 				trace_prt(1,
691 				    "mastermap_callback_ldap: key=[ %s ],"
692 				    " key_len=[ %d ]\n",
693 				    key, key_len);
694 			}
695 		}
696 	}
697 
698 	if (key_len >= LINESZ || contents_len >= LINESZ)
699 		return (0);
700 	if (key_len < 2 || contents_len < 2)
701 		return (0);
702 
703 	while (isspace(*contents))
704 		contents++;
705 	if (contents == NULL)
706 		return (0);
707 	if (isspace(*key) || *key == '#')
708 		return (0);
709 
710 	(void) strncpy(dir, key, key_len);
711 	dir[key_len] = '\0';
712 	if (trace > 1)
713 		trace_prt(1, "mastermap_callback_ldap: dir= [ %s ]\n", dir);
714 	for (i = 0; i < LINESZ; i++)
715 		qbuff[i] = ' ';
716 	if (macro_expand("", dir, qbuff, sizeof (dir))) {
717 		syslog(LOG_ERR,
718 		    "%s in ldap server map: entry too long (max %d chars)",
719 		    dir, sizeof (dir) - 1);
720 		return (0);
721 	}
722 	(void) strncpy(map, contents, contents_len);
723 	map[contents_len] = '\0';
724 	if (trace > 1)
725 		trace_prt(1, "mastermap_callback_ldap: map= [ %s ]\n", map);
726 	if (macro_expand("", map, qbuff, sizeof (map))) {
727 		syslog(LOG_ERR,
728 		    "%s in ldap server map: entry too long (max %d chars)",
729 		    map, sizeof (map) - 1);
730 		return (0);
731 	}
732 	pmap = map;
733 	while (*pmap && isspace(*pmap))
734 		pmap++;		/* skip blanks in front of map */
735 	opts = pmap;
736 	while (*opts && !isspace(*opts))
737 		opts++;
738 	if (*opts) {
739 		*opts++ = '\0';
740 		while (*opts && isspace(*opts))
741 			opts++;
742 		if (*opts == '-')
743 			opts++;
744 			else
745 			opts = defopts;
746 	}
747 	/*
748 	 * Check for no embedded blanks.
749 	 */
750 	if (strcspn(opts, " 	") == strlen(opts)) {
751 		if (trace > 1)
752 			trace_prt(1,
753 			"mastermap_callback_ldap: dir=[ %s ], pmap=[ %s ]\n",
754 			    dir, pmap);
755 		dirinit(dir, pmap, opts, 0, stack, stkptr);
756 	} else {
757 		char *dn = NULL;
758 
759 		/* get the value for the dn */
760 		for (i = 0; i < entry->attr_count; i++) {
761 			ns_ldap_attr_t *attr;
762 
763 			attr = entry->attr_pair[i];
764 			if (strcasecmp(attr->attrname, "dn")
765 			    == 0) {
766 				dn = attr->attrvalue[0];
767 				break;
768 			}
769 		}
770 		pr_msg(
771 		    "Warning: invalid entry for %s in ldap server"
772 		    " dn: %s ignored.\n",
773 		    dir, dn);
774 	}
775 	if (trace > 1)
776 		trace_prt(1, "mastermap_callback_ldap exiting...\n");
777 	return (0);
778 }
779 
780 static int
781 directmap_callback(ns_ldap_entry_t *entry, void *udata)
782 {
783 	char *key;
784 	char dir[256];
785 	int  key_len;
786 	struct loaddirect_cbdata *temp = (struct loaddirect_cbdata *)udata;
787 	char *opts = temp->ptr1;
788 	char *localmap = temp->ptr2;
789 	char **stack = temp->ptr3;
790 	char ***stkptr = temp->ptr4;
791 	int i;
792 
793 	/*
794 	 * For the current entry, obtain the value and length of the cn i.e.
795 	 * the contents of key and its key length.
796 	 */
797 	key_len = 0;
798 	key = NULL;
799 	for (i = 0; i < entry->attr_count; i++) {
800 		ns_ldap_attr_t *attr;
801 
802 		attr = entry->attr_pair[i];
803 		if (strcasecmp(attr->attrname, automountKey) == 0) {
804 			if (encode)
805 				key = tounix_str(attr->attrvalue[0]);
806 			else
807 				key = attr->attrvalue[0];
808 			key_len = strlen(key);
809 			break;
810 		}
811 	}
812 
813 	if (key_len >= 100 || key_len < 2)
814 		return (0);
815 
816 	if (isspace(*key) || *key == '#')
817 		return (0);
818 	(void) strncpy(dir, key, key_len);
819 	dir[key_len] = '\0';
820 
821 	dirinit(dir, localmap, opts, 1, stack, stkptr);
822 
823 	return (0);
824 }
825 
826 int
827 getmapkeys_ldap(char *nsmap, struct dir_entry **list, int *error,
828 int *cache_time, char **stack, char ***stkptr)
829 {
830 	char searchfilter[LDAP_FILT_MAXSIZ];
831 	int res;
832 	ns_ldap_result_t *result = NULL;
833 	ns_ldap_error_t *errp = NULL;
834 	struct dir_cbdata readdir_cbdata;
835 
836 #ifdef lint
837 	stack = stack;
838 	stkptr = stkptr;
839 #endif /* lint */
840 
841 	if (trace > 1)
842 		trace_prt(1, "getmapkeys_ldap called\n");
843 
844 	*cache_time = RDDIR_CACHE_TIME;
845 	*error = 0;
846 	readdir_cbdata.list = list;
847 	readdir_cbdata.last = NULL;
848 
849 	/* filter gets all the entries for the specified mapname */
850 	(void) sprintf(searchfilter, defaultFilter, "*");
851 
852 	if (trace > 1)
853 		trace_prt(1, "getmapkeys_ldap: Requesting list for %s in %s\n",
854 		    searchfilter, nsmap);
855 
856 	res = __ns_ldap_list(nsmap, searchfilter, NULL, NULL, NULL, 0,
857 	    &result, &errp, readdir_callback, (void *) &readdir_cbdata);
858 
859 	if (trace > 1)
860 		trace_prt(1, "  getmapkeys_ldap: __ns_ldap_list returned %d\n",
861 		    res);
862 
863 	if (readdir_cbdata.error)
864 		*error = readdir_cbdata.error;
865 
866 	if (res != NS_LDAP_SUCCESS && res != NS_LDAP_NOTFOUND) {
867 		if (errp) {
868 			if (verbose) {
869 				char errstr[MAXERROR];
870 				(void) sprintf(errstr, gettext(
871 				    "ldap server can't list map,"
872 				    " '%s': '%s' - '%d'."),
873 				    nsmap, errp->message, errp->status);
874 				syslog(LOG_ERR, errstr);
875 			}
876 			__ns_ldap_freeError(&errp);
877 		} else {
878 			if (verbose) {
879 				char *errmsg;
880 				__ns_ldap_err2str(res, &errmsg);
881 				syslog(LOG_ERR, errmsg);
882 			}
883 		}
884 		if (result)
885 			__ns_ldap_freeResult(&result);
886 		if (*error == 0)
887 			*error = ECOMM;
888 		return (ldap_err(res));
889 	}
890 	if (result)
891 		__ns_ldap_freeResult(&result);
892 
893 	return (__NSW_SUCCESS);
894 }
895 
896 static int
897 readdir_callback(const ns_ldap_entry_t *entry, const void *udata)
898 {
899 	char *key;
900 	int  key_len;
901 	struct dir_cbdata *temp = (struct dir_cbdata *)udata;
902 	struct dir_entry **list = temp->list;
903 	struct dir_entry *last = temp->last;
904 	int i;
905 
906 	if (trace > 1)
907 		trace_prt(1, "readdir_callback called\n");
908 	/*
909 	 * For the current entry, obtain the value and length of the cn i.e. the
910 	 * contents of key and its key length.
911 	 */
912 	key_len = 0;
913 	key = NULL;
914 
915 	if (trace > 1)
916 		trace_prt(1, "readdir_callback: entry->attr_count=[ %d ]\n",
917 		    entry->attr_count);
918 
919 	for (i = 0; i < entry->attr_count; i++) {
920 		ns_ldap_attr_t *attr;
921 
922 		attr = entry->attr_pair[i];
923 
924 		if (trace > 1)
925 			trace_prt(1,
926 			"readdir_callback: attr->attrname=[ %s ]\n",
927 			    attr->attrname);
928 
929 		if (strcasecmp(attr->attrname, automountKey) == 0) {
930 			if (encode)
931 				key = tounix_str(attr->attrvalue[0]);
932 			else
933 				key = attr->attrvalue[0];
934 			key_len = strlen(key);
935 
936 			if (trace > 1)
937 				trace_prt(1,
938 			"readdir_callback: key=[ %s ], key_len=[ %d ]\n",
939 				    key, key_len);
940 
941 			break;
942 		}
943 	}
944 
945 	if (key_len >= 100 || key_len < 2)
946 		return (0);
947 
948 	if (isspace(*key) || *key == '#')
949 		return (0);
950 
951 	/*
952 	 * Wildcard entry should be ignored - following entries should continue
953 	 * to be read to corroborate with the way we search for entries in
954 	 * LDAP, i.e., first for an exact key match and then a wildcard
955 	 * if there's no exact key match.
956 	 */
957 	if (key[0] == '*' && key[1] == '\0')
958 		return (0);
959 
960 	if (add_dir_entry(key, list, &last)) {
961 		temp->error = ENOMEM;
962 		return (1);
963 	}
964 
965 	temp->last = last;
966 	temp->error = 0;
967 
968 	if (trace > 1)
969 		trace_prt(1, "readdir_callback returning 0...\n");
970 
971 	return (0);
972 }
973 
974 /*
975  * Puts CAPCHAR in front of uppercase characters or surrounds a set of
976  * contiguous uppercase characters with CAPCHARS and square brackets.
977  *
978  * For example (assuming CAPCHAR = '%'):
979  *
980  * if str = Abc, it returns %Abc
981  * if str = ABc, it returns %[AB]c
982  * if str = AbC, it returns %Ab%C
983  *
984  */
985 static char *
986 tosunds_str(char *str)
987 {
988 	static char buf[BUFSIZ];
989 	int i, j, er = FALSE;
990 #ifdef NEWCAP
991 	int openBracket = FALSE, closeBracket = FALSE;
992 #endif
993 
994 	(void) memset(buf, 0, BUFSIZ);
995 
996 	j = 0;
997 	for (i = 0; i < strlen(str); i++) {
998 		/* Check the current element */
999 		if (isupper(str[i])) {
1000 #ifdef NEWCAP
1001 			/* check the next element */
1002 			if (isupper(str[i+1])) {
1003 				if (openBracket == FALSE) {
1004 					openBracket = TRUE;
1005 					buf[j] = CAPCHAR;
1006 					buf[j+1] = '[';
1007 					j += 2;
1008 				}
1009 			} else {
1010 				if (openBracket == FALSE) {
1011 					buf[j] = CAPCHAR;
1012 					j++;
1013 				} else {
1014 					openBracket = FALSE;
1015 					closeBracket = TRUE;
1016 				}
1017 			}
1018 #else
1019 			buf[j++] = CAPCHAR;
1020 #endif
1021 		}
1022 		buf[j] = str[i];
1023 		j++;
1024 
1025 #ifdef NEWCAP
1026 		if (closeBracket == TRUE) {
1027 			closeBracket = FALSE;
1028 			buf[j] = ']';
1029 			j++;
1030 		}
1031 #endif
1032 		if (j >= BUFSIZ) {
1033 			er = TRUE;
1034 			break;
1035 		}
1036 	}
1037 
1038 	if (er) {
1039 		syslog(LOG_ERR, "Buffer size exceeded.");
1040 		(void) memset(buf, 0, BUFSIZ);
1041 	} else
1042 		buf[j] = '\0';
1043 
1044 	return (buf);
1045 
1046 }
1047 
1048 /*
1049  * Reverses what tosunds_str() did
1050  */
1051 static char *
1052 tounix_str(char *str)
1053 {
1054 	static char buf[BUFSIZ];
1055 	int i, j;
1056 	int openBracket = FALSE;
1057 
1058 	(void) memset(buf, 0, BUFSIZ);
1059 	j = 0;
1060 
1061 	for (i = 0; i < strlen(str); i++) {
1062 		if (str[i] == '%') {
1063 			if (isupper(str[i+1])) {
1064 				i += 1;
1065 			} else if ((str[i+1] == '[') && (isupper(str[i+2]))) {
1066 				i += 2;
1067 				openBracket = TRUE;
1068 			}
1069 		} else if (str[i] == ']') {
1070 			if ((isupper(str[i-1])) && (openBracket == TRUE))
1071 				i += 1;
1072 			openBracket = FALSE;
1073 		}
1074 		buf[j] = str[i];
1075 		j++;
1076 	}
1077 	return (buf);
1078 }
1079