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