root/usr/src/cmd/fs.d/autofs/ns_ldap.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 *      ns_ldap.c
 *
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include <ctype.h>
#include <nsswitch.h>
#include <sys/param.h>
#include <sys/types.h>
#include <rpc/rpc.h>
#include <rpcsvc/nfs_prot.h>
#include <sys/errno.h>
#include <libintl.h>
#include "automount.h"
#include "../../../lib/libsldap/common/ns_sldap.h"

/*
 * LDAP schema used for automounter:
 *
 * automountMapName: mapname i.e. auto_home, etc.
 * automountKey: contains the key i.e. the mount point
 * automountInformation: contains the mount options and remote mount location
 * description: an optional description (not used by automounter)
 *
 * For example, if auto_direct has the following line of data:
 *
 *              /work -rw,intr,nosuid,noquota hosta:/export/work
 *
 * Then this would map to the the following LDAP entry:
 *
 *      dn: automountKey=/work,automountMapName=auto_direct,...
 *      automountKey: /work
 *      automountInformation: -rw,intr,nosuid,noquota hosta:/export/work
 *      objectclass: top
 *      objectclass: automount
 *
 * In this container:
 *
 *      dn: automountMapName=auto_direct,...
 *      automountMapName: auto_direct
 *      objectClass: top
 *      objectClass: automountMap
 *
 * Note that the schema can be mapped and SSD's can be used to relocate
 * the default location of these entries.
 *
 */

#define CAPCHAR '%'
#define MAXERROR 4000

static char *automountKey = NULL;
static char *automountInformation = NULL;
static char *defaultFilter = NULL;
static int encode = 0;

static int mastermap_callback_ldap();
static int directmap_callback();
static int ldap_err(int);
static int ldap_match();
static int readdir_callback();

struct loadmaster_cbdata {
        char *ptr1;
        char **ptr2;
        char ***ptr3;
};

struct loaddirect_cbdata {
        char *ptr1;
        char *ptr2;
        char **ptr3;
        char ***ptr4;
};

struct dir_cbdata {
        struct dir_entry **list;
        struct dir_entry *last;
        int error;
};

static char *tosunds_str(char *);
static char *tounix_str(char *);

static int
isAttrMapped(char *orig, char *mapped)
{
        char **s;
        char **mappedschema = NULL;

        mappedschema = __ns_ldap_getMappedAttributes("automount", orig);
        if (mappedschema == NULL)
                return (0);
        if (strcasecmp(mappedschema[0], mapped) != 0) {
                for (s = mappedschema; *s != NULL; s++)
                        free(*s);
                free(mappedschema);
                return (0);
        }
        for (s = mappedschema; *s != NULL; s++)
                free(*s);
        free(mappedschema);
        return (1);
}

static int
isObjectMapped(char *orig, char *mapped)
{
        char **s;
        char **mappedschema = NULL;

        mappedschema = __ns_ldap_getMappedObjectClass("automount", orig);
        if (mappedschema == NULL)
                return (0);
        if (strcasecmp(mappedschema[0], mapped) != 0) {
                for (s = mappedschema; *s != NULL; s++)
                        free(*s);
                free(mappedschema);
                return (0);
        }
        for (s = mappedschema; *s != NULL; s++)
                free(*s);
        free(mappedschema);
        return (1);
}

void
init_ldap(char **stack, char ***stkptr)
{
        /*
         * Check for version of the profile the client is using
         *
         * For version 1 profiles we do encoding of attributes
         * and use nisMap and nisObject schema for backward compatibility.
         *
         * For version 2 profiles we don't do encoding and use
         * automountMap and automount as default attributes (which can
         * then be overridden in libsldap if schema mapping is configured
         * in the profile).
         *
         * If profile version is not available, use version 2 as default
         * and syslog message.
         */
        int rc, v2 = 1;
        void **paramVal = NULL;
        ns_ldap_error_t *errorp = NULL;
        struct __nsw_switchconfig *conf = NULL;
        struct __nsw_lookup *lkp = NULL;
        enum __nsw_parse_err pserr;
        int     ldap_configured = 0;

#ifdef lint
        stack = stack;
        stkptr = stkptr;
#endif /* lint */

        /* get nsswitch info of "automount */
        conf = __nsw_getconfig("automount", &pserr);

        /* find out if LDAP backend is configured */
        if (conf != NULL) {
                for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
                        if (strcmp(lkp->service_name, "ldap") == 0) {
                                ldap_configured = 1;
                                break;
                        }
                }
                /* free conf at the end of "if"  bracket */
                (void) __nsw_freeconfig(conf);
        }

        /* if ldap is not configured, init_ldap is a no op */
        if (!ldap_configured)
                return;

        rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, &paramVal, &errorp);
        if (rc != NS_LDAP_SUCCESS || !paramVal || !*paramVal) {
                syslog(LOG_ERR, "Can not determine version of LDAP profile"
                    " that is used (%d, %s).  Using version 2 profile"
                    " defaults", rc, (errorp && errorp->message ?
                    errorp->message : ""));
                (void) __ns_ldap_freeError(&errorp);
        } else {
                if (strcasecmp(*paramVal, NS_LDAP_VERSION_1) == 0)
                        v2 = 0;
                (void) __ns_ldap_freeParam(&paramVal);
        }

        if (v2) {
                if (trace > 1)
                        trace_prt(1, "init_ldap: setting up for version 2\n");
                automountKey = "automountKey";
                automountInformation = "automountInformation";
                defaultFilter = "(&(objectClass=automount)(automountKey=%s))";

                /* check for automountMapName mapped to nisMapName */
                if (!isAttrMapped("automountMapName", "nisMapName"))
                        return;

                /* check for automountKey mapped to cn */
                if (!isAttrMapped("automountKey", "cn"))
                        return;

                /* check for automountInformation mapped to nisMapEntry */
                if (!isAttrMapped("automountInformation", "nisMapEntry"))
                        return;

                /* check for automountMap mapped to nisMap */
                if (!isObjectMapped("automountMap", "nisMap"))
                        return;

                /* check for automount mapped to nisObject */
                if (!isObjectMapped("automount", "nisObject"))
                        return;

                if (trace > 1)
                        trace_prt(1, "init_ldap: encode = TRUE\n");
                encode = 1;
        } else {
                if (trace > 1) {
                        trace_prt(1, "init_ldap: setting up for version 1\n");
                        trace_prt(1, "init_ldap: encode = TRUE\n");
                }
                encode = 1;
                automountKey = "cn";
                automountInformation = "nisMapEntry";
                defaultFilter = "(&(objectClass=nisObject)(cn=%s))";
        }
}

/*ARGSUSED*/
int
getmapent_ldap(char *key, char *map, struct mapline *ml,
char **stack, char ***stkptr, bool_t *iswildcard, bool_t isrestricted)
{
        char *ldap_line = NULL;
        char *lp;
        int ldap_len, len;
        int nserr;

        if (trace > 1)
                trace_prt(1, "getmapent_ldap called\n");

        if (trace > 1) {
                trace_prt(1, "getmapent_ldap: key=[ %s ]\n", key);
        }

        if (iswildcard)
                *iswildcard = FALSE;
        nserr = ldap_match(map, key, &ldap_line, &ldap_len);
        if (nserr) {
                if (nserr == __NSW_NOTFOUND) {
                        /* Try the default entry "*" */
                        if ((nserr = ldap_match(map, "\\2a", &ldap_line,
                            &ldap_len)))
                                goto done;
                        else {
                                if (iswildcard)
                                        *iswildcard = TRUE;
                        }
                } else
                        goto done;
        }

        /*
         * at this point we are sure that ldap_match
         * succeeded so massage the entry by
         * 1. ignoring # and beyond
         * 2. trim the trailing whitespace
         */
        if (lp = strchr(ldap_line, '#'))
                *lp = '\0';
        len = strlen(ldap_line);
        if (len == 0) {
                nserr = __NSW_NOTFOUND;
                goto done;
        }
        lp = &ldap_line[len - 1];
        while (lp > ldap_line && isspace(*lp))
                *lp-- = '\0';
        if (lp == ldap_line) {
                nserr = __NSW_NOTFOUND;
                goto done;
        }
        (void) strncpy(ml->linebuf, ldap_line, LINESZ);
        unquote(ml->linebuf, ml->lineqbuf);
        nserr = __NSW_SUCCESS;
done:
        if (ldap_line)
                free((char *)ldap_line);

        if (trace > 1)
                trace_prt(1, "getmapent_ldap: exiting ...\n");

        return (nserr);
}

static int
ldap_match(char *map, char *key, char **ldap_line, int *ldap_len)
{
        char searchfilter[LDAP_FILT_MAXSIZ];
        int res, attr_found;
        ns_ldap_result_t *result = NULL;
        ns_ldap_error_t *errp = NULL;
        ns_ldap_entry_t *entry = NULL;
        char *ldapkey;
        int i;

        if (trace > 1) {
                trace_prt(1, "ldap_match called\n");
                trace_prt(1, "ldap_match: key =[ %s ]\n", key);
        }

        /*
         * need to handle uppercase characters in the key because LDAP
         * searches are case insensitive.  Note, key = attribute automountKey.
         */
        if (encode)
                ldapkey = tosunds_str(key);
        else
                ldapkey = key;

        if (trace > 1) {
                trace_prt(1, "ldap_match: ldapkey =[ %s ]\n", ldapkey);
        }

        (void) sprintf(searchfilter, defaultFilter, ldapkey);

        if (trace > 1)
                trace_prt(1, "  ldap_match: Requesting list for %s in %s\n",
                    searchfilter, map);

        res = __ns_ldap_list(map, searchfilter, NULL,
            NULL, NULL, 0, &result, &errp, NULL, NULL);

        if (trace > 1) {
                if (res != NS_LDAP_SUCCESS)
                        trace_prt(1,
                            "  ldap_match: __ns_ldap_list FAILED (%d)\n", res);
                else
                        trace_prt(1, "  ldap_match: __ns_ldap_list OK\n");
        }

        if (res != NS_LDAP_SUCCESS && res != NS_LDAP_NOTFOUND) {
                if (errp) {
                        if (verbose) {
                                char errstr[MAXERROR];
                                (void) sprintf(errstr,
                                    gettext("ldap server can't list map,"
                                    " '%s': '%s' - '%d'."),
                                    map, errp->message, errp->status);
                                syslog(LOG_ERR, errstr);
                        }
                        __ns_ldap_freeError(&errp);
                } else {
                        if (verbose) {
                                char *errmsg;
                                __ns_ldap_err2str(res, &errmsg);
                                syslog(LOG_ERR, errmsg);
                        }
                }
                if (result)
                        __ns_ldap_freeResult(&result);
                return (ldap_err(res));
        }

        if (res == NS_LDAP_NOTFOUND || result == NULL ||
            result->entries_count == 0 || result->entry->attr_count == 0) {
                if (trace > 1)
                        trace_prt(1, "  ldap_match: no entries found\n");
                if (errp)
                        __ns_ldap_freeError(&errp);
                if (result)
                        __ns_ldap_freeResult(&result);
                return (__NSW_NOTFOUND);
        }

        /*
         * get value of attribute nisMapEntry.  This attribute contains a
         * list of mount options AND mount location for a particular mount
         * point (key).
         * For example:
         *
         * key: /work
         *      ^^^^^
         *      (mount point)
         *
         * nisMapEntry: -rw,intr,nosuid,noquota hosta:/export/work
         *              ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
         *              (    mount options    ) (remote mount location)
         *
         */
        attr_found = 0;
        entry = result->entry;
        for (i = 0; i < entry->attr_count; i++) {
                ns_ldap_attr_t *attr;

                attr = entry->attr_pair[i];
                if (strcasecmp(attr->attrname, automountInformation) == 0) {
                        char *attrval;

                        attr_found = 1;
                        if (encode)
                                attrval = tounix_str(attr->attrvalue[0]);
                        else
                                attrval = attr->attrvalue[0];
                        *ldap_len = strlen(key) + strlen(attrval);

                        /*
                         * so check for the length; it should be less than
                         * LINESZ
                         */
                        if ((*ldap_len + 2) > LINESZ) {
                                syslog(LOG_ERR,
                                    "ldap server map %s, entry for %s"
                                    " is too long %d chars (max %d)",
                                    map, key, (*ldap_len + 2), LINESZ);
                                __ns_ldap_freeResult(&result);
                                return (__NSW_UNAVAIL);
                        }
                        *ldap_line = (char *)malloc(*ldap_len + 2);
                        if (*ldap_line == NULL) {
                                syslog(LOG_ERR, "ldap_match: malloc failed");
                                __ns_ldap_freeResult(&result);
                                return (__NSW_UNAVAIL);
                        }

                        (void) sprintf(*ldap_line, "%s", attrval);

                        break;
                }
        }

        __ns_ldap_freeError(&errp);
        __ns_ldap_freeResult(&result);

        if (!attr_found)
                return (__NSW_NOTFOUND);

        if (trace > 1)
                trace_prt(1, "  ldap_match: found: %s\n", *ldap_line);

        return (__NSW_SUCCESS);
}

int
loadmaster_ldap(char *mapname, char *defopts, char **stack, char ***stkptr)
{
        char searchfilter[LDAP_FILT_MAXSIZ];
        int res;
        ns_ldap_result_t *result = NULL;
        ns_ldap_error_t *errp = NULL;
        struct loadmaster_cbdata master_cbdata;

        if (trace > 1)
                trace_prt(1, "loadmaster_ldap called\n");

        master_cbdata.ptr1 = defopts;
        master_cbdata.ptr2 = stack;
        master_cbdata.ptr3 = stkptr;

        /* filter gets all the entries for the specified mapname */
        (void) sprintf(searchfilter, defaultFilter, "*");

        if (trace > 1)
                trace_prt(1, "loadmaster_ldap: Requesting list for %s in %s\n",
                    searchfilter, mapname);

        res = __ns_ldap_list(mapname, searchfilter, NULL, NULL, NULL,
            0, &result, &errp, mastermap_callback_ldap,
            (void *) &master_cbdata);

        if (trace > 1)
                trace_prt(1,
                    "loadmaster_ldap: __ns_ldap_list just returned: %d\n",
                    res);

        if (res != NS_LDAP_SUCCESS) {
                if (errp) {
                        char errstr[MAXERROR];
                        if (verbose) {
                                (void) sprintf(errstr, gettext(
                                    "ldap server can't list map,"
                                    "'%s': '%s' - '%d'."),
                                    mapname, errp->message, errp->status);
                                syslog(LOG_ERR, errstr);
                        }
                        __ns_ldap_freeError(&errp);
                } else {
                        if (verbose) {
                                char *errmsg;
                                __ns_ldap_err2str(res, &errmsg);
                                syslog(LOG_ERR, errmsg);
                        }
                }
                if (result)
                        __ns_ldap_freeResult(&result);
                return (ldap_err(res));
        }

        if (trace > 1)
                trace_prt(1,
                    "loadmaster_ldap: calling __ns_ldap_freeResult...\n");

        __ns_ldap_freeResult(&result);

        if (trace > 1)
                trace_prt(1,
                    "loadmaster_ldap: about to return __NSW_SUCCESS...\n");

        return (__NSW_SUCCESS);
}

int
loaddirect_ldap(char *nsmap, char *localmap, char *opts,
char **stack, char ***stkptr)
{
        char searchfilter[LDAP_FILT_MAXSIZ];
        int res;
        ns_ldap_result_t *result = NULL;
        ns_ldap_error_t *errp = NULL;
        struct loaddirect_cbdata direct_cbdata;

        if (trace > 1) {
                trace_prt(1, "loaddirect_ldap called\n");
        }

        direct_cbdata.ptr1 = opts;
        direct_cbdata.ptr2 = localmap;
        direct_cbdata.ptr3 = stack;
        direct_cbdata.ptr4 = stkptr;

        /* filter gets all the entries for the specified mapname */
        (void) sprintf(searchfilter, defaultFilter, "*");

        if (trace > 1)
                trace_prt(1, "loaddirect_ldap: Requesting list for %s in %s\n",
                    searchfilter, nsmap);

        res = __ns_ldap_list(nsmap, searchfilter, NULL, NULL,
            NULL, 0, &result, &errp,
            directmap_callback, (void *) &direct_cbdata);


        if (res != NS_LDAP_SUCCESS) {
                if (errp) {
                        char errstr[MAXERROR];
                        if (verbose) {
                                (void) sprintf(errstr,
                                    gettext("ldap server can't list map,"
                                    " '%s': '%s' - '%d'."),
                                    nsmap, errp->message, errp->status);
                                syslog(LOG_ERR, errstr);
                        }
                        __ns_ldap_freeError(&errp);
                } else {
                        if (verbose) {
                                char *errmsg;
                                __ns_ldap_err2str(res, &errmsg);
                                syslog(LOG_ERR, errmsg);
                        }
                }
                if (result)
                        __ns_ldap_freeResult(&result);
                return (ldap_err(res));
        }

        __ns_ldap_freeResult(&result);
        return (__NSW_SUCCESS);
}

static int
ldap_err(int err)
{
        if (trace > 1)
                trace_prt(1, "ldap_err called\n");

        switch (err) {

        case NS_LDAP_SUCCESS:
                return (__NSW_SUCCESS);

        case NS_LDAP_NOTFOUND:
                return (__NSW_NOTFOUND);

        case NS_LDAP_PARTIAL:
                return (__NSW_TRYAGAIN);

        default:
                return (__NSW_UNAVAIL);
        }
}

static int
mastermap_callback_ldap(ns_ldap_entry_t *entry, void *udata)
{
        char *key, *contents, *pmap, *opts;
        char dir[LINESZ], map[LINESZ], qbuff[LINESZ];
        char cont_temp[LINESZ], key_temp[LINESZ];
        int  key_len, contents_len;
        struct loadmaster_cbdata *temp = (struct loadmaster_cbdata *)udata;
        char *defopts = temp->ptr1;
        char **stack = temp->ptr2;
        char ***stkptr = temp->ptr3;
        int i;

        if (trace > 1) {
                trace_prt(1, "mastermap_callback_ldap called\n");
                trace_prt(1, "mastermap_callback_ldap: entry=%x\n", entry);
                if (entry) {
                        trace_prt(1,
                        "mastermap_callback_ldap: entry->attr_count=[ %d ]\n",
                            entry->attr_count);
                }
        }

        /*
         * For the current entry, obtain the values of the cn and the
         * nisMapEntry attributes and the length of each value (cn=key,
         * nisMapEntry=contents).
         * We skip the description.  Even though LDAP allows for multiple
         * values per attribute, we take only the 1st value for each
         * attribute because the automount data is organized as such.
         */
        key_len = 0;
        contents_len = 0;
        key = NULL;
        contents = NULL;
        for (i = 0; i < entry->attr_count; i++) {
                ns_ldap_attr_t *attr;

                attr = entry->attr_pair[i];
                if (trace > 1) {
                        trace_prt(1,
                        "mastermap_callback_ldap: attr[%d]: %s=%s\n",
                            i, attr->attrname, attr->attrvalue[0]);
                }
                if (strcasecmp(attr->attrname, automountInformation) == 0) {
                        if (encode)
                                (void) strncpy(cont_temp,
                                    tounix_str(attr->attrvalue[0]), LINESZ);
                        else
                                (void) strncpy(cont_temp, attr->attrvalue[0],
                                    LINESZ);
                        contents = cont_temp;
                        contents_len = strlen(contents);
                        if (trace > 1) {
                                trace_prt(1,
                                    "mastermap_callback_ldap: contents=[ %s ],"
                                    " contents_len=[ %d ]\n",
                                    contents, contents_len);
                        }
                }
                if (strcasecmp(attr->attrname, automountKey) == 0) {
                        if (encode)
                                (void) strncpy(key_temp,
                                    tounix_str(attr->attrvalue[0]), LINESZ);
                        else
                                (void) strncpy(key_temp, attr->attrvalue[0],
                                    LINESZ);
                        key = key_temp;
                        key_len = strlen(key);
                        if (trace > 1) {
                                trace_prt(1,
                                    "mastermap_callback_ldap: key=[ %s ],"
                                    " key_len=[ %d ]\n",
                                    key, key_len);
                        }
                }
        }

        if (key_len >= LINESZ || contents_len >= LINESZ)
                return (0);
        if (key_len < 2 || contents_len < 2)
                return (0);

        while (isspace(*contents))
                contents++;
        if (contents == NULL)
                return (0);
        if (isspace(*key) || *key == '#')
                return (0);

        (void) strncpy(dir, key, key_len);
        dir[key_len] = '\0';
        if (trace > 1)
                trace_prt(1, "mastermap_callback_ldap: dir= [ %s ]\n", dir);
        for (i = 0; i < LINESZ; i++)
                qbuff[i] = ' ';
        if (macro_expand("", dir, qbuff, sizeof (dir))) {
                syslog(LOG_ERR,
                    "%s in ldap server map: entry too long (max %d chars)",
                    dir, sizeof (dir) - 1);
                return (0);
        }
        (void) strncpy(map, contents, contents_len);
        map[contents_len] = '\0';
        if (trace > 1)
                trace_prt(1, "mastermap_callback_ldap: map= [ %s ]\n", map);
        if (macro_expand("", map, qbuff, sizeof (map))) {
                syslog(LOG_ERR,
                    "%s in ldap server map: entry too long (max %d chars)",
                    map, sizeof (map) - 1);
                return (0);
        }
        pmap = map;
        while (*pmap && isspace(*pmap))
                pmap++;         /* skip blanks in front of map */
        opts = pmap;
        while (*opts && !isspace(*opts))
                opts++;
        if (*opts) {
                *opts++ = '\0';
                while (*opts && isspace(*opts))
                        opts++;
                if (*opts == '-')
                        opts++;
                        else
                        opts = defopts;
        }
        /*
         * Check for no embedded blanks.
         */
        if (strcspn(opts, "     ") == strlen(opts)) {
                if (trace > 1)
                        trace_prt(1,
                        "mastermap_callback_ldap: dir=[ %s ], pmap=[ %s ]\n",
                            dir, pmap);
                dirinit(dir, pmap, opts, 0, stack, stkptr);
        } else {
                char *dn = NULL;

                /* get the value for the dn */
                for (i = 0; i < entry->attr_count; i++) {
                        ns_ldap_attr_t *attr;

                        attr = entry->attr_pair[i];
                        if (strcasecmp(attr->attrname, "dn")
                            == 0) {
                                dn = attr->attrvalue[0];
                                break;
                        }
                }
                pr_msg(
                    "Warning: invalid entry for %s in ldap server"
                    " dn: %s ignored.\n",
                    dir, dn);
        }
        if (trace > 1)
                trace_prt(1, "mastermap_callback_ldap exiting...\n");
        return (0);
}

static int
directmap_callback(ns_ldap_entry_t *entry, void *udata)
{
        char *key;
        char dir[256];
        int  key_len;
        struct loaddirect_cbdata *temp = (struct loaddirect_cbdata *)udata;
        char *opts = temp->ptr1;
        char *localmap = temp->ptr2;
        char **stack = temp->ptr3;
        char ***stkptr = temp->ptr4;
        int i;

        /*
         * For the current entry, obtain the value and length of the cn i.e.
         * the contents of key and its key length.
         */
        key_len = 0;
        key = NULL;
        for (i = 0; i < entry->attr_count; i++) {
                ns_ldap_attr_t *attr;

                attr = entry->attr_pair[i];
                if (strcasecmp(attr->attrname, automountKey) == 0) {
                        if (encode)
                                key = tounix_str(attr->attrvalue[0]);
                        else
                                key = attr->attrvalue[0];
                        key_len = strlen(key);
                        break;
                }
        }

        if (key_len >= 100 || key_len < 2)
                return (0);

        if (isspace(*key) || *key == '#')
                return (0);
        (void) strncpy(dir, key, key_len);
        dir[key_len] = '\0';

        dirinit(dir, localmap, opts, 1, stack, stkptr);

        return (0);
}

int
getmapkeys_ldap(char *nsmap, struct dir_entry **list, int *error,
int *cache_time, char **stack, char ***stkptr)
{
        char searchfilter[LDAP_FILT_MAXSIZ];
        int res;
        ns_ldap_result_t *result = NULL;
        ns_ldap_error_t *errp = NULL;
        struct dir_cbdata readdir_cbdata;

#ifdef lint
        stack = stack;
        stkptr = stkptr;
#endif /* lint */

        if (trace > 1)
                trace_prt(1, "getmapkeys_ldap called\n");

        *cache_time = RDDIR_CACHE_TIME;
        *error = 0;
        readdir_cbdata.list = list;
        readdir_cbdata.last = NULL;

        /* filter gets all the entries for the specified mapname */
        (void) sprintf(searchfilter, defaultFilter, "*");

        if (trace > 1)
                trace_prt(1, "getmapkeys_ldap: Requesting list for %s in %s\n",
                    searchfilter, nsmap);

        res = __ns_ldap_list(nsmap, searchfilter, NULL, NULL, NULL, 0,
            &result, &errp, readdir_callback, (void *) &readdir_cbdata);

        if (trace > 1)
                trace_prt(1, "  getmapkeys_ldap: __ns_ldap_list returned %d\n",
                    res);

        if (readdir_cbdata.error)
                *error = readdir_cbdata.error;

        if (res != NS_LDAP_SUCCESS && res != NS_LDAP_NOTFOUND) {
                if (errp) {
                        if (verbose) {
                                char errstr[MAXERROR];
                                (void) sprintf(errstr, gettext(
                                    "ldap server can't list map,"
                                    " '%s': '%s' - '%d'."),
                                    nsmap, errp->message, errp->status);
                                syslog(LOG_ERR, errstr);
                        }
                        __ns_ldap_freeError(&errp);
                } else {
                        if (verbose) {
                                char *errmsg;
                                __ns_ldap_err2str(res, &errmsg);
                                syslog(LOG_ERR, errmsg);
                        }
                }
                if (result)
                        __ns_ldap_freeResult(&result);
                if (*error == 0)
                        *error = ECOMM;
                return (ldap_err(res));
        }
        if (result)
                __ns_ldap_freeResult(&result);

        return (__NSW_SUCCESS);
}

static int
readdir_callback(const ns_ldap_entry_t *entry, const void *udata)
{
        char *key;
        int  key_len;
        struct dir_cbdata *temp = (struct dir_cbdata *)udata;
        struct dir_entry **list = temp->list;
        struct dir_entry *last = temp->last;
        int i;

        if (trace > 1)
                trace_prt(1, "readdir_callback called\n");
        /*
         * For the current entry, obtain the value and length of the cn i.e. the
         * contents of key and its key length.
         */
        key_len = 0;
        key = NULL;

        if (trace > 1)
                trace_prt(1, "readdir_callback: entry->attr_count=[ %d ]\n",
                    entry->attr_count);

        for (i = 0; i < entry->attr_count; i++) {
                ns_ldap_attr_t *attr;

                attr = entry->attr_pair[i];

                if (trace > 1)
                        trace_prt(1,
                        "readdir_callback: attr->attrname=[ %s ]\n",
                            attr->attrname);

                if (strcasecmp(attr->attrname, automountKey) == 0) {
                        if (encode)
                                key = tounix_str(attr->attrvalue[0]);
                        else
                                key = attr->attrvalue[0];
                        key_len = strlen(key);

                        if (trace > 1)
                                trace_prt(1,
                        "readdir_callback: key=[ %s ], key_len=[ %d ]\n",
                                    key, key_len);

                        break;
                }
        }

        if (key_len >= 100 || key_len < 2)
                return (0);

        if (isspace(*key) || *key == '#')
                return (0);

        /*
         * Wildcard entry should be ignored - following entries should continue
         * to be read to corroborate with the way we search for entries in
         * LDAP, i.e., first for an exact key match and then a wildcard
         * if there's no exact key match.
         */
        if (key[0] == '*' && key[1] == '\0')
                return (0);

        if (add_dir_entry(key, list, &last)) {
                temp->error = ENOMEM;
                return (1);
        }

        temp->last = last;
        temp->error = 0;

        if (trace > 1)
                trace_prt(1, "readdir_callback returning 0...\n");

        return (0);
}

/*
 * Puts CAPCHAR in front of uppercase characters or surrounds a set of
 * contiguous uppercase characters with CAPCHARS and square brackets.
 *
 * For example (assuming CAPCHAR = '%'):
 *
 * if str = Abc, it returns %Abc
 * if str = ABc, it returns %[AB]c
 * if str = AbC, it returns %Ab%C
 *
 */
static char *
tosunds_str(char *str)
{
        static char buf[BUFSIZ];
        int i, j, er = FALSE;
#ifdef NEWCAP
        int openBracket = FALSE, closeBracket = FALSE;
#endif

        (void) memset(buf, 0, BUFSIZ);

        j = 0;
        for (i = 0; i < strlen(str); i++) {
                /* Check the current element */
                if (isupper(str[i])) {
#ifdef NEWCAP
                        /* check the next element */
                        if (isupper(str[i+1])) {
                                if (openBracket == FALSE) {
                                        openBracket = TRUE;
                                        buf[j] = CAPCHAR;
                                        buf[j+1] = '[';
                                        j += 2;
                                }
                        } else {
                                if (openBracket == FALSE) {
                                        buf[j] = CAPCHAR;
                                        j++;
                                } else {
                                        openBracket = FALSE;
                                        closeBracket = TRUE;
                                }
                        }
#else
                        buf[j++] = CAPCHAR;
#endif
                }
                buf[j] = str[i];
                j++;

#ifdef NEWCAP
                if (closeBracket == TRUE) {
                        closeBracket = FALSE;
                        buf[j] = ']';
                        j++;
                }
#endif
                if (j >= BUFSIZ) {
                        er = TRUE;
                        break;
                }
        }

        if (er) {
                syslog(LOG_ERR, "Buffer size exceeded.");
                (void) memset(buf, 0, BUFSIZ);
        } else
                buf[j] = '\0';

        return (buf);

}

/*
 * Reverses what tosunds_str() did
 */
static char *
tounix_str(char *str)
{
        static char buf[BUFSIZ];
        int i, j;
        int openBracket = FALSE;

        (void) memset(buf, 0, BUFSIZ);
        j = 0;

        for (i = 0; i < strlen(str); i++) {
                if (str[i] == '%') {
                        if (isupper(str[i+1])) {
                                i += 1;
                        } else if ((str[i+1] == '[') && (isupper(str[i+2]))) {
                                i += 2;
                                openBracket = TRUE;
                        }
                } else if (str[i] == ']') {
                        if ((isupper(str[i-1])) && (openBracket == TRUE))
                                i += 1;
                        openBracket = FALSE;
                }
                buf[j] = str[i];
                j++;
        }
        return (buf);
}