xref: /titanic_51/usr/src/cmd/fs.d/autofs/ns_ldap.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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 2004 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 loadmaster_ldap(char *mapname, char *defopts, char **stack, char ***stkptr)
476 {
477 	char searchfilter[LDAP_FILT_MAXSIZ];
478 	int res;
479 	ns_ldap_result_t *result = NULL;
480 	ns_ldap_error_t *errp = NULL;
481 	struct loadmaster_cbdata master_cbdata;
482 
483 	if (trace > 1)
484 		trace_prt(1, "loadmaster_ldap called\n");
485 
486 	master_cbdata.ptr1 = defopts;
487 	master_cbdata.ptr2 = stack;
488 	master_cbdata.ptr3 = stkptr;
489 
490 	/* filter gets all the entries for the specified mapname */
491 	(void) sprintf(searchfilter, defaultFilter, "*");
492 
493 	if (trace > 1)
494 		trace_prt(1, "loadmaster_ldap: Requesting list for %s in %s\n",
495 		    searchfilter, mapname);
496 
497 	res = __ns_ldap_list(mapname, searchfilter, NULL, NULL, NULL,
498 		0, &result, &errp, mastermap_callback_ldap,
499 		(void *) &master_cbdata);
500 
501 	if (trace > 1)
502 		trace_prt(1,
503 			"loadmaster_ldap: __ns_ldap_list just returned: %d\n",
504 			res);
505 
506 	if (res != NS_LDAP_SUCCESS) {
507 		if (errp) {
508 			char errstr[MAXERROR];
509 			if (verbose) {
510 				(void) sprintf(errstr, gettext(
511 					"ldap server can't list map,"
512 					"'%s': '%s' - '%d'."),
513 					mapname, errp->message, errp->status);
514 				syslog(LOG_ERR, errstr);
515 			}
516 			__ns_ldap_freeError(&errp);
517 		} else {
518 			if (verbose) {
519 				char *errmsg;
520 				__ns_ldap_err2str(res, &errmsg);
521 				syslog(LOG_ERR, errmsg);
522 			}
523 		}
524 		if (result)
525 			__ns_ldap_freeResult(&result);
526 		return (ldap_err(res));
527 	}
528 
529 	if (trace > 1)
530 		trace_prt(1,
531 			"loadmaster_ldap: calling __ns_ldap_freeResult...\n");
532 
533 	__ns_ldap_freeResult(&result);
534 
535 	if (trace > 1)
536 		trace_prt(1,
537 			"loadmaster_ldap: about to return __NSW_SUCCESS...\n");
538 
539 	return (__NSW_SUCCESS);
540 }
541 
542 loaddirect_ldap(char *nsmap, char *localmap, char *opts,
543 char **stack, char ***stkptr)
544 {
545 	char searchfilter[LDAP_FILT_MAXSIZ];
546 	int res;
547 	ns_ldap_result_t *result = NULL;
548 	ns_ldap_error_t *errp = NULL;
549 	struct loaddirect_cbdata direct_cbdata;
550 
551 	if (trace > 1) {
552 		trace_prt(1, "loaddirect_ldap called\n");
553 	}
554 
555 	direct_cbdata.ptr1 = opts;
556 	direct_cbdata.ptr2 = localmap;
557 	direct_cbdata.ptr3 = stack;
558 	direct_cbdata.ptr4 = stkptr;
559 
560 	/* filter gets all the entries for the specified mapname */
561 	(void) sprintf(searchfilter, defaultFilter, "*");
562 
563 	if (trace > 1)
564 		trace_prt(1, "loaddirect_ldap: Requesting list for %s in %s\n",
565 		    searchfilter, nsmap);
566 
567 	res = __ns_ldap_list(nsmap, searchfilter, NULL, NULL,
568 	    NULL, 0, &result, &errp,
569 	    directmap_callback, (void *) &direct_cbdata);
570 
571 
572 	if (res != NS_LDAP_SUCCESS) {
573 		if (errp) {
574 			char errstr[MAXERROR];
575 			if (verbose) {
576 				(void) sprintf(errstr,
577 					gettext("ldap server can't list map,"
578 					" '%s': '%s' - '%d'."),
579 					nsmap, errp->message, errp->status);
580 				syslog(LOG_ERR, errstr);
581 			}
582 			__ns_ldap_freeError(&errp);
583 		} else {
584 			if (verbose) {
585 				char *errmsg;
586 				__ns_ldap_err2str(res, &errmsg);
587 				syslog(LOG_ERR, errmsg);
588 			}
589 		}
590 		if (result)
591 			__ns_ldap_freeResult(&result);
592 		return (ldap_err(res));
593 	}
594 
595 	__ns_ldap_freeResult(&result);
596 	return (__NSW_SUCCESS);
597 }
598 
599 static int
600 ldap_err(int err)
601 {
602 	if (trace > 1)
603 		trace_prt(1, "ldap_err called\n");
604 
605 	switch (err) {
606 
607 	case NS_LDAP_SUCCESS:
608 		return (__NSW_SUCCESS);
609 
610 	case NS_LDAP_NOTFOUND:
611 		return (__NSW_NOTFOUND);
612 
613 	case NS_LDAP_PARTIAL:
614 		return (__NSW_TRYAGAIN);
615 
616 	default:
617 		return (__NSW_UNAVAIL);
618 	}
619 }
620 
621 static int
622 mastermap_callback_ldap(ns_ldap_entry_t *entry, void *udata)
623 {
624 	char *key, *contents, *pmap, *opts;
625 	char dir[LINESZ], map[LINESZ], qbuff[LINESZ];
626 	char cont_temp[LINESZ], key_temp[LINESZ];
627 	int  key_len, contents_len;
628 	struct loadmaster_cbdata *temp = (struct loadmaster_cbdata *)udata;
629 	char *defopts = temp->ptr1;
630 	char **stack = temp->ptr2;
631 	char ***stkptr = temp->ptr3;
632 	int i;
633 
634 	if (trace > 1) {
635 		trace_prt(1, "mastermap_callback_ldap called\n");
636 		trace_prt(1, "mastermap_callback_ldap: entry=%x\n", entry);
637 		if (entry) {
638 			trace_prt(1,
639 			"mastermap_callback_ldap: entry->attr_count=[ %d ]\n",
640 			    entry->attr_count);
641 		}
642 	}
643 
644 	/*
645 	 * For the current entry, obtain the values of the cn and the
646 	 * nisMapEntry attributes and the length of each value (cn=key,
647 	 * nisMapEntry=contents).
648 	 * We skip the description.  Even though LDAP allows for multiple
649 	 * values per attribute, we take only the 1st value for each
650 	 * attribute because the automount data is organized as such
651 	 * (same as NIS+).
652 	 */
653 	key_len = 0;
654 	contents_len = 0;
655 	key = NULL;
656 	contents = NULL;
657 	for (i = 0; i < entry->attr_count; i++) {
658 		ns_ldap_attr_t *attr;
659 
660 		attr = entry->attr_pair[i];
661 		if (trace > 1) {
662 			trace_prt(1,
663 			"mastermap_callback_ldap: attr[%d]: %s=%s\n",
664 			    i, attr->attrname, attr->attrvalue[0]);
665 		}
666 		if (strcasecmp(attr->attrname, automountInformation) == 0) {
667 			if (encode)
668 				(void) strncpy(cont_temp,
669 					tounix_str(attr->attrvalue[0]), LINESZ);
670 			else
671 				(void) strncpy(cont_temp, attr->attrvalue[0],
672 					LINESZ);
673 			contents = cont_temp;
674 			contents_len = strlen(contents);
675 			if (trace > 1) {
676 				trace_prt(1,
677 	"mastermap_callback_ldap: contents=[ %s ], contents_len=[ %d ]\n",
678 				contents, contents_len);
679 			}
680 		}
681 		if (strcasecmp(attr->attrname, automountKey) == 0) {
682 			if (encode)
683 				(void) strncpy(key_temp,
684 					tounix_str(attr->attrvalue[0]), LINESZ);
685 			else
686 				(void) strncpy(key_temp, attr->attrvalue[0],
687 					LINESZ);
688 			key = key_temp;
689 			key_len = strlen(key);
690 			if (trace > 1) {
691 				trace_prt(1,
692 			"mastermap_callback_ldap: key=[ %s ], 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 dn: %s ignored.\n",
772 		    dir, dn);
773 	}
774 	if (trace > 1)
775 		trace_prt(1, "mastermap_callback_ldap exiting...\n");
776 	return (0);
777 }
778 
779 static int
780 directmap_callback(ns_ldap_entry_t *entry, void *udata)
781 {
782 	char *key;
783 	char dir[256];
784 	int  key_len;
785 	struct loaddirect_cbdata *temp = (struct loaddirect_cbdata *)udata;
786 	char *opts = temp->ptr1;
787 	char *localmap = temp->ptr2;
788 	char **stack = temp->ptr3;
789 	char ***stkptr = temp->ptr4;
790 	int i;
791 
792 	/*
793 	 * For the current entry, obtain the value and length of the cn i.e.
794 	 * the contents of key and its key length.
795 	 */
796 	key_len = 0;
797 	key = NULL;
798 	for (i = 0; i < entry->attr_count; i++) {
799 		ns_ldap_attr_t *attr;
800 
801 		attr = entry->attr_pair[i];
802 		if (strcasecmp(attr->attrname, automountKey) == 0) {
803 			if (encode)
804 				key = tounix_str(attr->attrvalue[0]);
805 			else
806 				key = attr->attrvalue[0];
807 			key_len = strlen(key);
808 			break;
809 		}
810 	}
811 
812 	if (key_len >= 100 || key_len < 2)
813 		return (0);
814 
815 	if (isspace(*key) || *key == '#')
816 		return (0);
817 	(void) strncpy(dir, key, key_len);
818 	dir[key_len] = '\0';
819 
820 	dirinit(dir, localmap, opts, 1, stack, stkptr);
821 
822 	return (0);
823 }
824 
825 int
826 getmapkeys_ldap(char *nsmap, struct dir_entry **list, int *error,
827 int *cache_time, char **stack, char ***stkptr)
828 {
829 	char searchfilter[LDAP_FILT_MAXSIZ];
830 	int res;
831 	ns_ldap_result_t *result = NULL;
832 	ns_ldap_error_t *errp = NULL;
833 	struct dir_cbdata readdir_cbdata;
834 
835 #ifdef lint
836 	stack = stack;
837 	stkptr = stkptr;
838 #endif /* lint */
839 
840 	if (trace > 1)
841 		trace_prt(1, "getmapkeys_ldap called\n");
842 
843 	*cache_time = RDDIR_CACHE_TIME;
844 	*error = 0;
845 	readdir_cbdata.list = list;
846 	readdir_cbdata.last = NULL;
847 
848 	/* filter gets all the entries for the specified mapname */
849 	(void) sprintf(searchfilter, defaultFilter, "*");
850 
851 	if (trace > 1)
852 		trace_prt(1, "getmapkeys_ldap: Requesting list for %s in %s\n",
853 		    searchfilter, nsmap);
854 
855 	res = __ns_ldap_list(nsmap, searchfilter, NULL, NULL, NULL, 0,
856 		&result, &errp, readdir_callback, (void *) &readdir_cbdata);
857 
858 	if (trace > 1)
859 		trace_prt(1, "  getmapkeys_ldap: __ns_ldap_list returned %d\n",
860 			res);
861 
862 	if (readdir_cbdata.error)
863 		*error = readdir_cbdata.error;
864 
865 	if (res != NS_LDAP_SUCCESS && res != NS_LDAP_NOTFOUND) {
866 		if (errp) {
867 			if (verbose) {
868 				char errstr[MAXERROR];
869 				(void) sprintf(errstr, gettext(
870 					"ldap server can't list map,"
871 					" '%s': '%s' - '%d'."),
872 					nsmap, errp->message, errp->status);
873 				syslog(LOG_ERR, errstr);
874 			}
875 			__ns_ldap_freeError(&errp);
876 		} else {
877 			if (verbose) {
878 				char *errmsg;
879 				__ns_ldap_err2str(res, &errmsg);
880 				syslog(LOG_ERR, errmsg);
881 			}
882 		}
883 		if (result)
884 			__ns_ldap_freeResult(&result);
885 		if (*error == 0)
886 			*error = ECOMM;
887 		return (ldap_err(res));
888 	}
889 	if (result)
890 		__ns_ldap_freeResult(&result);
891 
892 	return (__NSW_SUCCESS);
893 }
894 
895 static int
896 readdir_callback(const ns_ldap_entry_t *entry, const void *udata)
897 {
898 	char *key;
899 	int  key_len;
900 	struct dir_cbdata *temp = (struct dir_cbdata *)udata;
901 	struct dir_entry **list = temp->list;
902 	struct dir_entry *last = temp->last;
903 	int i;
904 
905 	if (trace > 1)
906 		trace_prt(1, "readdir_callback called\n");
907 	/*
908 	 * For the current entry, obtain the value and length of the cn i.e. the
909 	 * contents of key and its key length.
910 	 */
911 	key_len = 0;
912 	key = NULL;
913 
914 	if (trace > 1)
915 		trace_prt(1, "readdir_callback: entry->attr_count=[ %d ]\n",
916 		    entry->attr_count);
917 
918 	for (i = 0; i < entry->attr_count; i++) {
919 		ns_ldap_attr_t *attr;
920 
921 		attr = entry->attr_pair[i];
922 
923 		if (trace > 1)
924 			trace_prt(1,
925 			"readdir_callback: attr->attrname=[ %s ]\n",
926 			    attr->attrname);
927 
928 		if (strcasecmp(attr->attrname, automountKey) == 0) {
929 			if (encode)
930 				key = tounix_str(attr->attrvalue[0]);
931 			else
932 				key = attr->attrvalue[0];
933 			key_len = strlen(key);
934 
935 			if (trace > 1)
936 				trace_prt(1,
937 			"readdir_callback: key=[ %s ], key_len=[ %d ]\n",
938 				    key, key_len);
939 
940 			break;
941 		}
942 	}
943 
944 	if (key_len >= 100 || key_len < 2)
945 		return (0);
946 
947 	if (isspace(*key) || *key == '#')
948 		return (0);
949 
950 	/*
951 	 * Wildcard entry should be ignored - following entries should continue
952 	 * to be read to corroborate with the way we search for entries in
953 	 * LDAP, i.e., first for an exact key match and then a wildcard
954 	 * if there's no exact key match.
955 	 */
956 	if (key[0] == '*' && key[1] == '\0')
957 		return (0);
958 
959 	if (add_dir_entry(key, list, &last)) {
960 		temp->error = ENOMEM;
961 		return (1);
962 	}
963 
964 	temp->last = last;
965 	temp->error = 0;
966 
967 	if (trace > 1)
968 		trace_prt(1, "readdir_callback returning 0...\n");
969 
970 	return (0);
971 }
972 
973 /*
974  * Puts CAPCHAR in front of uppercase characters or surrounds a set of
975  * contiguous uppercase characters with CAPCHARS and square brackets.
976  *
977  * For example (assuming CAPCHAR = '%'):
978  *
979  * if str = Abc, it returns %Abc
980  * if str = ABc, it returns %[AB]c
981  * if str = AbC, it returns %Ab%C
982  *
983  */
984 static char *
985 tosunds_str(char *str)
986 {
987 	static char buf[BUFSIZ];
988 	int i, j, er = FALSE;
989 #ifdef NEWCAP
990 	int openBracket = FALSE, closeBracket = FALSE;
991 #endif
992 
993 	(void) memset(buf, 0, BUFSIZ);
994 
995 	j = 0;
996 	for (i = 0; i < strlen(str); i++) {
997 		/* Check the current element */
998 		if (isupper(str[i])) {
999 #ifdef NEWCAP
1000 			/* check the next element */
1001 			if (isupper(str[i+1])) {
1002 				if (openBracket == FALSE) {
1003 					openBracket = TRUE;
1004 					buf[j] = CAPCHAR;
1005 					buf[j+1] = '[';
1006 					j += 2;
1007 				}
1008 			} else {
1009 				if (openBracket == FALSE) {
1010 					buf[j] = CAPCHAR;
1011 					j++;
1012 				} else {
1013 					openBracket = FALSE;
1014 					closeBracket = TRUE;
1015 				}
1016 			}
1017 #else
1018 			buf[j++] = CAPCHAR;
1019 #endif
1020 		}
1021 		buf[j] = str[i];
1022 		j++;
1023 
1024 #ifdef NEWCAP
1025 		if (closeBracket == TRUE) {
1026 			closeBracket = FALSE;
1027 			buf[j] = ']';
1028 			j++;
1029 		}
1030 #endif
1031 		if (j >= BUFSIZ) {
1032 			er = TRUE;
1033 			break;
1034 		}
1035 	}
1036 
1037 	if (er) {
1038 		syslog(LOG_ERR, "Buffer size exceeded.");
1039 		(void) memset(buf, 0, BUFSIZ);
1040 	} else
1041 		buf[j] = '\0';
1042 
1043 	return (buf);
1044 
1045 }
1046 
1047 /*
1048  * Reverses what tosunds_str() did
1049  */
1050 static char *
1051 tounix_str(char *str)
1052 {
1053 	static char buf[BUFSIZ];
1054 	int i, j;
1055 	int openBracket = FALSE;
1056 
1057 	(void) memset(buf, 0, BUFSIZ);
1058 	j = 0;
1059 
1060 	for (i = 0; i < strlen(str); i++) {
1061 		if (str[i] == '%') {
1062 			if (isupper(str[i+1])) {
1063 				i += 1;
1064 			} else if ((str[i+1] == '[') && (isupper(str[i+2]))) {
1065 				i += 2;
1066 				openBracket = TRUE;
1067 			}
1068 		} else if (str[i] == ']') {
1069 			if ((isupper(str[i-1])) && (openBracket == TRUE))
1070 				i += 1;
1071 			openBracket = FALSE;
1072 		}
1073 		buf[j] = str[i];
1074 		j++;
1075 	}
1076 	return (buf);
1077 }
1078