root/usr/src/lib/libsldap/common/ns_config.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
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * libsldap - library side configuration components
 * Routines to manage the config structure
 */

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <strings.h>
#include <libintl.h>
#include <locale.h>
#include <thread.h>
#include <synch.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <crypt.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
#include <netdb.h>
#include <sys/systeminfo.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <limits.h>
#include "ns_sldap.h"
#include "ns_internal.h"
#include "ns_cache_door.h"
#include "ns_connmgmt.h"

#pragma fini(__s_api_shutdown_conn_mgmt, \
        _free_config, __ns_ldap_doorfd_close)

static mutex_t          ns_parse_lock = DEFAULTMUTEX;
static mutex_t          ns_loadrefresh_lock = DEFAULTMUTEX;
static ns_config_t      *current_config = NULL;

static int              cache_server = FALSE;
extern thread_key_t     ns_cmgkey;

/*
 * Parameter Index Type validation routines
 */
static int
__s_val_postime(ParamIndexType i, ns_default_config *def,
    ns_param_t *param, char *errbuf);
static int
__s_val_basedn(ParamIndexType i, ns_default_config *def,
    ns_param_t *param, char *errbuf);

static int
__s_val_binddn(ParamIndexType i, ns_default_config *def,
    ns_param_t *param, char *errbuf);

static int
__s_val_bindpw(ParamIndexType i, ns_default_config *def,
    ns_param_t *param, char *errbuf);

static int
__s_val_serverList(ParamIndexType i, ns_default_config *def,
    ns_param_t *param, char *errbuf);

/*
 * Forward declarations
 */

static ns_parse_status
verify_value(ns_config_t *cfg, char *name, char *value, char *errstr);

static int
set_default_value(ns_config_t *configptr, char *name, char *value,
    ns_ldap_error_t **error);

static void
set_curr_config(ns_config_t *ptr);

static int
__door_getldapconfig(char **buffer, int *buflen, ns_ldap_error_t **error);

static ns_config_t *
SetDoorInfo(char *buffer, ns_ldap_error_t **errorp);

static boolean_t
timetorefresh(ns_config_t *cfg);

static ns_config_t *
LoadCacheConfiguration(ns_config_t *, ns_ldap_error_t **error);

static void **
dupParam(ns_param_t *ptr);

static time_t
conv_time(char *s);

/*
 * Structures used in enum <-> string mapping routines
 */

static ns_enum_map ns_auth_enum_v1[] = {
        { ENUM2INT(NS_LDAP_EA_NONE), "NS_LDAP_AUTH_NONE" },
        { ENUM2INT(NS_LDAP_EA_SIMPLE), "NS_LDAP_AUTH_SIMPLE" },
        { ENUM2INT(NS_LDAP_EA_SASL_CRAM_MD5), "NS_LDAP_AUTH_SASL_CRAM_MD5" },
        { -1, NULL },
};

static ns_enum_map ns_auth_enum_v2[] = {
        { ENUM2INT(NS_LDAP_EA_NONE), "none" },
        { ENUM2INT(NS_LDAP_EA_SIMPLE), "simple" },
        { ENUM2INT(NS_LDAP_EA_SASL_CRAM_MD5), "sasl/CRAM-MD5" },
        { ENUM2INT(NS_LDAP_EA_SASL_DIGEST_MD5), "sasl/DIGEST-MD5" },
        { ENUM2INT(NS_LDAP_EA_SASL_DIGEST_MD5_INT),
                        "sasl/DIGEST-MD5:auth-int" },
        { ENUM2INT(NS_LDAP_EA_SASL_DIGEST_MD5_CONF),
                        "sasl/DIGEST-MD5:auth-conf" },
        { ENUM2INT(NS_LDAP_EA_SASL_EXTERNAL), "sasl/EXTERNAL" },
        { ENUM2INT(NS_LDAP_EA_SASL_GSSAPI), "sasl/GSSAPI" },
        { ENUM2INT(NS_LDAP_EA_TLS_NONE), "tls:none" },
        { ENUM2INT(NS_LDAP_EA_TLS_SIMPLE), "tls:simple" },
        { ENUM2INT(NS_LDAP_EA_TLS_SASL_CRAM_MD5), "tls:sasl/CRAM-MD5" },
        { ENUM2INT(NS_LDAP_EA_TLS_SASL_DIGEST_MD5), "tls:sasl/DIGEST-MD5" },
        { ENUM2INT(NS_LDAP_EA_TLS_SASL_DIGEST_MD5_INT),
                        "tls:sasl/DIGEST-MD5:auth-int" },
        { ENUM2INT(NS_LDAP_EA_TLS_SASL_DIGEST_MD5_CONF),
                        "tls:sasl/DIGEST-MD5:auth-conf" },
        { ENUM2INT(NS_LDAP_EA_TLS_SASL_EXTERNAL), "tls:sasl/EXTERNAL" },
        { -1, NULL },
};

        /* V1 ONLY */
static ns_enum_map ns_sec_enum_v1[] = {
        { ENUM2INT(NS_LDAP_TLS_NONE), "NS_LDAP_SEC_NONE" },
        { -1, NULL },
};

        /* V2 ONLY */
static ns_enum_map ns_cred_enum_v2[] = {
        { ENUM2INT(NS_LDAP_CRED_ANON), "anonymous" },
        { ENUM2INT(NS_LDAP_CRED_PROXY), "proxy" },
        { ENUM2INT(NS_LDAP_CRED_SELF), "self" },
        { -1, NULL },
};

static ns_enum_map ns_ref_enum_v1[] = {
        { ENUM2INT(NS_LDAP_FOLLOWREF), "NS_LDAP_FOLLOWREF" },
        { ENUM2INT(NS_LDAP_NOREF), "NS_LDAP_NOREF" },
        { -1, NULL },
};

static ns_enum_map ns_ref_enum_v2[] = {
        { ENUM2INT(NS_LDAP_FOLLOWREF), "TRUE" },
        { ENUM2INT(NS_LDAP_NOREF), "FALSE" },
        { -1, NULL },
};

static ns_enum_map ns_scope_enum_v1[] = {
        { ENUM2INT(NS_LDAP_SCOPE_BASE), "NS_LDAP_SCOPE_BASE" },
        { ENUM2INT(NS_LDAP_SCOPE_ONELEVEL), "NS_LDAP_SCOPE_ONELEVEL" },
        { ENUM2INT(NS_LDAP_SCOPE_SUBTREE), "NS_LDAP_SCOPE_SUBTREE" },
        { -1, NULL },
};

static ns_enum_map ns_scope_enum_v2[] = {
        { ENUM2INT(NS_LDAP_SCOPE_BASE), "base" },
        { ENUM2INT(NS_LDAP_SCOPE_ONELEVEL), "one" },
        { ENUM2INT(NS_LDAP_SCOPE_SUBTREE), "sub" },
        { -1, NULL },
};

static ns_enum_map ns_pref_enum[] = {
        { ENUM2INT(NS_LDAP_PREF_FALSE), "NS_LDAP_FALSE" },
        { ENUM2INT(NS_LDAP_PREF_TRUE), "NS_LDAP_TRUE" },
        { -1, NULL },
};

static ns_enum_map ns_shadow_update_enum[] = {
        { ENUM2INT(NS_LDAP_ENABLE_SHADOW_UPDATE_FALSE), "FALSE" },
        { ENUM2INT(NS_LDAP_ENABLE_SHADOW_UPDATE_TRUE), "TRUE" },
        { -1, NULL },
};

static int      ns_def_auth_v1[] = {
        ENUM2INT(NS_LDAP_EA_NONE),
        0
};

static int      ns_def_auth_v2[] = {
        ENUM2INT(NS_LDAP_EA_NONE),
        0
};

static int      ns_def_cred_v1[] = {
        ENUM2INT(NS_LDAP_CRED_PROXY),
        0
};

static int      ns_def_cred_v2[] = {
        ENUM2INT(NS_LDAP_CRED_ANON),
        0
};

/*
 * The next macro places an integer in the first sizeof(int) bytes of a
 * void pointer location. For 32-bit, it is the same as "(void *) i". It
 * is used to solve a problem found during 64-bit testing.  The problem
 * was that for a configuration parameter such as NS_LDAP_SEARCH_REF_P,
 * which is of type INT and has defined default value, an int
 * variable(ns_param.ns_pu.i) defined inside an union(ns_pu) structure, is
 * used to access the defined default value. This requires the default
 * value to be in the first sizeof(int) bytes of the union element.  If
 * just using "(void *) intval" to declare the default value in the
 * following defconfig[] structure, the intval data will be placed is the
 * last sizeof(int) bytes. In which case, when accessing via ns_pu_i in
 * a 64-bit system, ZERO will be returned as the default value, not the
 * defined one.
 *
 * Note since amd64 is little-endian, the problem is not an issue.
 * INT2VOIDPTR will just leave the data (i) unchanged.
 */
#if defined(__amd64)
#define INT2VOIDPTR(i)  (void *)i
#else
#define INT2VOIDPTR(i)  \
        (void *)(((long)(i))<<(8*(sizeof (void *) - sizeof (int))))
#endif
/*
 * The default configuration table
 * Version 1 entries are first, V2 entries follow.
 */
static ns_default_config defconfig[] = {
        /* optional V1 profile */
        {"NS_LDAP_FILE_VERSION", NS_LDAP_FILE_VERSION_P,
                CLIENTCONFIG,   CHARPTR,        TRUE,   NS_LDAP_V1,
                NULL,   /* No version number defined in V1 */
                { CHARPTR, 0, (void *)NS_LDAP_VERSION_1 },
                NULL, NULL },

        /* ---------- V1 profile ---------- */
        {"NS_LDAP_BINDDN", NS_LDAP_BINDDN_P,
                CREDCONFIG,     CHARPTR,        TRUE,   NS_LDAP_V1,
                _P1_BINDDN,
                { CHARPTR, 0, NULL },
                __s_val_binddn, NULL },

        {"NS_LDAP_BINDPASSWD", NS_LDAP_BINDPASSWD_P,
                CREDCONFIG,     CHARPTR,        TRUE,   NS_LDAP_V1,
                _P1_BINDPASSWORD,
                { CHARPTR, 0, NULL },
                __s_val_bindpw, NULL },

        {"NS_LDAP_SERVERS", NS_LDAP_SERVERS_P,
                SERVERCONFIG,   ARRAYCP,        FALSE,  NS_LDAP_V1,
                _P1_SERVERS,
                { ARRAYCP, 0, NULL },
                __s_val_serverList, NULL },

        {"NS_LDAP_SEARCH_BASEDN", NS_LDAP_SEARCH_BASEDN_P,
                SERVERCONFIG,   CHARPTR,        TRUE,   NS_LDAP_V1,
                _P1_SEARCHBASEDN,
                { CHARPTR, 0, NULL },
                __s_val_basedn, NULL },

        {"NS_LDAP_AUTH", NS_LDAP_AUTH_P,
                CLIENTCONFIG,   ARRAYAUTH,      FALSE,  NS_LDAP_V1,
                _P1_AUTHMETHOD,
                { ARRAYAUTH, 1, (void *)&ns_def_auth_v1[0] },
                NULL, ns_auth_enum_v1 },

        {"NS_LDAP_TRANSPORT_SEC", NS_LDAP_TRANSPORT_SEC_P,
                CLIENTCONFIG,   INT,            TRUE,   NS_LDAP_V1,
                _P1_TRANSPORTSECURITY,
                { INT, 0, INT2VOIDPTR(NS_LDAP_TLS_NONE) },
                NULL, ns_sec_enum_v1 },

        {"NS_LDAP_SEARCH_REF", NS_LDAP_SEARCH_REF_P,
                CLIENTCONFIG,   INT,            TRUE,   NS_LDAP_V1,
                _P1_SEARCHREFERRAL,
                { INT, 0, INT2VOIDPTR(NS_LDAP_FOLLOWREF) },
                NULL, ns_ref_enum_v1 },

        {"NS_LDAP_DOMAIN", NS_LDAP_DOMAIN_P,
                CLIENTCONFIG,   CHARPTR,        TRUE,   NS_LDAP_V1,
                NULL,   /* not defined in the Profile */
                { CHARPTR, 0, NULL },
                NULL, NULL },

        {"NS_LDAP_EXP", NS_LDAP_EXP_P,
                SERVERCONFIG,   TIMET,          TRUE,   NS_LDAP_V1,
                NULL,   /* initialized by code to time+NS_LDAP_CACHETTL */
                { INT, 0, 0 },
                NULL, NULL },

        {"NS_LDAP_CERT_PATH", NS_LDAP_CERT_PATH_P,
                CREDCONFIG,     CHARPTR,        TRUE,   NS_LDAP_V1,
                _P1_CERTIFICATEPATH,
                { CHARPTR, 0, NULL },
                NULL, NULL },

        {"NS_LDAP_CERT_PASS", NS_LDAP_CERT_PASS_P,
                CREDCONFIG,     CHARPTR,        TRUE,   NS_LDAP_V1,
                _P1_CERTIFICATEPASSWORD,
                { CHARPTR, 0, NULL },
                NULL, NULL },

        {"NS_LDAP_SEARCH_DN", NS_LDAP_SEARCH_DN_P,
                CLIENTCONFIG,   SSDLIST,        FALSE,  NS_LDAP_V1,
                _P1_DATASEARCHDN,
                { SSDLIST, 0, NULL },
                NULL, NULL },

        {"NS_LDAP_SEARCH_SCOPE", NS_LDAP_SEARCH_SCOPE_P,
                CLIENTCONFIG,   INT,            TRUE,   NS_LDAP_V1,
                _P1_SEARCHSCOPE,
                { INT, 0, INT2VOIDPTR(NS_LDAP_SCOPE_ONELEVEL) },
                NULL, ns_scope_enum_v1 },

        {"NS_LDAP_SEARCH_TIME", NS_LDAP_SEARCH_TIME_P,
                CLIENTCONFIG,   INT,            TRUE,   NS_LDAP_V1,
                _P1_SEARCHTIMELIMIT,
                { INT, 0, INT2VOIDPTR(NS_DEFAULT_SEARCH_TIMEOUT) },
                NULL, NULL },

        {"NS_LDAP_SERVER_PREF", NS_LDAP_SERVER_PREF_P,
                CLIENTCONFIG,   ARRAYCP,        FALSE,  NS_LDAP_V1,
                _P1_PREFERREDSERVER,
                { ARRAYCP, 0, NULL },
                __s_val_serverList, NULL },

        {"NS_LDAP_PREF_ONLY", NS_LDAP_PREF_ONLY_P,
                CLIENTCONFIG,   INT,            TRUE,   NS_LDAP_V1,
                _P1_PREFERREDSERVERONLY,
                { INT, 0, INT2VOIDPTR(NS_LDAP_PREF_FALSE) },
                NULL, ns_pref_enum },

        {"NS_LDAP_CACHETTL", NS_LDAP_CACHETTL_P,
                CLIENTCONFIG,   CHARPTR,        TRUE,   NS_LDAP_V1,
                _P1_CACHETTL,
                { CHARPTR, 0, (void *)EXP_DEFAULT_TTL },
                __s_val_postime, NULL },

        {"NS_LDAP_PROFILE", NS_LDAP_PROFILE_P,
                CLIENTCONFIG,   CHARPTR,        TRUE,   NS_LDAP_V1,
                _P_CN,
                { CHARPTR, 0, (void *)DEFAULTCONFIGNAME },
                NULL, NULL },

        {"NS_LDAP_BIND_TIME", NS_LDAP_BIND_TIME_P,
                CLIENTCONFIG,   INT,            TRUE,   NS_LDAP_V1,
                _P1_BINDTIMELIMIT,
                { INT, 0, INT2VOIDPTR(NS_DEFAULT_BIND_TIMEOUT) },
                NULL, NULL },

        /* This configuration option is not visible in V1 */
        {"NS_LDAP_CREDENTIAL_LEVEL", NS_LDAP_CREDENTIAL_LEVEL_P,
                CLIENTCONFIG,   ARRAYCRED,      TRUE,   NS_LDAP_V1,
                NULL,   /* No version defined in V1 */
                { ARRAYCRED, 0, (void *)&ns_def_cred_v1[0] },
                NULL, NULL },

        /* ---------- V2 profile ---------- */
        {"NS_LDAP_FILE_VERSION", NS_LDAP_FILE_VERSION_P,
                CLIENTCONFIG,   CHARPTR,        TRUE,   NS_LDAP_V2,
                NULL,   /* No version number defined in V1 */
                { CHARPTR, 0, (void *)NS_LDAP_VERSION_2 },
                NULL, NULL },

        {"NS_LDAP_BINDDN", NS_LDAP_BINDDN_P,
                CREDCONFIG,     CHARPTR,        TRUE,   NS_LDAP_V2,
                NULL,   /* not defined in the Profile */
                { CHARPTR, 0, NULL },
                __s_val_binddn, NULL },

        {"NS_LDAP_BINDPASSWD", NS_LDAP_BINDPASSWD_P,
                CREDCONFIG,     CHARPTR,        TRUE,   NS_LDAP_V2,
                NULL,   /* not defined in the Profile */
                { CHARPTR, 0, NULL },
                __s_val_bindpw, NULL },

        {"NS_LDAP_ENABLE_SHADOW_UPDATE", NS_LDAP_ENABLE_SHADOW_UPDATE_P,
                CREDCONFIG,     INT,    TRUE,   NS_LDAP_V2,
                NULL,   /* not defined in the Profile */
                { INT, 0, INT2VOIDPTR(NS_LDAP_ENABLE_SHADOW_UPDATE_FALSE) },
                NULL, ns_shadow_update_enum },

        {"NS_LDAP_ADMIN_BINDDN", NS_LDAP_ADMIN_BINDDN_P,
                CREDCONFIG,     CHARPTR,        TRUE,   NS_LDAP_V2,
                NULL,   /* not defined in the Profile */
                { CHARPTR, 0, NULL },
                __s_val_binddn, NULL },

        {"NS_LDAP_ADMIN_BINDPASSWD", NS_LDAP_ADMIN_BINDPASSWD_P,
                CREDCONFIG,     CHARPTR,        TRUE,   NS_LDAP_V2,
                NULL,   /* not defined in the Profile */
                { CHARPTR, 0, NULL },
                __s_val_bindpw, NULL },

        {"NS_LDAP_EXP", NS_LDAP_EXP_P,
                SERVERCONFIG,   TIMET,          TRUE,   NS_LDAP_V2,
                NULL,   /* initialized by code to time+NS_LDAP_CACHETTL */
                { INT, 0, 0 },
                NULL, NULL },

        {"NS_LDAP_SERVER_PREF", NS_LDAP_SERVER_PREF_P,
                CLIENTCONFIG,   SERVLIST,       FALSE,  NS_LDAP_V2,
                _P2_PREFERREDSERVER,
                { SERVLIST, 0, NULL },
                __s_val_serverList, NULL },

        {"NS_LDAP_SERVERS", NS_LDAP_SERVERS_P,
                SERVERCONFIG,   SERVLIST,       FALSE,  NS_LDAP_V2,
                _P2_DEFAULTSERVER,
                { SERVLIST, 0, NULL },
                __s_val_serverList, NULL },

        {"NS_LDAP_SEARCH_BASEDN", NS_LDAP_SEARCH_BASEDN_P,
                SERVERCONFIG,   CHARPTR,        TRUE,   NS_LDAP_V2,
                _P2_SEARCHBASEDN,
                { CHARPTR, 0, NULL },
                __s_val_basedn, NULL },

        {"NS_LDAP_SEARCH_SCOPE", NS_LDAP_SEARCH_SCOPE_P,
                CLIENTCONFIG,   INT,            TRUE,   NS_LDAP_V2,
                _P2_SEARCHSCOPE,
                { INT, 0, INT2VOIDPTR(NS_LDAP_SCOPE_ONELEVEL) },
                NULL, ns_scope_enum_v2 },

        {"NS_LDAP_AUTH", NS_LDAP_AUTH_P,
                CLIENTCONFIG,   ARRAYAUTH,      FALSE,  NS_LDAP_V2,
                _P2_AUTHMETHOD,
                { ARRAYAUTH, 2, (void *)&ns_def_auth_v2[0] },
                NULL, ns_auth_enum_v2 },

        {"NS_LDAP_CREDENTIAL_LEVEL", NS_LDAP_CREDENTIAL_LEVEL_P,
                CLIENTCONFIG,   ARRAYCRED,      FALSE,  NS_LDAP_V2,
                _P2_CREDENTIALLEVEL,
                { ARRAYCRED, 0, (void *)&ns_def_cred_v2[0] },
                NULL, ns_cred_enum_v2 },

        {"NS_LDAP_SERVICE_SEARCH_DESC", NS_LDAP_SERVICE_SEARCH_DESC_P,
                CLIENTCONFIG,   SSDLIST,        FALSE,  NS_LDAP_V2,
                _P2_SERVICESEARCHDESC,
                { SSDLIST, 0, NULL },
                NULL, NULL },

        {"NS_LDAP_SEARCH_TIME", NS_LDAP_SEARCH_TIME_P,
                CLIENTCONFIG,   INT,            TRUE,   NS_LDAP_V2,
                _P2_SEARCHTIMELIMIT,
                { INT, 0, INT2VOIDPTR(NS_DEFAULT_SEARCH_TIMEOUT) },
                NULL, NULL },

        {"NS_LDAP_BIND_TIME", NS_LDAP_BIND_TIME_P,
                CLIENTCONFIG,   INT,            TRUE,   NS_LDAP_V2,
                _P2_BINDTIMELIMIT,
                { INT, 0, INT2VOIDPTR(NS_DEFAULT_BIND_TIMEOUT) },
                NULL, NULL },

        {"NS_LDAP_SEARCH_REF", NS_LDAP_SEARCH_REF_P,
                CLIENTCONFIG,   INT,            TRUE,   NS_LDAP_V2,
                _P2_FOLLOWREFERRALS,
                { INT, 0, INT2VOIDPTR(NS_LDAP_FOLLOWREF) },
                NULL, ns_ref_enum_v2 },

        {"NS_LDAP_CACHETTL", NS_LDAP_CACHETTL_P,
                CLIENTCONFIG,   CHARPTR,        TRUE,   NS_LDAP_V2,
                _P2_PROFILETTL,
                { CHARPTR, 0, (void *)EXP_DEFAULT_TTL },
                __s_val_postime, NULL },

        {"NS_LDAP_ATTRIBUTEMAP", NS_LDAP_ATTRIBUTEMAP_P,
                CLIENTCONFIG,   ATTRMAP,        FALSE,  NS_LDAP_V2,
                _P2_ATTRIBUTEMAP,
                { ATTRMAP, 0, NULL },
                NULL, NULL },

        {"NS_LDAP_OBJECTCLASSMAP", NS_LDAP_OBJECTCLASSMAP_P,
                CLIENTCONFIG,   OBJMAP,         FALSE,  NS_LDAP_V2,
                _P2_OBJECTCLASSMAP,
                { OBJMAP, 0, NULL },
                NULL, NULL },

        {"NS_LDAP_PROFILE", NS_LDAP_PROFILE_P,
                CLIENTCONFIG,   CHARPTR,        TRUE,   NS_LDAP_V2,
                _P_CN,
                { CHARPTR, 0, (void *)DEFAULTCONFIGNAME },
                NULL, NULL },

        {"NS_LDAP_SERVICE_AUTH_METHOD", NS_LDAP_SERVICE_AUTH_METHOD_P,
                CLIENTCONFIG,   SAMLIST,        FALSE,  NS_LDAP_V2,
                _P2_SERVICEAUTHMETHOD,
                { SAMLIST, 0, NULL },
                NULL, NULL },

        {"NS_LDAP_SERVICE_CRED_LEVEL", NS_LDAP_SERVICE_CRED_LEVEL_P,
                CLIENTCONFIG,   SCLLIST,        FALSE,  NS_LDAP_V2,
                _P2_SERVICECREDLEVEL,
                { SCLLIST, 0, NULL },
                NULL, NULL },

        {"NS_LDAP_HOST_CERTPATH", NS_LDAP_HOST_CERTPATH_P,
                CREDCONFIG,     CHARPTR,        TRUE,   NS_LDAP_V2,
                NULL,   /* not defined in the Profile */
                { CHARPTR, 0, (void *)NSLDAPDIRECTORY },
                NULL, NULL },

        /* array terminator [not an entry] */
        {NULL, NS_LDAP_FILE_VERSION_P,
                CLIENTCONFIG,   NS_UNKNOWN,     TRUE,   0,
                NULL,
                { NS_UNKNOWN, 0, NULL },
                NULL, NULL },
};

static char *
__getdomainname()
{
        /*
         * The sysinfo man page recommends using a buffer size
         * of 257 bytes. MAXHOSTNAMELEN is 256. So add 1 here.
         */
        char    buf[MAXHOSTNAMELEN + 1];
        int     status;

        status = sysinfo(SI_SRPC_DOMAIN, buf, MAXHOSTNAMELEN);
        if (status < 0)
                return (NULL);
        /* error: not enough space to hold returned value */
        if (status > sizeof (buf))
                return (NULL);
        return (strdup(buf));
}

void
__ns_ldap_setServer(int set)
{
        cache_server = set;
}

static boolean_t
timetorefresh(ns_config_t *cfg)
{
        struct timeval  tp;
        static time_t   expire = 0;

        if (cfg == NULL || gettimeofday(&tp, NULL) == -1)
                return (B_TRUE);

        if (cfg->paramList[NS_LDAP_EXP_P].ns_ptype == TIMET)
                expire = cfg->paramList[NS_LDAP_EXP_P].ns_tm;
        else
                return (B_TRUE);

        return (expire != 0 && tp.tv_sec > expire);
}

int
__s_get_enum_value(ns_config_t *ptr, char *value, ParamIndexType i)
{
        register ns_enum_map    *mapp;
        char                    *pstart = value;
        char                    *pend;
        int                     len;

        if (pstart == NULL)
                return (-1);

        /* skip leading spaces */
        while (*pstart == SPACETOK)
                pstart++;
        /* skip trailing spaces */
        pend = pstart + strlen(pstart) - 1;
        for (; pend >= pstart && *pend == SPACETOK; pend--)
                ;
        len = pend - pstart + 1;
        if (len == 0)
                return (-1);

        switch (i) {
        case NS_LDAP_AUTH_P:
                if (ptr->version == NS_LDAP_V1)
                        mapp = &ns_auth_enum_v1[0];
                else
                        mapp = &ns_auth_enum_v2[0];
                break;
        case NS_LDAP_TRANSPORT_SEC_P:
                return (-1);
        case NS_LDAP_SEARCH_SCOPE_P:
                if (ptr->version == NS_LDAP_V1)
                        mapp = &ns_scope_enum_v1[0];
                else
                        mapp = &ns_scope_enum_v2[0];
                break;
        case NS_LDAP_SEARCH_REF_P:
                if (ptr->version == NS_LDAP_V1)
                        mapp = &ns_ref_enum_v1[0];
                else
                        mapp = &ns_ref_enum_v2[0];
                break;
        case NS_LDAP_PREF_ONLY_P:
                mapp = &ns_pref_enum[0];
                break;
        case NS_LDAP_ENABLE_SHADOW_UPDATE_P:
                mapp = &ns_shadow_update_enum[0];
                break;
        case NS_LDAP_CREDENTIAL_LEVEL_P:
                if (ptr->version == NS_LDAP_V1)
                        return (-1);
                else
                        mapp = &ns_cred_enum_v2[0];
                break;
        case NS_LDAP_SERVICE_AUTH_METHOD_P:
                mapp = &ns_auth_enum_v2[0];
                break;
        case NS_LDAP_SERVICE_CRED_LEVEL_P:
                mapp = &ns_cred_enum_v2[0];
                break;
        default:
                return (-1);
        }

        for (; mapp->name != NULL; mapp++) {
                if (strncasecmp(pstart, mapp->name, len) == 0 &&
                    (strlen(mapp->name) == len)) {
                        return (mapp->value);
                }
        }
        return (-1);
}

char *
__s_get_auth_name(ns_config_t *ptr, AuthType_t type)
{
        register ns_enum_map    *mapp;

        if (ptr->version == NS_LDAP_V1)
                mapp = &ns_auth_enum_v1[0];
        else
                mapp = &ns_auth_enum_v2[0];

        for (; mapp->name != NULL; mapp++) {
                if (type == INT2AUTHENUM(mapp->value)) {
                        return (mapp->name);
                }
        }
        return ("Unknown AuthType_t type specified");
}


char *
__s_get_security_name(ns_config_t *ptr, TlsType_t type)
{
        register ns_enum_map    *mapp;

        if (ptr->version == NS_LDAP_V1) {
                mapp = &ns_sec_enum_v1[0];

                for (; mapp->name != NULL; mapp++) {
                        if (type == INT2SECENUM(mapp->value)) {
                                return (mapp->name);
                        }
                }
        }
        return ("Unknown TlsType_t type specified");
}


char *
__s_get_scope_name(ns_config_t *ptr, ScopeType_t type)
{
        register ns_enum_map    *mapp;

        if (ptr->version == NS_LDAP_V1)
                mapp = &ns_scope_enum_v1[0];
        else
                mapp = &ns_scope_enum_v2[0];

        for (; mapp->name != NULL; mapp++) {
                if (type == INT2SCOPEENUM(mapp->value)) {
                        return (mapp->name);
                }
        }
        return ("Unknown ScopeType_t type specified");
}


char *
__s_get_pref_name(PrefOnly_t type)
{
        register ns_enum_map    *mapp = &ns_pref_enum[0];

        for (; mapp->name != NULL; mapp++) {
                if (type == INT2PREFONLYENUM(mapp->value)) {
                        return (mapp->name);
                }
        }
        return ("Unknown PrefOnly_t type specified");
}

char *
__s_get_searchref_name(ns_config_t *ptr, SearchRef_t type)
{
        register ns_enum_map    *mapp;

        if (ptr->version == NS_LDAP_V1)
                mapp = &ns_ref_enum_v1[0];
        else
                mapp = &ns_ref_enum_v2[0];

        for (; mapp->name != NULL; mapp++) {
                if (type == INT2SEARCHREFENUM(mapp->value)) {
                        return (mapp->name);
                }
        }
        return ("Unknown SearchRef_t type specified");
}

char *
__s_get_shadowupdate_name(enableShadowUpdate_t type)
{
        register ns_enum_map    *mapp;

        mapp = &ns_shadow_update_enum[0];

        for (; mapp->name != NULL; mapp++) {
                if (type == INT2SHADOWUPDATENUM(mapp->value)) {
                        return (mapp->name);
                }
        }
        return ("Unknown enableShadowUpdate_t type specified");
}

static char *
__s_get_credlvl_name(ns_config_t *ptr, CredLevel_t type)
{
        register ns_enum_map    *mapp;

        if (ptr->version == NS_LDAP_V2) {
                mapp = &ns_cred_enum_v2[0];
                for (; mapp->name != NULL; mapp++) {
                        if (type == INT2CREDLEVELENUM(mapp->value)) {
                                return (mapp->name);
                        }
                }
        }
        return ("Unknown CredLevel_t type specified");
}

static void
destroy_param(ns_config_t *ptr, ParamIndexType type)
{
        int     i, j;
        char    **ppc;

        if (ptr == NULL)
                return;

        /*
         * This routine is not lock protected because
         * the config param it may be destroying is not
         * necessarily THE config.  Mutex protect elsewhere.
         */
        switch (ptr->paramList[type].ns_ptype) {
        case CHARPTR:
                if (ptr->paramList[type].ns_pc) {
                        free(ptr->paramList[type].ns_pc);
                        ptr->paramList[type].ns_pc = NULL;
                }
                break;
        case SAMLIST:
        case SCLLIST:
        case SSDLIST:
        case ARRAYCP:
        case SERVLIST:
                if (ptr->paramList[type].ns_ppc) {
                        ppc = ptr->paramList[type].ns_ppc;
                        j = ptr->paramList[type].ns_acnt;
                        for (i = 0; i < j && ppc[i] != NULL; i++) {
                                free((void *)ppc[i]);
                        }
                        free((void *)ppc);
                        ptr->paramList[type].ns_ppc = NULL;
                }
                break;
        case ARRAYAUTH:
        case ARRAYCRED:
                if (ptr->paramList[type].ns_pi) {
                        free(ptr->paramList[type].ns_pi);
                        ptr->paramList[type].ns_pi = NULL;
                }
                break;
        case INT:
                ptr->paramList[type].ns_i = 0;
                break;
        case ATTRMAP:
                break;
        case OBJMAP:
                break;
        default:
                break;
        }
        ptr->paramList[type].ns_ptype = NS_UNKNOWN;
}

static void
destroy_config(ns_config_t *ptr)
{
        ParamIndexType  i;

        if (ptr != NULL) {
                if (ptr == current_config)
                        current_config = NULL;
                free(ptr->domainName);
                ptr->domainName = NULL;
                for (i = 0; i <= LAST_VALUE; i++) {
                        destroy_param(ptr, i);
                }
                __s_api_destroy_hash(ptr);
                free(ptr);
        }
}

/*
 * Marks the ns_config_t to be deleted and then releases it. (If no other
 * caller is using, then __s_api_release_config will destroy it.)
 *
 * Note that __s_api_destroy_config should only be called if the caller has
 * created the ns_config_t with __s_api_create_config (with the exception
 * of set_curr_config). The ns_config_t should be private to the caller.
 *
 * This function should not be called with the current_config except by
 * set_curr_config which locks ns_parse_lock to ensure that no thread
 * will be waiting on current_config->config_mutex. This ensures that
 * no caller with be waiting on cfg->config_mutex while it is being
 * destroyed by __s_api_release_config.
 */

void
__s_api_destroy_config(ns_config_t *cfg)
{
        if (cfg != NULL) {
                (void) mutex_lock(&cfg->config_mutex);
                cfg->delete = TRUE;
                (void) mutex_unlock(&cfg->config_mutex);
                __s_api_release_config(cfg);
        }
}


/*
 * Increment the configuration use count by one - assumes ns_parse_lock has
 * been obtained.
 */

static ns_config_t *
get_curr_config_unlocked(ns_config_t *cfg, boolean_t global)
{
        ns_config_t *ret;

        ret = cfg;
        if (cfg != NULL) {
                (void) mutex_lock(&cfg->config_mutex);
                /*
                 * allow access to per connection management (non-global)
                 * config so operations on connection being closed can still
                 * be completed
                 */
                if (cfg->delete && global == B_TRUE)
                        ret = NULL;
                else
                        cfg->nUse++;
                (void) mutex_unlock(&cfg->config_mutex);
        }
        return (ret);
}

/*
 * set_curr_config_global sets the current global config to the
 * specified ns_config_t. Note that this function is similar
 * to the project private function __s_api_init_config_global
 * except that it does not release the new ns_config_t.
 */
static void
set_curr_config_global(ns_config_t *ptr)
{
        ns_config_t     *cfg;
        ns_config_t     *cur_cfg;

        (void) mutex_lock(&ns_parse_lock);
        cur_cfg = current_config;
        cfg = get_curr_config_unlocked(cur_cfg, B_TRUE);
        if (cfg != ptr) {
                __s_api_destroy_config(cfg);
                current_config = ptr;
        }
        (void) mutex_unlock(&ns_parse_lock);
}


/*
 * set_curr_config sets the current config or the per connection
 * management one to the specified ns_config_t. Note that this function
 * is similar to the project private function __s_api_init_config
 * except that it does not release the new ns_config_t. Also note
 * that if there's no per connection management one to set, the
 * global current config will be set.
 */

static void
set_curr_config(ns_config_t *ptr)
{
        ns_config_t     *cfg;
        ns_config_t     *cur_cfg;
        ns_conn_mgmt_t  *cmg;
        int             rc;

        rc = thr_getspecific(ns_cmgkey, (void **)&cmg);

        /* set the per connection management config if possible */
        if (rc == 0 && cmg != NULL && cmg->config != NULL) {
                (void) mutex_lock(&cmg->cfg_lock);
                cur_cfg = cmg->config;
                cfg = get_curr_config_unlocked(cur_cfg, B_FALSE);
                if (cfg != ptr) {
                        __s_api_destroy_config(cfg);
                        cmg->config = ptr;
                }
                (void) mutex_unlock(&cmg->cfg_lock);
                return;
        }

        /* else set the global current config */
        set_curr_config_global(ptr);
}

/*
 * Decrements the ns_config_t usage count by one. Delete if delete flag
 * is set and no other callers are using.
 */

void
__s_api_release_config(ns_config_t *cfg)
{
        if (cfg != NULL) {
                (void) mutex_lock(&cfg->config_mutex);
                cfg->nUse--;
                if (cfg->nUse == 0 && cfg->delete) {
                        destroy_config(cfg);
                } else
                        (void) mutex_unlock(&cfg->config_mutex);
        }
}

/*
 * __s_api_init_config function destroys the previous global configuration
 * sets the new global configuration and then releases it
 */
void
__s_api_init_config_global(ns_config_t *ptr)
{
        set_curr_config_global(ptr);
        __s_api_release_config(ptr);
}

/*
 * __s_api_init_config function destroys the previous configuration
 * sets the new configuration and then releases it. The configuration
 * may be the global one or the per connection management one.
 */
void
__s_api_init_config(ns_config_t *ptr)
{
        set_curr_config(ptr);
        __s_api_release_config(ptr);
}


/*
 * Create an ns_config_t, set the usage count to one
 */

ns_config_t *
__s_api_create_config(void)
{
        ns_config_t     *ret;
        ret = (ns_config_t *)calloc(1, sizeof (ns_config_t));
        if (ret == NULL)
                return (NULL);

        ret->domainName = __getdomainname();
        if (ret->domainName == NULL) {
                free(ret);
                return (NULL);
        }
        ret->version = NS_LDAP_V1;
        (void) mutex_init(&ret->config_mutex, USYNC_THREAD, NULL);
        ret->nUse = 1;
        ret->delete = B_FALSE;
        return (ret);
}

/*
 * __s_api_get_default_config_global returns the current global config
 */
ns_config_t *
__s_api_get_default_config_global(void)
{
        ns_config_t     *cfg;
        ns_config_t     *cur_cfg;

        (void) mutex_lock(&ns_parse_lock);
        cur_cfg = current_config;
        cfg = get_curr_config_unlocked(cur_cfg, B_TRUE);
        (void) mutex_unlock(&ns_parse_lock);

        return (cfg);
}

/*
 * __s_api_get_default_config returns the current global config or the
 * per connection management one.
 */
ns_config_t *
__s_api_get_default_config(void)
{
        ns_config_t     *cfg;
        ns_config_t     *cur_cfg;
        ns_conn_mgmt_t  *cmg;
        int             rc;

        rc = thr_getspecific(ns_cmgkey, (void **)&cmg);

        /* get the per connection management config if available */
        if (rc == 0 && cmg != NULL && cmg->config != NULL) {
                (void) mutex_lock(&cmg->cfg_lock);
                cur_cfg = cmg->config;
                cfg = get_curr_config_unlocked(cur_cfg, B_FALSE);
                (void) mutex_unlock(&cmg->cfg_lock);
                return (cfg);
        }

        /* else get the global current config */
        return (__s_api_get_default_config_global());
}

static char *
stripdup(const char *instr)
{
        char    *pstart = (char *)instr;
        char    *pend, *ret;
        int     len;

        if (pstart == NULL)
                return (NULL);
        /* remove leading spaces */
        while (*pstart == SPACETOK)
                pstart++;
        /* remove trailing spaces */
        pend = pstart + strlen(pstart) - 1;
        for (; pend >= pstart && *pend == SPACETOK; pend--)
                ;
        len = pend - pstart + 1;
        if ((ret = malloc(len + 1)) == NULL)
                return (NULL);
        if (len != 0) {
                (void) strncpy(ret, pstart, len);
        }
        ret[len] = '\0';
        return (ret);
}

/*
 * Note that __s_api_crosscheck is assumed to be called with an ns_config_t
 * that is properly protected - so that it will not change during the
 * duration of the call
 */

/* Size of errstr needs to be MAXERROR */
ns_parse_status
__s_api_crosscheck(ns_config_t *ptr, char *errstr, int check_dn)
{
        int             value, j;
        time_t          tm;
        const char      *str, *str1;
        int             i, cnt;
        int             self, gssapi;

        if (ptr == NULL)
                return (NS_SUCCESS);

        /* check for no server specified */
        if (ptr->paramList[NS_LDAP_SERVERS_P].ns_ppc == NULL) {
                if (ptr->version == NS_LDAP_V1) {
                        str = NULL_OR_STR(__s_api_get_configname(
                            NS_LDAP_SERVERS_P));
                        (void) snprintf(errstr, MAXERROR,
                            gettext("Configuration Error: No entry for "
                            "'%s' found"), str);
                        return (NS_PARSE_ERR);
                } else if (ptr->paramList[NS_LDAP_SERVER_PREF_P].ns_ppc ==
                    NULL) {
                        str = NULL_OR_STR(__s_api_get_configname(
                            NS_LDAP_SERVERS_P));
                        str1 = NULL_OR_STR(__s_api_get_configname(
                            NS_LDAP_SERVER_PREF_P));
                        (void) snprintf(errstr, MAXERROR,
                            gettext("Configuration Error: "
                            "Neither '%s' nor '%s' is defined"), str, str1);
                        return (NS_PARSE_ERR);
                }
        }
        if (ptr->paramList[NS_LDAP_CERT_PASS_P].ns_pc != NULL &&
            ptr->paramList[NS_LDAP_CERT_PATH_P].ns_pc == NULL) {
                        str = NULL_OR_STR(__s_api_get_configname(
                            NS_LDAP_CERT_PASS_P));
                        str1 = NULL_OR_STR(__s_api_get_configname(
                            NS_LDAP_CERT_PATH_P));
                        (void) snprintf(errstr, MAXERROR,
                        gettext("Configuration Error: %s specified "
                            "but no value for '%s' found"), str, str1);
                return (NS_PARSE_ERR);
        }
        if (ptr->paramList[NS_LDAP_CERT_PASS_P].ns_pc == NULL &&
            ptr->paramList[NS_LDAP_CERT_PATH_P].ns_pc != NULL) {
                        str = NULL_OR_STR(__s_api_get_configname(
                            NS_LDAP_CERT_PATH_P));
                        str1 = NULL_OR_STR(__s_api_get_configname(
                            NS_LDAP_CERT_PASS_P));
                        (void) snprintf(errstr, MAXERROR,
                        gettext("Configuration Error: %s specified "
                            "but no value for '%s' found"), str, str1);
                return (NS_PARSE_ERR);
        }
        /* check if search basedn has been specified */
        if (ptr->paramList[NS_LDAP_SEARCH_BASEDN_P].ns_ppc == NULL) {
                str = NULL_OR_STR(__s_api_get_configname(
                    NS_LDAP_SEARCH_BASEDN_P));
                (void) snprintf(errstr, MAXERROR,
                    gettext("Configuration Error: No entry for "
                    "'%s' found"), str);
                return (NS_PARSE_ERR);
        }

        if (check_dn) {
            /* check for auth value....passwd/bindn if necessary */

                for (j = 0; ptr->paramList[NS_LDAP_AUTH_P].ns_pi != NULL &&
                    ptr->paramList[NS_LDAP_AUTH_P].ns_pi[j] != 0; j++) {
                value = ptr->paramList[NS_LDAP_AUTH_P].ns_pi[j];
                switch (value) {
                case NS_LDAP_EA_SIMPLE:
                case NS_LDAP_EA_SASL_CRAM_MD5:
                case NS_LDAP_EA_SASL_DIGEST_MD5:
                case NS_LDAP_EA_SASL_DIGEST_MD5_INT:
                case NS_LDAP_EA_SASL_DIGEST_MD5_CONF:
                case NS_LDAP_EA_TLS_SIMPLE:
                case NS_LDAP_EA_TLS_SASL_CRAM_MD5:
                case NS_LDAP_EA_TLS_SASL_DIGEST_MD5:
                case NS_LDAP_EA_TLS_SASL_DIGEST_MD5_INT:
                case NS_LDAP_EA_TLS_SASL_DIGEST_MD5_CONF:
                        if (ptr->paramList[NS_LDAP_BINDDN_P].ns_ppc == NULL) {
                                str = NULL_OR_STR(__s_api_get_configname(
                                    NS_LDAP_BINDDN_P));
                                (void) snprintf(errstr, MAXERROR,
                                gettext("Configuration Error: No entry for "
                                    "'%s' found"), str);
                                return (NS_PARSE_ERR);
                        }
                        if (ptr->paramList[NS_LDAP_BINDPASSWD_P].ns_ppc
                            == NULL) {
                                str = NULL_OR_STR(__s_api_get_configname(
                                    NS_LDAP_BINDPASSWD_P));
                                (void) snprintf(errstr, MAXERROR,
                                gettext("Configuration Error: No entry for "
                                    "'%s' found"), str);
                                return (NS_PARSE_ERR);
                        }
                        break;
                }
                }
        }

        /*
         * If NS_LDAP_CACHETTL is not specified,
         * init NS_LDAP_EXP_P here. Otherwise,
         * ldap_cachemgr will never refresh the profile.
         * Set it to current time + default
         * NS_LDAP_CACHETTL
         */
        if (ptr->paramList[NS_LDAP_CACHETTL_P].ns_pc == NULL) {
                tm = conv_time(
                    defconfig[NS_LDAP_CACHETTL_P].defval.ns_pc);
                ptr->paramList[NS_LDAP_EXP_P].ns_ptype = TIMET;
                if (tm != 0) {
                        tm += time(NULL);
                }
                ptr->paramList[NS_LDAP_EXP_P].ns_tm = tm;
        }
        /*
         * If credential level self is defined, there should be
         * at least an auth method sasl/GSSAPI and vice versa.
         */
        self = 0;
        cnt = ptr->paramList[NS_LDAP_CREDENTIAL_LEVEL_P].ns_acnt;
        for (i = 0; i < cnt; i++) {
                if (ptr->paramList[NS_LDAP_CREDENTIAL_LEVEL_P].ns_pi[i] ==
                    NS_LDAP_CRED_SELF)
                        self++;
        }
        gssapi = 0;
        cnt = ptr->paramList[NS_LDAP_AUTH_P].ns_acnt;
        for (i = 0; i < cnt; i++) {
                if (ptr->paramList[NS_LDAP_AUTH_P].ns_pi[i] ==
                    NS_LDAP_EA_SASL_GSSAPI)
                        gssapi++;
        }
        if (gssapi == 0 && self > 0) {
                (void) snprintf(errstr, MAXERROR,
                    gettext("Configuration Error: "
                    "Credential level self requires "
                    "authentication method sasl/GSSAPI"));
                return (NS_PARSE_ERR);
        }
        if (gssapi > 0 && self == 0) {
                (void) snprintf(errstr, MAXERROR,
                    gettext("Configuration Error: "
                    "Authentication method sasl/GSSAPI "
                    "requires credential level self"));
                return (NS_PARSE_ERR);
        }
        return (NS_SUCCESS);
}


int
__s_api_get_type(const char *value, ParamIndexType *type)
{
        int     i;

        for (i = 0; defconfig[i].name != NULL; i++) {
                if (strcasecmp(defconfig[i].name, value) == 0) {
                        *type = defconfig[i].index;
                        return (0);
                }
        }
        return (-1);
}

/*
 * Externally defined version of get_type.
 * Includes extra error checking
 */

int
__ns_ldap_getParamType(const char *value, ParamIndexType *type)
{
        if (value == NULL || type == NULL)
                return (-1);
        return (__s_api_get_type(value, type));
}

int
__s_api_get_versiontype(ns_config_t *ptr, char *value, ParamIndexType *type)
{
        ns_version_t    ver;
        int             i;

        if (ptr == NULL)
                return (-1);

        ver = ptr->version;

        for (i = 0; defconfig[i].name != NULL; i++) {
                if (strcasecmp(defconfig[i].name, value) == 0) {
                        if (defconfig[i].version == ver) {
                                *type = defconfig[i].index;
                                return (0);
                        }
                }
        }
        return (-1);
}

int
__s_api_get_profiletype(char *value, ParamIndexType *type)
{
        int     i;

        for (i = 0; defconfig[i].name != NULL; i++) {
                if (defconfig[i].profile_name == NULL)
                        continue;
                if (strcasecmp(defconfig[i].profile_name, value) == 0) {
                        *type = defconfig[i].index;
                        return (0);
                }
        }
        return (-1);
}

int
__s_api_get_configtype(ParamIndexType type)
{
        int i;

        for (i = 0; defconfig[i].name != NULL; i++) {
                if (defconfig[i].index == type) {
                        return (defconfig[i].config_type);
                }
        }
        return (-1);
}

const char *
__s_api_get_configname(ParamIndexType type)
{
        int i;

        for (i = 0; defconfig[i].name != NULL; i++) {
                if (defconfig[i].index == type) {
                        if (defconfig[i].name[0] == '\0')
                                return (NULL);
                        else
                                return (defconfig[i].name);
                }
        }
        return (NULL);
}

static ns_default_config *
get_defconfig(ns_config_t *ptr, ParamIndexType type)
{
        ns_version_t    ver;
        int             i;

        ver = ptr->version;

        for (i = 0; defconfig[i].name != NULL; i++) {
                if (defconfig[i].index == type &&
                    defconfig[i].version == ver) {
                        return (&defconfig[i]);
                }
        }
        return (NULL);
}

static int
set_default_value(ns_config_t *configptr, char *name,
    char *value, ns_ldap_error_t **error)
{
        ParamIndexType  i;
        int             ret;
        char            errstr[MAXERROR];

        if (__s_api_get_type(name, &i) < 0) {
                (void) snprintf(errstr, sizeof (errstr), gettext(
                    "Illegal type name (%s).\n"), name);
                MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr),
                    NS_LDAP_MEMORY);
                return (NS_LDAP_CONFIG);
        }

        if (i != NS_LDAP_SERVERS_P &&
            i != NS_LDAP_SERVICE_AUTH_METHOD_P &&
            i != NS_LDAP_SERVICE_CRED_LEVEL_P &&
            i != NS_LDAP_SERVICE_SEARCH_DESC_P &&
            i != NS_LDAP_SERVER_PREF_P &&
            i != NS_LDAP_SEARCH_DN_P) {
                if (configptr->paramList[i].ns_ptype != NS_UNKNOWN) {
                        destroy_param(configptr, i);
                }
        }

        ret = __ns_ldap_setParamValue(configptr, i, value, error);
        return (ret);
}


/*
 * Initialize config to a default state
 * By default leave configuration empty
 * getParam will automatically get the
 * appropriate default value if none exists
 */

void
__ns_ldap_default_config()
{
        ns_config_t     *ptr;

        ptr = __s_api_create_config();
        if (ptr == NULL)
                return;

        set_curr_config(ptr);
        __s_api_release_config(ptr);
}

/*
 * Get the current configuration pointer and return it.
 * If necessary initialize or refresh the current
 * configuration as applicable. If global is set, returns
 * the global one.
 */

static ns_config_t *
loadrefresh_config(boolean_t global)
{
        ns_config_t             *cfg;
        ns_config_t             *new_cfg;
        ns_ldap_error_t         *errorp;

        /* We want to refresh only one configuration at a time */
        (void) mutex_lock(&ns_loadrefresh_lock);
        if (global == B_TRUE)
                cfg = __s_api_get_default_config_global();
        else
                cfg = __s_api_get_default_config();

        /* (re)initialize configuration if necessary */
        if (!__s_api_isStandalone() && timetorefresh(cfg)) {
                new_cfg = LoadCacheConfiguration(cfg, &errorp);
                if (new_cfg != NULL && new_cfg != cfg) {
                        __s_api_release_config(cfg);
                        if (global == B_TRUE)
                                set_curr_config_global(new_cfg);
                        else
                                set_curr_config(new_cfg);
                        cfg = new_cfg;
                }
                if (errorp != NULL)
                        (void) __ns_ldap_freeError(&errorp);
        }
        (void) mutex_unlock(&ns_loadrefresh_lock);
        return (cfg);
}

/*
 * Get the current global configuration pointer and return it.
 * If necessary initialize or refresh the current
 * configuration as applicable.
 */

ns_config_t *
__s_api_loadrefresh_config_global()
{
        return (loadrefresh_config(B_TRUE));
}

/*
 * Get the current configuration pointer and return it.
 * If necessary initialize or refresh the current
 * configuration as applicable. The configuration may
 * be the global one or the per connection management one.
 */

ns_config_t *
__s_api_loadrefresh_config()
{
        return (loadrefresh_config(B_FALSE));
}

/*
 * In general this routine is not very usefull. Individual routines can be
 * created to do this job.  Once that is done, this function can be removed.
 * Size of errstr buffer needs to be MAXERROR.
 */
static ns_parse_status
verify_value(ns_config_t *cfg, char *name, char *value, char *errstr)
{
        ParamIndexType  index = 0;
        int             found = 0, j;
        char            *ptr = NULL, *strptr = NULL, buffer[BUFSIZE];
        char            *rest;
        ns_default_config       *def = NULL;

        if (__s_api_get_type(name, &index) != 0) {
                (void) snprintf(errstr, MAXERROR,
                    gettext("Unknown keyword encountered '%s'."), name);
                return (NS_PARSE_ERR);
        }

        def = get_defconfig(cfg, index);

        /* eat up beginning quote, if any */
        while (value != NULL && (*value == QUOTETOK || *value == SPACETOK))
                value++;

        /* eat up space/quote at end of value */
        if (strlen(value) > 0)
                ptr = value + strlen(value) - 1;
        else
                ptr = value;
        for (; ptr != value && (*ptr == SPACETOK || *ptr == QUOTETOK); ptr--) {
                *ptr = '\0';
        }

        switch (index) {
        case NS_LDAP_EXP_P:
        case NS_LDAP_CACHETTL_P:
        case NS_LDAP_CERT_PATH_P:
        case NS_LDAP_CERT_PASS_P:
        case NS_LDAP_CERT_NICKNAME_P:
        case NS_LDAP_BINDDN_P:
        case NS_LDAP_BINDPASSWD_P:
        case NS_LDAP_ADMIN_BINDDN_P:
        case NS_LDAP_ADMIN_BINDPASSWD_P:
        case NS_LDAP_DOMAIN_P:
        case NS_LDAP_SEARCH_BASEDN_P:
        case NS_LDAP_SEARCH_TIME_P:
        case NS_LDAP_PROFILE_P:
        case NS_LDAP_AUTH_P:
        case NS_LDAP_SEARCH_SCOPE_P:
        case NS_LDAP_CREDENTIAL_LEVEL_P:
        case NS_LDAP_SERVICE_SEARCH_DESC_P:
        case NS_LDAP_BIND_TIME_P:
        case NS_LDAP_ATTRIBUTEMAP_P:
        case NS_LDAP_OBJECTCLASSMAP_P:
        case NS_LDAP_SERVICE_AUTH_METHOD_P:
        case NS_LDAP_SERVICE_CRED_LEVEL_P:
        case NS_LDAP_HOST_CERTPATH_P:
                break;
        case NS_LDAP_SEARCH_DN_P:
                /* depreciated because of service descriptors */
                /* Parse as appropriate at descriptor create time */
                break;
        case NS_LDAP_FILE_VERSION_P:
                if (value != NULL &&
                    strcasecmp(value, NS_LDAP_VERSION_1) != 0 &&
                    strcasecmp(value, NS_LDAP_VERSION_2) != 0) {
                        (void) snprintf(errstr, MAXERROR,
                            gettext("Version mismatch, expected "
                            "cache version '%s' or '%s' but "
                            "encountered version '%s'."),
                            NS_LDAP_VERSION_1,
                            NS_LDAP_VERSION_2, value);
                                return (NS_PARSE_ERR);
                }
                break;
        case NS_LDAP_SERVERS_P:
        case NS_LDAP_SERVER_PREF_P:
                (void) strcpy(buffer, value);
                strptr = strtok_r(buffer, ",", &rest);
                while (strptr != NULL) {
                        char    *tmp = NULL;
                        tmp = stripdup(strptr);
                        if (tmp == NULL || (strchr(tmp, ' ') != NULL)) {
                                (void) snprintf(errstr, MAXERROR,
                                    gettext("Invalid parameter values "
                                    "'%s' specified for keyword '%s'."),
                                    tmp, name);
                                free(tmp);
                                return (NS_PARSE_ERR);
                        }
                        free(tmp);
                        strptr = strtok_r(NULL, ",", &rest);
                }
                break;
        default:
                found = 0; j = 0;
                while (def->allowed != NULL &&
                    def->allowed[j].name != NULL && j < DEFMAX) {
                        if (strcmp(def->allowed[j].name,
                            value) == 0) {
                                found = 1;
                                break;
                        }
                        j++;
                }
                if (!found) {
                        (void) snprintf(errstr, MAXERROR,
                            gettext("Invalid option specified for "
                            "'%s' keyword. '%s' is not a recognized "
                            "keyword value."), name, value);
                        return (NS_PARSE_ERR);
                }
        }

        return (NS_SUCCESS);
}

void
__s_api_split_key_value(char *buffer, char **name, char **value)
{
        char    *ptr;

        *name = buffer;
        /* split into name value pair */
        if ((ptr = strchr(buffer, TOKENSEPARATOR)) != NULL) {
                *ptr = '\0';
                ptr++;
                /* trim whitespace */
                while (*ptr == SPACETOK)
                        ptr++;
                *value = ptr;
        }
}

/*
 * Set a parameter value in a generic configuration structure
 * Assume any necessary locks are in place.  This routine would
 * be better named: __ns_ldap_translateString2Param
 *
 * This routine translates external string format into internal
 * param format and saves the result in the param table.
 */
int
__ns_ldap_setParamValue(ns_config_t *ptr, const ParamIndexType type,
    const void *data, ns_ldap_error_t **error)
{
        ns_default_config       *def = NULL;
        ns_param_t              conf;
        ns_mapping_t            *map, *rmap;
        int                     i, j, len;
        char                    *cp, *cp2, *end;
        char                    *tcp = NULL;
        char                    errstr[2 * MAXERROR];
        char                    tbuf[100], *ptbuf;
        char                    *sid, *origA, **mapA;
        char                    **attr;
        time_t                  tm;
        int                     free_memory, exitrc;
        char                    **p;

        /* Find ParamIndexType default configuration data */
        def = get_defconfig(ptr, type);
        if (def == NULL) {
                (void) snprintf(errstr, sizeof (errstr),
                    gettext("Unable to set value: "
                    "invalid ParamIndexType (%d)"), type);
                MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr),
                    NS_LDAP_MEMORY);
                return (NS_LDAP_CONFIG);
        }

        (void) memset(&conf, 0, sizeof (conf));

        /* data is actually const char */
        cp = (char *)data;

        /* eat up beginning quote, if any */
        while (cp && (*cp == QUOTETOK || *cp == SPACETOK))
                cp++;

        /* eat up space/quote at end of value */
        end = cp2 = cp + strlen(cp) - 1;
        for (; cp2 > cp && (*cp2 == SPACETOK || *cp2 == QUOTETOK); cp2--)
                ;
        /* data is const, must duplicate */
        if (cp2 != end) {
                tcp = (char *)calloc((int)(cp2 - cp + 2), sizeof (char));
                if (tcp == NULL)
                        return (NS_LDAP_MEMORY);
                end = cp2;
                cp2 = tcp;
                while (cp <= end) {
                        *cp2++ = *cp++;
                }
                *cp2 = '\0';
                cp = tcp;
        }

        /* Parse data according to type */
        switch (def->data_type) {
        case INT:
                switch (def->index) {
                case NS_LDAP_PREF_ONLY_P:
                case NS_LDAP_SEARCH_REF_P:
                case NS_LDAP_SEARCH_SCOPE_P:
                case NS_LDAP_ENABLE_SHADOW_UPDATE_P:
                        i = __s_get_enum_value(ptr, cp, def->index);
                        if (i < 0) {
                                (void) snprintf(errstr, sizeof (errstr),
                                    gettext("Unable to set value: "
                                    "invalid %s (%d)"), def->name,
                                    def->index);
                                MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
                                    strdup(errstr), NS_LDAP_MEMORY);
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_CONFIG);
                        }
                        conf.ns_i = i;
                        break;
                case NS_LDAP_TRANSPORT_SEC_P:   /* ignore TRANSPORT_SEC */
                        break;
                default:
                        cp2 = cp;
                        if ((*cp2 == '+') || (*cp2 == '-'))
                                cp2++;
                        for (/* empty */; *cp2; cp2++) {
                                if (isdigit(*cp2))
                                        continue;

                                (void) snprintf(errstr, sizeof (errstr),
                                    gettext("Unable to set value: "
                                    "invalid %s (%d)"), def->name,
                                    def->index);
                                MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
                                    strdup(errstr), NS_LDAP_MEMORY);
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_CONFIG);
                        }
                        i = atoi(cp);
                        conf.ns_i = i;
                        break;
                }
                break;
        case TIMET:
                /* Do nothing with a TIMET.  Initialize it below */
                break;
        case CHARPTR:
                conf.ns_pc = (char *)strdup(cp);
                if (conf.ns_pc == NULL) {
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_MEMORY);
                }
                break;
        case SAMLIST:
                /* first check to see if colon (:) is there */
                if ((strchr(cp, COLONTOK)) == NULL) {
                        (void) snprintf(errstr, sizeof (errstr),
                            gettext("Unable to set value: "
                            "invalid serviceAuthenticationMethod (%s)"),
                            cp);
                        MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
                            strdup(errstr), NS_LDAP_MEMORY);
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_CONFIG);
                }
                /* Appends an entry to the existing list */
                if (ptr->paramList[type].ns_ptype != SAMLIST) {
                        conf.ns_ppc = (char **)calloc(2, sizeof (char *));
                        if (conf.ns_ppc == NULL) {
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        conf.ns_acnt = 1;
                        conf.ns_ppc[0] = (char *)strdup(cp);
                        if (conf.ns_ppc[0] == NULL) {
                                free(conf.ns_ppc);
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                } else {
                        char *dp, *dpend;
                        int fnd = 0;

                        /* Attempt to replace if possible */
                        dpend = strchr(cp, COLONTOK);
                        len = dpend - cp;
                        dp = (char *)malloc(len+1);
                        if (dp == NULL) {
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        (void) strlcpy(dp, cp, len+1);
                        fnd = 0;
                        for (j = 0; j < ptr->paramList[type].ns_acnt; j++) {
                                dpend = strchr(ptr->paramList[type].ns_ppc[j],
                                    COLONTOK);
                                if (dpend == NULL)
                                        continue;
                                i = dpend - ptr->paramList[type].ns_ppc[j];
                                if (i != len)
                                        continue;
                                if (strncmp(ptr->paramList[type].ns_ppc[j],
                                    dp, len) == 0) {
                                        conf.ns_acnt =
                                            ptr->paramList[type].ns_acnt;
                                        conf.ns_ppc =
                                            ptr->paramList[type].ns_ppc;
                                        ptr->paramList[type].ns_ppc = NULL;
                                        free(conf.ns_ppc[j]);
                                        conf.ns_ppc[j] = (char *)strdup(cp);
                                        if (conf.ns_ppc[j] == NULL) {
                                                free(dp);
                                                __s_api_free2dArray
                                                    (conf.ns_ppc);
                                                if (tcp != NULL)
                                                        free(tcp);
                                                return (NS_LDAP_MEMORY);
                                        }
                                        fnd = 1;
                                        break;
                                }
                        }
                        free(dp);

                        if (fnd)
                                break;  /* Replaced completed */

                        /* Append */
                        len = ptr->paramList[type].ns_acnt + 1;
                        if (len > 1) {
                                p = (char **)dupParam(&ptr->paramList[type]);
                                if (p == NULL) {
                                        if (tcp != NULL)
                                                free(tcp);
                                        return (NS_LDAP_MEMORY);
                                }
                        } else
                                p = NULL;
                        conf.ns_ppc =
                            (char **)realloc(p, (len+1) * sizeof (char *));
                        if (conf.ns_ppc == NULL) {
                                __s_api_free2dArray(p);
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        conf.ns_acnt = len;
                        conf.ns_ppc[len-1] = (char *)strdup(cp);
                        if (conf.ns_ppc[len-1] == NULL) {
                                __s_api_free2dArray(conf.ns_ppc);
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        conf.ns_ppc[len] = NULL;
                }
                break;
        case SCLLIST:
                /* first check to see if colon (:) is there */
                if ((strchr(cp, COLONTOK)) == NULL) {
                        (void) snprintf(errstr, sizeof (errstr),
                            gettext("Unable to set value: "
                            "invalid serviceCredentialLevel (%s)"),
                            cp);
                        MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
                            strdup(errstr), NS_LDAP_MEMORY);
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_CONFIG);
                }
                /* Appends an entry to the existing list */
                if (ptr->paramList[type].ns_ptype != SCLLIST) {
                        conf.ns_ppc = (char **)calloc(2, sizeof (char *));
                        if (conf.ns_ppc == NULL) {
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        conf.ns_acnt = 1;
                        conf.ns_ppc[0] = (char *)strdup(cp);
                        if (conf.ns_ppc[0] == NULL) {
                                free(conf.ns_ppc);
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                } else {
                        char *dp, *dpend;
                        int fnd = 0;

                        /* Attempt to replace if possible */
                        dpend = strchr(cp, COLONTOK);
                        len = dpend - cp;
                        dp = (char *)malloc(len+1);
                        if (dp == NULL) {
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        (void) strlcpy(dp, cp, len+1);
                        fnd = 0;
                        for (j = 0; j < ptr->paramList[type].ns_acnt; j++) {
                                dpend = strchr(ptr->paramList[type].ns_ppc[j],
                                    COLONTOK);
                                if (dpend == NULL)
                                        continue;
                                i = dpend - ptr->paramList[type].ns_ppc[j];
                                if (i != len)
                                        continue;
                                if (strncmp(ptr->paramList[type].ns_ppc[j],
                                    dp, len) == 0) {
                                        conf.ns_acnt =
                                            ptr->paramList[type].ns_acnt;
                                        conf.ns_ppc =
                                            ptr->paramList[type].ns_ppc;
                                        ptr->paramList[type].ns_ppc = NULL;
                                        free(conf.ns_ppc[j]);
                                        conf.ns_ppc[j] = (char *)strdup(cp);
                                        if (conf.ns_ppc[j] == NULL) {
                                                free(dp);
                                                __s_api_free2dArray
                                                    (conf.ns_ppc);
                                                if (tcp != NULL)
                                                        free(tcp);
                                                return (NS_LDAP_MEMORY);
                                        }
                                        fnd = 1;
                                        break;
                                }
                        }
                        free(dp);

                        if (fnd)
                                break;  /* Replaced completed */

                        /* Append */
                        len = ptr->paramList[type].ns_acnt + 1;
                        if (len > 1) {
                                p = (char **)dupParam(&ptr->paramList[type]);
                                if (p == NULL) {
                                        if (tcp != NULL)
                                                free(tcp);
                                        return (NS_LDAP_MEMORY);
                                }
                        } else
                                p = NULL;
                        conf.ns_ppc =
                            (char **)realloc(p, (len+1) * sizeof (char *));
                        if (conf.ns_ppc == NULL) {
                                __s_api_free2dArray(p);
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        conf.ns_acnt = len;
                        conf.ns_ppc[len-1] = (char *)strdup(cp);
                        if (conf.ns_ppc[len-1] == NULL) {
                                __s_api_free2dArray(conf.ns_ppc);
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        conf.ns_ppc[len] = NULL;
                }
                break;
        case SSDLIST:
                /*
                 * first check to see if colon (:) is there,
                 * if so, make sure the serviceId is specified,
                 * i.e., colon is not the first character
                 */
                if ((strchr(cp, COLONTOK)) == NULL || *cp == COLONTOK) {
                        (void) snprintf(errstr, sizeof (errstr),
                            gettext("Unable to set value: "
                            "invalid serviceSearchDescriptor (%s)"),
                            cp);
                        MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
                            strdup(errstr), NS_LDAP_MEMORY);
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_CONFIG);
                }
                /* Appends an entry to the existing list */
                if (ptr->paramList[type].ns_ptype != SSDLIST) {
                        conf.ns_ppc = (char **)calloc(2, sizeof (char *));
                        if (conf.ns_ppc == NULL) {
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        conf.ns_acnt = 1;
                        conf.ns_ppc[0] = (char *)strdup(cp);
                        if (conf.ns_ppc[0] == NULL) {
                                free(conf.ns_ppc);
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                } else {
                        char *dp, *dpend;
                        int fnd = 0;

                        /* Attempt to replace if possible */
                        dpend = strchr(cp, COLONTOK);
                        len = dpend - cp;
                        dp = (char *)malloc(len+1);
                        if (dp == NULL) {
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        (void) strlcpy(dp, cp, len+1);
                        fnd = 0;
                        for (j = 0; j < ptr->paramList[type].ns_acnt; j++) {
                                dpend = strchr(ptr->paramList[type].ns_ppc[j],
                                    COLONTOK);
                                if (dpend == NULL)
                                        continue;
                                i = dpend - ptr->paramList[type].ns_ppc[j];
                                if (i != len)
                                        continue;
                                if (strncmp(ptr->paramList[type].ns_ppc[j],
                                    dp, len) == 0) {
                                        conf.ns_acnt =
                                            ptr->paramList[type].ns_acnt;
                                        conf.ns_ppc =
                                            ptr->paramList[type].ns_ppc;
                                        ptr->paramList[type].ns_ppc = NULL;
                                        free(conf.ns_ppc[j]);
                                        conf.ns_ppc[j] = (char *)strdup(cp);
                                        if (conf.ns_ppc[j] == NULL) {
                                                free(dp);
                                                __s_api_free2dArray
                                                    (conf.ns_ppc);
                                                if (tcp != NULL)
                                                        free(tcp);
                                                return (NS_LDAP_MEMORY);
                                        }
                                        fnd = 1;
                                        break;
                                }
                        }
                        free(dp);

                        if (fnd)
                                break;  /* Replaced completed */

                        /* Append */
                        len = ptr->paramList[type].ns_acnt + 1;
                        if (len > 1) {
                                p = (char **)dupParam(&ptr->paramList[type]);
                                if (p == NULL) {
                                        if (tcp != NULL)
                                                free(tcp);
                                        return (NS_LDAP_MEMORY);
                                }
                        } else
                                p = NULL;
                        conf.ns_ppc =
                            (char **)realloc(p, (len+1) * sizeof (char *));
                        if (conf.ns_ppc == NULL) {
                                __s_api_free2dArray(p);
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        conf.ns_acnt = len;
                        conf.ns_ppc[len-1] = (char *)strdup(cp);
                        if (conf.ns_ppc[len-1] == NULL) {
                                __s_api_free2dArray(conf.ns_ppc);
                                if (tcp != NULL)
                                        free(tcp);
                                return (NS_LDAP_MEMORY);
                        }
                        conf.ns_ppc[len] = NULL;
                }
                break;
        case ARRAYCP:
                len = 0;
                for (cp2 = cp; *cp2; cp2++) {
                        if (*cp2 == COMMATOK)
                                len++;
                }
                if (cp != cp2)
                        len++;
                if (len == 0) {
                        conf.ns_ppc = (char **)NULL;
                        conf.ns_acnt = 0;
                        break;
                }
                conf.ns_ppc = (char **)calloc(len + 1, sizeof (char *));
                if (conf.ns_ppc == NULL) {
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_MEMORY);
                }
                conf.ns_acnt = len;
                i = 0;
                for (cp2 = cp; *cp2; cp2++) {
                        if (*cp2 == COMMATOK) {
                                j = cp2 - cp + 1;
                                conf.ns_ppc[i] = (char *)malloc(j + 1);
                                if (conf.ns_ppc[i] == NULL) {
                                        __s_api_free2dArray(conf.ns_ppc);
                                        if (tcp != NULL)
                                                free(tcp);
                                        return (NS_LDAP_MEMORY);
                                }
                                (void) strlcpy(conf.ns_ppc[i], cp, j);
                                cp = cp2+1;
                                while (*cp == SPACETOK || *cp == COMMATOK)
                                        cp++;
                                cp2 = cp - 1;
                                i++;
                        }
                }
                j = cp2 - cp + 1;
                conf.ns_ppc[i] = (char *)malloc(j + 1);
                if (conf.ns_ppc[i] == NULL) {
                        __s_api_free2dArray(conf.ns_ppc);
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_MEMORY);
                }
                (void) strlcpy(conf.ns_ppc[i], cp, j);
                break;
        case SERVLIST:
                len = 0;
                for (cp2 = cp; *cp2; cp2++) {
                        if (*cp2 == SPACETOK || *cp2 == COMMATOK) {
                                len++;
                                for (; *(cp2 + 1) == SPACETOK ||
                                    *(cp2 +1) == COMMATOK; cp2++)
                                        ;
                        }
                }
                if (cp != cp2)
                        len++;
                if (len == 0) {
                        conf.ns_ppc = (char **)NULL;
                        conf.ns_acnt = 0;
                        break;
                }
                conf.ns_ppc = (char **)calloc(len + 1, sizeof (char *));
                if (conf.ns_ppc == NULL) {
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_MEMORY);
                }
                conf.ns_acnt = len;
                i = 0;
                for (cp2 = cp; *cp2; cp2++) {
                        if (*cp2 == SPACETOK || *cp2 == COMMATOK) {
                                j = cp2 - cp + 1;
                                conf.ns_ppc[i] = (char *)malloc(j + 1);
                                if (conf.ns_ppc[i] == NULL) {
                                        __s_api_free2dArray(conf.ns_ppc);
                                        if (tcp != NULL)
                                                free(tcp);
                                        return (NS_LDAP_MEMORY);
                                }
                                (void) strlcpy(conf.ns_ppc[i], cp, j);
                                cp = cp2+1;
                                while (*cp == SPACETOK || *cp == COMMATOK)
                                        cp++;
                                cp2 = cp - 1;
                                i++;
                        }
                }
                j = cp2 - cp + 1;
                conf.ns_ppc[i] = (char *)malloc(j + 1);
                if (conf.ns_ppc[i] == NULL) {
                        __s_api_free2dArray(conf.ns_ppc);
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_MEMORY);
                }
                (void) strlcpy(conf.ns_ppc[i], cp, j);
                break;
        case ARRAYAUTH:
                len = 0;
                for (cp2 = cp; *cp2; cp2++) {
                        if (*cp2 == SEMITOK || *cp2 == COMMATOK)
                                len++;
                }
                if (cp != cp2)
                        len++;
                if (len == 0) {
                        conf.ns_pi = (int *)NULL;
                        conf.ns_acnt = 0;
                        break;
                }
                conf.ns_pi = (int *)calloc(len + 1, sizeof (int));
                if (conf.ns_pi == NULL) {
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_MEMORY);
                }
                conf.ns_acnt = len;
                i = 0;
                for (cp2 = cp; *cp2; cp2++) {
                        if (*cp2 == SEMITOK || *cp2 == COMMATOK) {
                                j = cp2 - cp + 1;
                                if (j > sizeof (tbuf)) {
                                        j = -1;
                                        ptbuf = cp;
                                } else {
                                        (void) strlcpy(tbuf, cp, j);
                                        j = __s_get_enum_value(ptr, tbuf,
                                            def->index);
                                        ptbuf = tbuf;
                                }
                                if (j < 0) {
                                        (void) snprintf(errstr, sizeof (errstr),
                                            gettext("Unable to set value: "
                                            "invalid "
                                            "authenticationMethod (%s)"),
                                            ptbuf);
                                        MKERROR(LOG_ERR, *error,
                                            NS_CONFIG_SYNTAX,
                                            strdup(errstr), NS_LDAP_MEMORY);
                                        free(conf.ns_pi);
                                        if (tcp != NULL)
                                                free(tcp);
                                        return (NS_LDAP_CONFIG);
                                }
                                conf.ns_pi[i] = j;
                                cp = cp2+1;
                                i++;
                        }
                }
                j = cp2 - cp + 1;
                if (j > sizeof (tbuf)) {
                        j = -1;
                        ptbuf = cp;
                } else {
                        (void) strlcpy(tbuf, cp, j);
                        j = __s_get_enum_value(ptr, tbuf, def->index);
                        ptbuf = tbuf;
                }
                if (j < 0) {
                        (void) snprintf(errstr, sizeof (errstr),
                            gettext("Unable to set value: "
                            "invalid authenticationMethod (%s)"), ptbuf);
                        MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
                            strdup(errstr), NS_LDAP_MEMORY);
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_CONFIG);
                }
                conf.ns_pi[i] = j;
                break;
        case ARRAYCRED:
                len = 0;
                for (cp2 = cp; *cp2; cp2++) {
                        if (*cp2 == SPACETOK)
                                len++;
                }
                if (cp != cp2)
                        len++;
                if (len == 0) {
                        conf.ns_pi = (int *)NULL;
                        conf.ns_acnt = 0;
                        break;
                }
                conf.ns_pi = (int *)calloc(len + 1, sizeof (int));
                if (conf.ns_pi == NULL) {
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_MEMORY);
                }
                conf.ns_acnt = len;
                i = 0;
                for (cp2 = cp; *cp2; cp2++) {
                        if (*cp2 == SPACETOK) {
                                j = cp2 - cp + 1;
                                if (j > sizeof (tbuf)) {
                                        j = -1;
                                        ptbuf = cp;
                                } else {
                                        (void) strlcpy(tbuf, cp, j);
                                        j = __s_get_enum_value(ptr, tbuf,
                                            def->index);
                                        ptbuf = tbuf;
                                }
                                if (j < 0) {
                                        (void) snprintf(errstr, sizeof (errstr),
                                            gettext("Unable to set value: "
                                            "invalid credentialLevel (%s)"),
                                            ptbuf);
                                        MKERROR(LOG_ERR, *error,
                                            NS_CONFIG_SYNTAX,
                                            strdup(errstr), NS_LDAP_MEMORY);
                                        free(conf.ns_pi);
                                        if (tcp != NULL)
                                                free(tcp);
                                        return (NS_LDAP_CONFIG);
                                }
                                conf.ns_pi[i] = j;
                                cp = cp2+1;
                                i++;
                        }
                }
                j = cp2 - cp + 1;
                if (j > sizeof (tbuf)) {
                        j = -1;
                        ptbuf = cp;
                } else {
                        (void) strlcpy(tbuf, cp, j);
                        j = __s_get_enum_value(ptr, tbuf, def->index);
                        ptbuf = tbuf;
                }
                if (j < 0) {
                        (void) snprintf(errstr, sizeof (errstr),
                            gettext("Unable to set value: "
                            "invalid credentialLevel (%s)"), ptbuf);
                        MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
                            strdup(errstr), NS_LDAP_MEMORY);
                        if (tcp != NULL)
                                free(tcp);
                        return (NS_LDAP_CONFIG);
                }
                conf.ns_pi[i] = j;
                break;
        case ATTRMAP:
        case OBJMAP:
                i = __s_api_parse_map(cp, &sid, &origA, &mapA);
                if (i != NS_HASH_RC_SUCCESS) {
                        if (i == NS_HASH_RC_NO_MEMORY) {
                                exitrc = NS_LDAP_MEMORY;
                        } else {
                                (void) snprintf(errstr, sizeof (errstr),
                                gettext("Unable to set value: "
                                "invalid schema mapping (%s)"), cp);
                                exitrc = NS_LDAP_CONFIG;
                                MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX,
                                    strdup(errstr), NS_LDAP_MEMORY);
                        }
                        if (tcp)
                                free(tcp);
                        return (exitrc);
                }

                /*
                 * Add reverse map first.
                 * There could be more than one.
                 */
                for (attr = mapA; *attr; attr++) {

                        free_memory = 1;
                        exitrc = NS_LDAP_MEMORY;

                        rmap = (ns_mapping_t *)calloc(1,
                            sizeof (ns_mapping_t));
                        if (rmap) {
                                rmap->service = strdup(sid);
                                if (rmap->service) {
                                        rmap->orig = strdup(*attr);
                                        if (rmap->orig) {
                                                rmap->map = (char **)calloc(2,
                                                    sizeof (char *));
                                                if (rmap->map) {
                                                        (rmap->map)[0] =
                                                            strdup(origA);
                                                        if ((rmap->map)[0])
                                                                free_memory = 0;
                                                }
                                        }
                                }
                        }

                        if (free_memory == 0) {
                                if (def->data_type == ATTRMAP) {
                                        rmap->type = NS_ATTR_MAP;
                                        i = __s_api_add_map2hash(ptr,
                                            NS_HASH_RAMAP, rmap);
                                } else {
                                        rmap->type = NS_OBJ_MAP;
                                        i = __s_api_add_map2hash(ptr,
                                            NS_HASH_ROMAP, rmap);
                                }

                                if (i != NS_HASH_RC_SUCCESS) {
                                        switch (i) {
                                        case NS_HASH_RC_CONFIG_ERROR:
                                                exitrc = NS_LDAP_INTERNAL;
                                                (void) snprintf(errstr,
                                                    sizeof (errstr),
                                                    gettext(
                                                    "Unable to set value: "
                                                    "no configuration info "
                                                    "for schema map "
                                                    "update (%s)"), cp);
                                                MKERROR(LOG_ERR, *error,
                                                    NS_LDAP_INTERNAL,
                                                    strdup(errstr),
                                                    NS_LDAP_MEMORY);
                                                break;
                                        case NS_HASH_RC_EXISTED:
                                                exitrc = NS_LDAP_CONFIG;
                                                (void) snprintf(errstr,
                                                    sizeof (errstr),
                                                    gettext(
                                                    "Unable to set value: "
                                                    "schema map "
                                                    "already existed for "
                                                    "(%s, %s)."),
                                                    *attr, origA);
                                                MKERROR(LOG_ERR, *error,
                                                    NS_CONFIG_SYNTAX,
                                                    strdup(errstr),
                                                    NS_LDAP_MEMORY);
                                                break;
                                        case NS_HASH_RC_NO_MEMORY:
                                                exitrc = NS_LDAP_MEMORY;
                                                break;
                                        }
                                        free_memory = 1;
                                }
                        }

                        if (free_memory) {
                                if (tcp)
                                        free(tcp);
                                free(sid);
                                free(origA);
                                __s_api_free2dArray(mapA);
                                if (rmap) {
                                        if (rmap->service)
                                                free(rmap->service);
                                        if (rmap->orig)
                                                free(rmap->orig);
                                        if (rmap->map) {
                                                if ((rmap->map)[0])
                                                        free((rmap->map)[0]);
                                                free(rmap->map);
                                        }
                                        free(rmap);
                                }
                                return (exitrc);
                        }
                }

                /*
                 * For performance gain,
                 * add a "schema mapping existed" indicator
                 * for the given service if not already added.
                 * This dummy map needs not be removed, if
                 * the next real map add operation fails.
                 * since the caller, e.g. ldap_cachemgr.
                 * should exit anyway.
                 */
                free_memory = 1;
                exitrc = NS_LDAP_MEMORY;

                map = (ns_mapping_t *)calloc(1,
                    sizeof (ns_mapping_t));
                if (map) {
                        map->service = strdup(sid);
                        if (map->service) {
                                map->orig = strdup(
                                    NS_HASH_SCHEMA_MAPPING_EXISTED);
                                if (map->orig) {
                                        map->map = (char **)calloc(2,
                                            sizeof (char *));
                                        if (map->map) {
                                                (map->map)[0] =
                                                    strdup(sid);
                                                if ((map->map)[0])
                                                        free_memory = 0;
                                        }
                                }
                        }
                }

                if (free_memory == 0) {
                        map->type = NS_ATTR_MAP;
                        /*
                         * add to reverse map,
                         * so that "ldapclient list"
                         * would not show it
                         */
                        i = __s_api_add_map2hash(ptr,
                            NS_HASH_RAMAP, map);

                        /*
                         * ignore "map already existed" error,
                         * just need one per service.
                         * Need however to free memory allocated
                         * for map.
                         */
                        if (i != NS_HASH_RC_SUCCESS &&
                            i != NS_HASH_RC_EXISTED) {
                                switch (i) {
                                case NS_HASH_RC_CONFIG_ERROR:
                                        exitrc = NS_LDAP_INTERNAL;
                                        (void) snprintf(errstr,
                                            sizeof (errstr),
                                            gettext(
                                            "Unable to set value: "
                                            "no configuration info "
                                            "for schema map "
                                            "update (%s)"), cp);
                                        MKERROR(LOG_ERR, *error,
                                            NS_LDAP_INTERNAL,
                                            strdup(errstr),
                                            NS_LDAP_MEMORY);
                                        break;
                                case NS_HASH_RC_NO_MEMORY:
                                        exitrc = NS_LDAP_MEMORY;
                                        break;
                                }
                                free_memory = 1;
                        } else if (i == NS_HASH_RC_EXISTED) {
                                if (map->service)
                                        free(map->service);
                                if (map->orig)
                                        free(map->orig);
                                if (map->map) {
                                        if ((map->map)[0])
                                                free((map->map)[0]);
                                        free(map->map);
                                }
                                free(map);
                                map = NULL;
                        }
                }

                if (free_memory) {
                        if (tcp)
                                free(tcp);
                        free(sid);
                        free(origA);
                        __s_api_free2dArray(mapA);
                        if (map) {
                                if (map->service)
                                        free(map->service);
                                if (map->orig)
                                        free(map->orig);
                                if (map->map) {
                                        if ((map->map)[0])
                                                free((map->map)[0]);
                                        free(map->map);
                                }
                                free(map);
                        }
                        return (exitrc);
                }

                /*
                 * add the real schema map
                 */
                free_memory = 1;
                exitrc = NS_LDAP_MEMORY;
                map = (ns_mapping_t *)calloc(1, sizeof (ns_mapping_t));
                if (map) {
                        map->service = sid;
                        map->orig = origA;
                        map->map = mapA;

                        if (def->data_type == ATTRMAP) {
                                map->type = NS_ATTR_MAP;
                                i = __s_api_add_map2hash(ptr,
                                    NS_HASH_AMAP, map);
                        } else {
                                map->type = NS_OBJ_MAP;
                                i = __s_api_add_map2hash(ptr,
                                    NS_HASH_OMAP, map);
                        }

                        if (i != NS_HASH_RC_SUCCESS) {
                                switch (i) {
                                case NS_HASH_RC_CONFIG_ERROR:
                                        exitrc = NS_LDAP_INTERNAL;
                                        (void) snprintf(errstr,
                                            sizeof (errstr),
                                            gettext(
                                            "Unable to set value: "
                                            "no configuration info "
                                            "for schema map "
                                            "update (%s)"), cp);
                                        MKERROR(LOG_ERR, *error,
                                            NS_LDAP_INTERNAL,
                                            strdup(errstr),
                                            NS_LDAP_MEMORY);
                                        break;
                                case NS_HASH_RC_EXISTED:
                                        exitrc = NS_LDAP_CONFIG;
                                        (void) snprintf(errstr,
                                            sizeof (errstr),
                                            gettext(
                                            "Unable to set value: "
                                            "schema map "
                                            "already existed for "
                                            "'%s'."), origA);
                                        MKERROR(LOG_ERR, *error,
                                            NS_CONFIG_SYNTAX,
                                            strdup(errstr),
                                            NS_LDAP_MEMORY);
                                        break;
                                case NS_HASH_RC_NO_MEMORY:
                                        exitrc = NS_LDAP_MEMORY;
                                        break;
                                }
                                free_memory = 1;
                        } else
                                free_memory = 0;
                }

                if (free_memory) {
                        if (tcp)
                                free(tcp);
                        free(sid);
                        free(origA);
                        __s_api_free2dArray(mapA);
                        if (map)
                                free(map);
                        return (exitrc);
                }

                break;
        default:
                /* This should never happen. */
                (void) snprintf(errstr, sizeof (errstr),
                    gettext("Unable to set value: invalid configuration "
                    "type (%d)"), def->data_type);
                MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr),
                    NS_LDAP_MEMORY);
                if (tcp != NULL)
                        free(tcp);
                return (NS_LDAP_CONFIG);
        }
        conf.ns_ptype = def->data_type;
        if (tcp != NULL)
                free(tcp);

        /* Individually written verify routines here can replace */
        /* verify_value.  Verify conf (data) as appropriate here */
        if (def->ns_verify != NULL) {
                if ((*def->ns_verify)(type, def, &conf, errstr) != NS_SUCCESS) {
                        ns_param_t sav_conf;

                        MKERROR(LOG_WARNING, *error, NS_CONFIG_SYNTAX,
                            strdup(errstr), NS_LDAP_MEMORY);

                        sav_conf = ptr->paramList[type];
                        ptr->paramList[type] = conf;
                        destroy_param(ptr, type);
                        ptr->paramList[type] = sav_conf;

                        return (NS_LDAP_CONFIG);
                }
        }

        /* post evaluate the data */

        /*
         * if this is for setting a password,
         * encrypt the password first.
         * NOTE evalue() is smart and will just return
         * the value passed if it is already encrypted.
         *
         * Init NS_LDAP_EXP_P here when CACHETTL is updated
         */
        if (type == NS_LDAP_BINDPASSWD_P ||
            type == NS_LDAP_ADMIN_BINDPASSWD_P) {
                cp = conf.ns_pc;
                cp2 = evalue((char *)cp);
                conf.ns_pc = cp2;
                free(cp);
                cp = NULL;
        } else if (type == NS_LDAP_FILE_VERSION_P) {
                ptr->version = NS_LDAP_V1;
                if (strcasecmp(conf.ns_pc, NS_LDAP_VERSION_2) == 0) {
                        ptr->version = NS_LDAP_V2;
                }
        } else if (type == NS_LDAP_CACHETTL_P) {
                cp = conf.ns_pc;
                tm = conv_time(cp);
                ptr->paramList[NS_LDAP_EXP_P].ns_ptype = TIMET;
                if (tm != 0) {
                        tm += time(NULL);
                }
                ptr->paramList[NS_LDAP_EXP_P].ns_tm = tm;
        }

        /* Everything checks out move new values into param */
        destroy_param(ptr, type);
        /* Assign new/updated value into paramList */
        ptr->paramList[type] = conf;

        return (NS_LDAP_SUCCESS);
}


/*
 * Set a parameter value in the 'config' configuration structure
 * Lock as appropriate
 */

int
__ns_ldap_setParam(const ParamIndexType type,
    const void *data, ns_ldap_error_t **error)
{
        ns_ldap_error_t         *errorp;
        int                     ret;
        char                    errstr[2 * MAXERROR];
        ns_config_t             *cfg;
        ns_config_t             *cfg_g = (ns_config_t *)-1;
        ns_config_t             *new_cfg;
        boolean_t               reinit_connmgmt = B_FALSE;

        /* We want to refresh only one configuration at a time */
        (void) mutex_lock(&ns_loadrefresh_lock);
        cfg = __s_api_get_default_config();

        if (cache_server == TRUE) {
                if (cfg == NULL) {
                        __ns_ldap_default_config();
                        cfg = __s_api_get_default_config();
                        if (cfg == NULL) {
                                (void) mutex_unlock(&ns_loadrefresh_lock);
                                return (NS_LDAP_MEMORY);
                        }
                }
        } else {
                /*
                 * This code always return error here on client side,
                 * this needs to change once libsldap is used by more
                 * applications that need to set parameters.
                 */
                (void) snprintf(errstr, sizeof (errstr),
                    gettext("Unable to set parameter from a client in "
                    "__ns_ldap_setParam()"));
                MKERROR(LOG_WARNING, *error, NS_CONFIG_SYNTAX, strdup(errstr),
                    NS_LDAP_MEMORY);
                if (cfg != NULL)
                        __s_api_release_config(cfg);
                (void) mutex_unlock(&ns_loadrefresh_lock);
                return (NS_LDAP_CONFIG);
        }

        /* (re)initialize configuration if necessary */
        if (!__s_api_isStandalone() &&
            cache_server == FALSE && timetorefresh(cfg))
                cfg_g = __s_api_get_default_config_global();
        /* only (re)initialize the global configuration */
        if (cfg == cfg_g) {
                if (cfg_g != NULL)
                        __s_api_release_config(cfg_g);
                new_cfg = LoadCacheConfiguration(cfg, &errorp);
                if (new_cfg != cfg)
                        __s_api_release_config(cfg);
                if (new_cfg == NULL) {
                        (void) snprintf(errstr, sizeof (errstr),
                            gettext("Unable to load configuration '%s' "
                            "('%s')."), NSCONFIGFILE,
                            errorp != NULL && errorp->message != NULL ?
                            errorp->message : "");
                        MKERROR(LOG_WARNING, *error, NS_CONFIG_NOTLOADED,
                            strdup(errstr), NS_LDAP_MEMORY);
                        if (errorp != NULL)
                                (void) __ns_ldap_freeError(&errorp);
                        (void) mutex_unlock(&ns_loadrefresh_lock);
                        return (NS_LDAP_CONFIG);
                }
                if (new_cfg != cfg) {
                        set_curr_config_global(new_cfg);
                        cfg = new_cfg;
                        reinit_connmgmt = B_TRUE;
                }
        }
        (void) mutex_unlock(&ns_loadrefresh_lock);

        if (reinit_connmgmt == B_TRUE)
                __s_api_reinit_conn_mgmt_new_config(cfg);

        /* translate input and save in the parameter list */
        ret = __ns_ldap_setParamValue(cfg, type, data, error);

        __s_api_release_config(cfg);

        return (ret);
}


/*
 * Make a copy of a parameter entry
 */

static void **
dupParam(ns_param_t *ptr)
{
        int             count, i;
        void            **dupdata, *ret;
        int             *intptr;
        char            *cp, tmbuf[32];
        static time_t   expire = 0;
        ns_auth_t       *ap;

        switch (ptr->ns_ptype) {
        case ARRAYAUTH:
        case ARRAYCRED:
        case SAMLIST:
        case SCLLIST:
        case SSDLIST:
        case SERVLIST:
        case ARRAYCP:
                count = ptr->ns_acnt;
                if (count == 0)
                        return (NULL);
                break;
        case CHARPTR:
        case INT:
        case TIMET:
                count = 1;
        }

        dupdata = (void **)calloc((count + 1), sizeof (void *));
        if (dupdata == NULL)
                return (NULL);

        switch (ptr->ns_ptype) {
        case ARRAYAUTH:
                for (i = 0; i < count; i++) {
                        ap = __s_api_AuthEnumtoStruct(
                            (EnumAuthType_t)ptr->ns_pi[i]);
                        if (ap == NULL) {
                                free(dupdata);
                                return (NULL);
                        }
                        dupdata[i] = ap;
                }
                break;
        case ARRAYCRED:
                for (i = 0; i < count; i++) {
                        intptr = (int *)malloc(sizeof (int));
                        if (intptr == NULL) {
                                free(dupdata);
                                return (NULL);
                        }
                        dupdata[i] = (void *)intptr;
                        *intptr = ptr->ns_pi[i];
                }
                break;
        case SAMLIST:
        case SCLLIST:
        case SSDLIST:
        case SERVLIST:
        case ARRAYCP:
                for (i = 0; i < count; i++) {
                        ret = (void *)strdup(ptr->ns_ppc[i]);
                        if (ret == NULL) {
                                free(dupdata);
                                return (NULL);
                        }
                        dupdata[i] = ret;
                }
                break;
        case CHARPTR:
                if (ptr->ns_pc == NULL) {
                        free(dupdata);
                        return (NULL);
                }
                ret = (void *)strdup(ptr->ns_pc);
                if (ret == NULL) {
                        free(dupdata);
                        return (NULL);
                }
                dupdata[0] = ret;
                break;
        case INT:
                intptr = (int *)malloc(sizeof (int));
                if (intptr == NULL) {
                        free(dupdata);
                        return (NULL);
                }
                *intptr = ptr->ns_i;
                dupdata[0] = (void *)intptr;
                break;
        case TIMET:
                expire = ptr->ns_tm;
                tmbuf[31] = '\0';
                cp = lltostr((long)expire, &tmbuf[31]);
                ret = (void *)strdup(cp);
                if (ret == NULL) {
                        free(dupdata);
                        return (NULL);
                }
                dupdata[0] = ret;
                break;
        }
        return (dupdata);
}

int
__ns_ldap_freeParam(void ***data)
{
        void    **tmp;
        int     i = 0;

        if (*data == NULL)
                return (NS_LDAP_SUCCESS);

        for (i = 0, tmp = *data; tmp[i] != NULL; i++)
                free(tmp[i]);

        free(*data);

        *data = NULL;

        return (NS_LDAP_SUCCESS);
}

/*
 * Get the internal format for a parameter value.  This
 * routine makes a copy of an internal param value from
 * the currently active parameter list and returns it.
 */

int
__ns_ldap_getParam(const ParamIndexType Param,
    void ***data, ns_ldap_error_t **error)
{
        char                    errstr[2 * MAXERROR];
        ns_ldap_error_t         *errorp;
        ns_default_config       *def;
        ns_config_t             *cfg;
        ns_config_t             *cfg_g = (ns_config_t *)-1;
        ns_config_t             *new_cfg;
        boolean_t               reinit_connmgmt = B_FALSE;

        if (data == NULL)
                return (NS_LDAP_INVALID_PARAM);

        *data = NULL;

        /* We want to refresh only one configuration at a time */
        (void) mutex_lock(&ns_loadrefresh_lock);
        cfg = __s_api_get_default_config();

        /* (re)initialize configuration if necessary */
        if (!__s_api_isStandalone() &&
            cache_server == FALSE && timetorefresh(cfg))
                cfg_g = __s_api_get_default_config_global();
        /* only (re)initialize the global configuration */
        if (cfg == cfg_g) {
                if (cfg_g != NULL)
                        __s_api_release_config(cfg_g);
                new_cfg = LoadCacheConfiguration(cfg, &errorp);
                if (new_cfg != cfg)
                        __s_api_release_config(cfg);
                if (new_cfg == NULL) {
                        (void) snprintf(errstr, sizeof (errstr),
                            gettext("Unable to load configuration "
                            "'%s' ('%s')."),
                            NSCONFIGFILE,
                            errorp != NULL && errorp->message != NULL ?
                            errorp->message : "");
                        MKERROR(LOG_WARNING, *error, NS_CONFIG_NOTLOADED,
                            strdup(errstr), NS_LDAP_MEMORY);
                        if (errorp != NULL)
                                (void) __ns_ldap_freeError(&errorp);
                        (void) mutex_unlock(&ns_loadrefresh_lock);
                        return (NS_LDAP_CONFIG);
                }
                if (new_cfg != cfg) {
                        set_curr_config_global(new_cfg);
                        cfg = new_cfg;
                        reinit_connmgmt = B_TRUE;
                }
        }
        (void) mutex_unlock(&ns_loadrefresh_lock);

        if (reinit_connmgmt == B_TRUE)
                __s_api_reinit_conn_mgmt_new_config(cfg);

        if (cfg == NULL) {
                (void) snprintf(errstr, sizeof (errstr),
                    gettext("No configuration information available."));
                MKERROR(LOG_ERR, *error, NS_CONFIG_NOTLOADED,
                    strdup(errstr), NS_LDAP_MEMORY);
                return (NS_LDAP_CONFIG);
        }

        if (Param == NS_LDAP_DOMAIN_P) {
                *data = (void **)calloc(2, sizeof (void *));
                if (*data == NULL) {
                        __s_api_release_config(cfg);
                        return (NS_LDAP_MEMORY);
                }
                (*data)[0] = (void *)strdup(cfg->domainName);
                if ((*data)[0] == NULL) {
                        free(*data);
                        __s_api_release_config(cfg);
                        return (NS_LDAP_MEMORY);
                }
        } else if (cfg->paramList[Param].ns_ptype == NS_UNKNOWN) {
                /* get default */
                def = get_defconfig(cfg, Param);
                if (def != NULL)
                        *data = dupParam(&def->defval);
        } else {
                *data = dupParam(&(cfg->paramList[Param]));
        }
        __s_api_release_config(cfg);

        return (NS_LDAP_SUCCESS);
}

/*
 * This routine takes a parameter in internal format and
 * translates it into a variety of string formats for various
 * outputs (doors/file/ldif).  This routine would be better
 * named: __ns_ldap_translateParam2String
 */

char *
__s_api_strValue(ns_config_t *cfg, ParamIndexType index, ns_strfmt_t fmt)
{
        ns_default_config *def = NULL;
        ns_param_t      *ptr;
        ns_hash_t       *hptr;
        ns_mapping_t    *mptr;
        char            ibuf[14];
        char            abuf[64], **cpp;
        int             count, i;
        boolean_t       first = B_TRUE;
        LineBuf         lbuf;
        LineBuf         *buffer = &lbuf;
        char            *retstring;
        char            *sepstr;

        if (cfg == NULL)
                return (NULL);

        /* NS_LDAP_EXP and TRANSPORT_SEC are not exported externally */
        if (index == NS_LDAP_EXP_P || index == NS_LDAP_TRANSPORT_SEC_P)
                return (NULL);

        /* Return nothing if the value is the default */
        if (cfg->paramList[index].ns_ptype == NS_UNKNOWN)
                return (NULL);

        (void) memset((char *)buffer, 0, sizeof (LineBuf));

        ptr = &(cfg->paramList[index]);

        abuf[0] = '\0';

        /* get default */
        def = get_defconfig(cfg, index);
        if (def == NULL)
                return (NULL);

        switch (fmt) {
        case NS_DOOR_FMT:
                (void) strlcpy(abuf, def->name, sizeof (abuf));
                (void) strlcat(abuf, EQUALSEP, sizeof (abuf));
                break;
        case NS_FILE_FMT:
                (void) strlcpy(abuf, def->name, sizeof (abuf));
                (void) strlcat(abuf, EQUSPSEP, sizeof (abuf));
                break;
        case NS_LDIF_FMT:
                /* If no LDIF attr exists ignore the entry */
                if (def->profile_name == NULL)
                        return (NULL);
                (void) strlcpy(abuf, def->profile_name, sizeof (abuf));
                (void) strlcat(abuf, COLSPSEP, sizeof (abuf));
                break;
        default:
                break;
        }

        if (__print2buf(buffer, abuf, NULL))
                goto strValueError;

        switch (ptr->ns_ptype) {
        case ARRAYAUTH:
                count = ptr->ns_acnt;
                for (i = 0; i < count; i++) {
                        sepstr = NULL;
                        if (i != count-1) {
                                if (cfg->version == NS_LDAP_V1) {
                                        sepstr = COMMASEP;
                                } else {
                                        sepstr = SEMISEP;
                                }
                        }
                        if (__print2buf(buffer, __s_get_auth_name(cfg,
                            (AuthType_t)(ptr->ns_pi[i])), sepstr))
                                goto strValueError;
                }
                break;
        case ARRAYCRED:
                count = ptr->ns_acnt;
                for (i = 0; i < count; i++) {
                        sepstr = NULL;
                        if (i != count-1) {
                                sepstr = SPACESEP;
                        }
                        if (__print2buf(buffer, __s_get_credlvl_name(cfg,
                            (CredLevel_t)ptr->ns_pi[i]), sepstr))
                                goto strValueError;
                }
                break;
        case SAMLIST:
        case SCLLIST:
        case SSDLIST:
                count = ptr->ns_acnt;
                for (i = 0; i < count; i++) {
                        if (__print2buf(buffer, ptr->ns_ppc[i], NULL))
                                goto strValueError;

                        if (i == count-1)
                                continue;

                        /* Separate items */
                        switch (fmt) {
                        case NS_DOOR_FMT:
                                if (__print2buf(buffer, DOORLINESEP, NULL) ||
                                    __print2buf(buffer, def->name, EQUALSEP))
                                        goto strValueError;
                                break;
                        case NS_FILE_FMT:
                                if (__print2buf(buffer, "\n", NULL) ||
                                    __print2buf(buffer, def->name, EQUSPSEP))
                                        goto strValueError;
                                break;
                        case NS_LDIF_FMT:
                                if (__print2buf(buffer, "\n", NULL) ||
                                    __print2buf(buffer, def->profile_name,
                                    COLSPSEP))
                                        goto strValueError;
                                break;
                        }
                }
                break;
        case ARRAYCP:
                count = ptr->ns_acnt;
                for (i = 0; i < count; i++) {
                        sepstr = NULL;
                        if (i != count-1) {
                                sepstr = COMMASEP;
                        }
                        if (__print2buf(buffer, ptr->ns_ppc[i], sepstr))
                                goto strValueError;
                }
                break;
        case SERVLIST:
                count = ptr->ns_acnt;
                for (i = 0; i < count; i++) {
                        sepstr = NULL;
                        if (i != count-1) {
                                if (fmt == NS_LDIF_FMT) {
                                        sepstr = SPACESEP;
                                } else {
                                        sepstr = COMMASEP;
                                }
                        }
                        if (__print2buf(buffer, ptr->ns_ppc[i], sepstr))
                                goto strValueError;
                }
                break;
        case CHARPTR:
                if (ptr->ns_pc == NULL)
                        break;
                if (__print2buf(buffer, ptr->ns_pc, NULL))
                        goto strValueError;
                break;
        case INT:
                switch (def->index) {
                case NS_LDAP_PREF_ONLY_P:
                        if (__print2buf(buffer,
                            __s_get_pref_name((PrefOnly_t)ptr->ns_i), NULL))
                                goto strValueError;
                        break;
                case NS_LDAP_SEARCH_REF_P:
                        if (__print2buf(buffer, __s_get_searchref_name(cfg,
                            (SearchRef_t)ptr->ns_i), NULL))
                                goto strValueError;
                        break;
                case NS_LDAP_SEARCH_SCOPE_P:
                        if (__print2buf(buffer,  __s_get_scope_name(cfg,
                            (ScopeType_t)ptr->ns_i), NULL))
                                goto strValueError;
                        break;
                case NS_LDAP_ENABLE_SHADOW_UPDATE_P:
                        if (__print2buf(buffer, __s_get_shadowupdate_name(
                            (enableShadowUpdate_t)ptr->ns_i), NULL))
                                goto strValueError;
                        break;
                default:
                        (void) snprintf(ibuf, sizeof (ibuf),
                            "%d", ptr->ns_i);
                        if (__print2buf(buffer, ibuf, NULL))
                                goto strValueError;
                        break;
                }
                break;
        case ATTRMAP:
                for (hptr = cfg->llHead; hptr; hptr = hptr->h_llnext) {
                        if (hptr->h_type != NS_HASH_AMAP) {
                                continue;
                        }
                        if (!first) {
                                /* print abuf as "separator" */
                                if (fmt == NS_DOOR_FMT) {
                                        if (__print2buf(buffer, DOORLINESEP,
                                            abuf))
                                                goto strValueError;
                                } else {
                                        if (__print2buf(buffer, "\n", abuf))
                                                goto strValueError;
                                }
                        }
                        mptr = hptr->h_map;
                        if (__print2buf(buffer, mptr->service, COLONSEP) ||
                            __print2buf(buffer, mptr->orig, EQUALSEP))
                                goto strValueError;
                        for (cpp = mptr->map; cpp && *cpp; cpp++) {
                                /* print *cpp as "separator" */
                                sepstr = "";
                                if (cpp != mptr->map)
                                        sepstr = SPACESEP;
                                if (__print2buf(buffer, sepstr, *cpp))
                                        goto strValueError;
                        }
                        first = B_FALSE;
                }
                break;
        case OBJMAP:
                for (hptr = cfg->llHead; hptr; hptr = hptr->h_llnext) {
                        if (hptr->h_type != NS_HASH_OMAP) {
                                continue;
                        }
                        if (!first) {
                                /* print abuf as "separator" */
                                if (fmt == NS_DOOR_FMT) {
                                        if (__print2buf(buffer, DOORLINESEP,
                                            abuf))
                                                goto strValueError;
                                } else {
                                        if (__print2buf(buffer, "\n", abuf))
                                                goto strValueError;
                                }
                        }
                        mptr = hptr->h_map;
                        if (__print2buf(buffer, mptr->service, COLONSEP) ||
                            __print2buf(buffer, mptr->orig, EQUALSEP))
                                goto strValueError;
                        for (cpp = mptr->map; cpp && *cpp; cpp++) {
                                /* print *cpp as "separator" */
                                sepstr = "";
                                if (cpp != mptr->map)
                                        sepstr = SPACESEP;
                                if (__print2buf(buffer, sepstr, *cpp))
                                        goto strValueError;
                        }
                        first = B_FALSE;
                }
                break;
        }

        retstring = buffer->str;
        return (retstring);

strValueError:
        if (buffer->len > 0)
                free(buffer->str);
        return (NULL);
}

/* shared by __door_getldapconfig() and __door_getadmincred() */
int
__door_getconf(char **buffer, int *buflen, ns_ldap_error_t **error,
    int callnumber)
{
        typedef union {
                ldap_data_t     s_d;
                char            s_b[DOORBUFFERSIZE];
        } space_t;
        space_t                 *space;

        ldap_data_t             *sptr;
        int                     ndata;
        int                     adata;
        char                    errstr[MAXERROR];
        char                    *domainname;
        ns_ldap_return_code     retCode;
        ldap_config_out_t       *cfghdr;

        *error = NULL;

        domainname = __getdomainname();
        if (domainname == NULL || buffer == NULL || buflen == NULL ||
            (strlen(domainname) >= (sizeof (space_t)
            - sizeof (space->s_d.ldap_call.ldap_callnumber)))) {
                return (NS_LDAP_OP_FAILED);
        }

        space = (space_t *)calloc(1, sizeof (space_t));
        if (space == NULL)
                return (NS_LDAP_MEMORY);

        adata = (sizeof (ldap_call_t) + strlen(domainname) +1);
        ndata = sizeof (space_t);
        space->s_d.ldap_call.ldap_callnumber = callnumber;
        (void) strcpy(space->s_d.ldap_call.ldap_u.domainname, domainname);
        free(domainname);
        domainname = NULL;
        sptr = &space->s_d;

        switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) {
        case NS_CACHE_SUCCESS:
                break;
        case NS_CACHE_NOTFOUND:
                (void) snprintf(errstr, sizeof (errstr),
                    gettext("Door call to "
                    "ldap_cachemgr failed - error: %d."),
                    space->s_d.ldap_ret.ldap_errno);
                MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
                    strdup(errstr), NS_LDAP_MEMORY);
                free(space);
                return (NS_LDAP_OP_FAILED);
        default:
                free(space);
                return (NS_LDAP_OP_FAILED);
        }

        retCode = NS_LDAP_SUCCESS;

        /* copy info from door call to buffer here */
        cfghdr = &sptr->ldap_ret.ldap_u.config_str;
        *buflen = offsetof(ldap_config_out_t, config_str) +
            cfghdr->data_size + 1;
        *buffer = calloc(*buflen, sizeof (char));
        if (*buffer == NULL) {
                retCode = NS_LDAP_MEMORY;
        } else
                (void) memcpy(*buffer, cfghdr, *buflen - 1);

        if (sptr != &space->s_d) {
                (void) munmap((char *)sptr, ndata);
        }
        free(space);

        return (retCode);
}

static int
__door_getldapconfig(char **buffer, int *buflen, ns_ldap_error_t **error)
{
        return (__door_getconf(buffer, buflen, error, GETLDAPCONFIGV1));
}

/*
 * SetDoorInfoToUnixCred parses ldapcachemgr configuration information
 * for Admin credentials.
 */
int
SetDoorInfoToUnixCred(char *buffer, ns_ldap_error_t **errorp,
    UnixCred_t **cred)
{
        UnixCred_t      *ptr;
        char            errstr[MAXERROR];
        char            *name, *value, valbuf[BUFSIZE];
        char            *bufptr = buffer;
        char            *strptr;
        char            *rest;
        ParamIndexType  index = 0;
        ldap_config_out_t       *cfghdr;

        if (errorp == NULL || cred == NULL || *cred == NULL)
                return (NS_LDAP_INVALID_PARAM);
        *errorp = NULL;

        ptr = *cred;

        cfghdr = (ldap_config_out_t *)bufptr;
        bufptr = (char *)cfghdr->config_str;

        strptr = (char *)strtok_r(bufptr, DOORLINESEP, &rest);
        for (; ; ) {
                if (strptr == NULL)
                        break;
                (void) strlcpy(valbuf, strptr, sizeof (valbuf));
                __s_api_split_key_value(valbuf, &name, &value);
                if (__ns_ldap_getParamType(name, &index) != 0) {
                        (void) snprintf(errstr, MAXERROR,
                            gettext("SetDoorInfoToUnixCred: "
                            "Unknown keyword encountered '%s'."), name);
                        MKERROR(LOG_ERR, *errorp, NS_CONFIG_SYNTAX,
                            strdup(errstr), NS_LDAP_MEMORY);
                        return (NS_LDAP_CONFIG);
                }
                switch (index) {
                case NS_LDAP_ADMIN_BINDDN_P:
                        ptr->userID = (char *)strdup(value);
                        break;
                case NS_LDAP_ADMIN_BINDPASSWD_P:
                        ptr->passwd = (char *)strdup(value);
                        break;
                default:
                        (void) snprintf(errstr, MAXERROR,
                            gettext("SetDoorInfoToUnixCred: "
                            "Unknown index encountered '%d'."), index);
                        MKERROR(LOG_ERR, *errorp, NS_CONFIG_SYNTAX,
                            strdup(errstr), NS_LDAP_MEMORY);
                        return (NS_LDAP_CONFIG);
                }
                strptr = (char *)strtok_r(NULL, DOORLINESEP, &rest);
        }

        return (NS_LDAP_SUCCESS);
}

/*
 * SetDoorInfo parses ldapcachemgr configuration information
 * and verifies that the profile is version 1 or version 2 based.
 * version 2 profiles must have a version number as the first profile
 * attribute in the configuration.
 */
static ns_config_t *
SetDoorInfo(char *buffer, ns_ldap_error_t **errorp)
{
        ns_config_t     *ptr;
        char            errstr[MAXERROR], errbuf[MAXERROR];
        char            *name, *value, valbuf[BUFSIZE];
        char            *strptr;
        char            *rest;
        char            *bufptr = buffer;
        ParamIndexType  i;
        int             ret;
        int             first = 1;
        int             errfnd = 0;
        ldap_config_out_t *cfghdr;

        if (errorp == NULL)
                return (NULL);
        *errorp = NULL;

        ptr = __s_api_create_config();
        if (ptr == NULL) {
                return (NULL);
        }

        /* get config cookie from the header */
        cfghdr = (ldap_config_out_t *)bufptr;
        ptr->config_cookie = cfghdr->cookie;
        bufptr = (char *)cfghdr->config_str;

        strptr = (char *)strtok_r(bufptr, DOORLINESEP, &rest);
        for (; ; ) {
                if (strptr == NULL)
                        break;
                (void) strlcpy(valbuf, strptr, sizeof (valbuf));
                __s_api_split_key_value(valbuf, &name, &value);
                /* Use get_versiontype and check for V1 vs V2 prototypes */
                if (__s_api_get_versiontype(ptr, name, &i) < 0) {
                        (void) snprintf(errstr, sizeof (errstr),
                            "%s (%s)\n",
                            gettext("Illegal profile entry "
                            "line in configuration."),
                            name);
                        errfnd++;
                /* Write verify routines and get rid of verify_value here */
                } else if (verify_value(ptr, name,
                    value, errbuf) != NS_SUCCESS) {
                        (void) snprintf(errstr, sizeof (errstr),
                            gettext("%s\n"), errbuf);
                        errfnd++;
                } else if (!first && i == NS_LDAP_FILE_VERSION_P) {
                        (void) snprintf(errstr, sizeof (errstr),
                            gettext("Illegal NS_LDAP_FILE_VERSION "
                            "line in configuration.\n"));
                        errfnd++;
                }
                if (errfnd) {
                        MKERROR(LOG_ERR, *errorp, NS_CONFIG_SYNTAX,
                            strdup(errstr), NULL);
                } else {
                        ret = set_default_value(ptr, name, value, errorp);
                }
                if (errfnd || ret != NS_SUCCESS) {
                        __s_api_destroy_config(ptr);
                        return (NULL);
                }
                first = 0;

                strptr = (char *)strtok_r(NULL, DOORLINESEP, &rest);
        }

        if (__s_api_crosscheck(ptr, errstr, B_TRUE) != NS_SUCCESS) {
                __s_api_destroy_config(ptr);
                MKERROR(LOG_WARNING, *errorp, NS_CONFIG_SYNTAX, strdup(errstr),
                    NULL);
                return (NULL);
        }

        return (ptr);
}

static ns_config_t *
LoadCacheConfiguration(ns_config_t *oldcfg, ns_ldap_error_t **error)
{
        char            *buffer = NULL;
        int             buflen = 0;
        int             ret;
        ns_config_t     *cfg;
        ldap_config_out_t *cfghdr;
        ldap_get_chg_cookie_t old_cookie;
        ldap_get_chg_cookie_t new_cookie;

        *error = NULL;
        ret = __door_getldapconfig(&buffer, &buflen, error);

        if (ret != NS_LDAP_SUCCESS) {
                if (*error != NULL && (*error)->message != NULL)
                        syslog(LOG_WARNING, "libsldap: %s", (*error)->message);
                return (NULL);
        }

        /* No need to reload configuration if config cookie is the same */
        cfghdr = (ldap_config_out_t *)buffer;
        new_cookie = cfghdr->cookie;
        if (oldcfg != NULL)
                old_cookie = oldcfg->config_cookie;

        if (oldcfg != NULL && old_cookie.mgr_pid == new_cookie.mgr_pid &&
            old_cookie.seq_num == new_cookie.seq_num) {
                free(buffer);
                return (oldcfg);
        }

        /* now convert from door format */
        cfg = SetDoorInfo(buffer, error);
        free(buffer);

        if (cfg == NULL && *error != NULL && (*error)->message != NULL)
                syslog(LOG_WARNING, "libsldap: %s", (*error)->message);
        return (cfg);
}

/*
 * converts the time string into seconds.  The time string can be specified
 * using one of the following time units:
 *      #s (# of seconds)
 *      #m (# of minutes)
 *      #h (# of hours)
 *      #d (# of days)
 *      #w (# of weeks)
 * NOTE: you can only specify one the above.  No combination of the above
 * units is allowed.  If no unit specified, it will default to "seconds".
 */
static time_t
conv_time(char *s)
{
        time_t t;
        char c;
        int l, m;
        long tot;

        l = strlen(s);
        if (l == 0)
                return (0);
        c = s[--l];
        m = 0;
        switch (c) {
        case 'w': /* weeks */
                m = 604800;
                break;
        case 'd': /* days */
                m = 86400;
                break;
        case 'h': /* hours */
                m = 3600;
                break;
        case 'm': /* minutes */
                m = 60;
                break;
        case 's': /* seconds */
                m = 1;
                break;
        /* the default case is set to "second" */
        }
        if (m != 0)
                s[l] = '\0';
        else
                m = 1;
        errno = 0;
        tot = atol(s);
        if ((0 == tot) && (EINVAL == errno))
                return (0);
        if (((LONG_MAX == tot) || (LONG_MIN == tot)) && (EINVAL == errno))
                return (0);

        tot = tot * m;
        t = (time_t)tot;
        return (t);
}


ns_auth_t *
__s_api_AuthEnumtoStruct(const EnumAuthType_t i)
{
        ns_auth_t *ap;

        ap = (ns_auth_t *)calloc(1, sizeof (ns_auth_t));
        if (ap == NULL)
                return (NULL);
        switch (i) {
                case NS_LDAP_EA_NONE:
                        break;
                case NS_LDAP_EA_SIMPLE:
                        ap->type = NS_LDAP_AUTH_SIMPLE;
                        break;
                case NS_LDAP_EA_SASL_CRAM_MD5:
                        ap->type = NS_LDAP_AUTH_SASL;
                        ap->saslmech = NS_LDAP_SASL_CRAM_MD5;
                        break;
                case NS_LDAP_EA_SASL_DIGEST_MD5:
                        ap->type = NS_LDAP_AUTH_SASL;
                        ap->saslmech = NS_LDAP_SASL_DIGEST_MD5;
                        break;
                case NS_LDAP_EA_SASL_DIGEST_MD5_INT:
                        ap->type = NS_LDAP_AUTH_SASL;
                        ap->saslmech = NS_LDAP_SASL_DIGEST_MD5;
                        ap->saslopt = NS_LDAP_SASLOPT_INT;
                        break;
                case NS_LDAP_EA_SASL_DIGEST_MD5_CONF:
                        ap->type = NS_LDAP_AUTH_SASL;
                        ap->saslmech = NS_LDAP_SASL_DIGEST_MD5;
                        ap->saslopt = NS_LDAP_SASLOPT_PRIV;
                        break;
                case NS_LDAP_EA_SASL_EXTERNAL:
                        ap->type = NS_LDAP_AUTH_SASL;
                        ap->saslmech = NS_LDAP_SASL_EXTERNAL;
                        break;
                case NS_LDAP_EA_SASL_GSSAPI:
                        ap->type = NS_LDAP_AUTH_SASL;
                        ap->saslmech = NS_LDAP_SASL_GSSAPI;
                        ap->saslopt = NS_LDAP_SASLOPT_INT |
                            NS_LDAP_SASLOPT_PRIV;
                        break;
                case NS_LDAP_EA_TLS_NONE:
                        ap->type = NS_LDAP_AUTH_TLS;
                        ap->tlstype = NS_LDAP_TLS_NONE;
                        break;
                case NS_LDAP_EA_TLS_SIMPLE:
                        ap->type = NS_LDAP_AUTH_TLS;
                        ap->tlstype = NS_LDAP_TLS_SIMPLE;
                        break;
                case NS_LDAP_EA_TLS_SASL_CRAM_MD5:
                        ap->type = NS_LDAP_AUTH_TLS;
                        ap->tlstype = NS_LDAP_TLS_SASL;
                        ap->saslmech = NS_LDAP_SASL_CRAM_MD5;
                        break;
                case NS_LDAP_EA_TLS_SASL_DIGEST_MD5:
                        ap->type = NS_LDAP_AUTH_TLS;
                        ap->tlstype = NS_LDAP_TLS_SASL;
                        ap->saslmech = NS_LDAP_SASL_DIGEST_MD5;
                        break;
                case NS_LDAP_EA_TLS_SASL_DIGEST_MD5_INT:
                        ap->type = NS_LDAP_AUTH_TLS;
                        ap->tlstype = NS_LDAP_TLS_SASL;
                        ap->saslmech = NS_LDAP_SASL_DIGEST_MD5;
                        ap->saslopt = NS_LDAP_SASLOPT_INT;
                        break;
                case NS_LDAP_EA_TLS_SASL_DIGEST_MD5_CONF:
                        ap->type = NS_LDAP_AUTH_TLS;
                        ap->tlstype = NS_LDAP_TLS_SASL;
                        ap->saslmech = NS_LDAP_SASL_DIGEST_MD5;
                        ap->saslopt = NS_LDAP_SASLOPT_PRIV;
                        break;
                case NS_LDAP_EA_TLS_SASL_EXTERNAL:
                        ap->type = NS_LDAP_AUTH_TLS;
                        ap->tlstype = NS_LDAP_TLS_SASL;
                        ap->saslmech = NS_LDAP_SASL_EXTERNAL;
                        break;
                default:
                        /* should never get here */
                        free(ap);
                        return (NULL);
        }
        return (ap);
}


/*
 * Parameter Index Type validation routines
 */

/* Validate a positive integer */
/* Size of errbuf needs to be MAXERROR */
/* ARGSUSED */
static int
__s_val_postime(ParamIndexType i, ns_default_config *def,
    ns_param_t *param, char *errbuf)
{
        char    *cp;
        long    tot;

        if (param && param->ns_ptype == CHARPTR && param->ns_pc) {
                for (cp = param->ns_pc; cp && *cp; cp++) {
                        if (*cp >= '0' && *cp <= '9')
                                continue;
                        switch (*cp) {
                        case 'w': /* weeks */
                        case 'd': /* days */
                        case 'h': /* hours */
                        case 'm': /* minutes */
                        case 's': /* seconds */
                                if (*(cp+1) == '\0') {
                                        break;
                                }
                                /* FALLTHROUGH */
                        default:
                                (void) strcpy(errbuf, "Illegal time value");
                                return (NS_PARSE_ERR);
                        }
                }
                /* Valid form:  [0-9][0-9]*[wdhms]* */
                tot = atol(param->ns_pc);       /* check overflow */
                if (tot >= 0)
                        return (NS_SUCCESS);
        }
        (void) snprintf(errbuf, MAXERROR,
            gettext("Illegal time value in %s"), def->name);
        return (NS_PARSE_ERR);
}


/* Validate the Base DN */
/* It can be empty (RootDSE request) or needs to have an '=' */
/* Size of errbuf needs to be MAXERROR */
/* ARGSUSED */
static int
__s_val_basedn(ParamIndexType i, ns_default_config *def,
    ns_param_t *param, char *errbuf)
{
        if (param && param->ns_ptype == CHARPTR &&
            i == NS_LDAP_SEARCH_BASEDN_P &&
            ((param->ns_pc == NULL) ||                  /* empty */
            (*(param->ns_pc) == '\0') ||                /* empty */
            (strchr(param->ns_pc, '=') != NULL)))       /* '=' */
        {
                return (NS_SUCCESS);
        }
        (void) snprintf(errbuf, MAXERROR,
            gettext("Non-existent or invalid DN in %s"),
            def->name);
        return (NS_PARSE_ERR);
}


/* Validate the serverList */
/* For each server in list, check if valid IP or hostname */
/* Size of errbuf needs to be MAXERROR */
/* ARGSUSED */
static int
__s_val_serverList(ParamIndexType i, ns_default_config *def,
    ns_param_t *param, char *errbuf)
{
        for (i = 0; i < param->ns_acnt; i++) {
                if ((__s_api_isipv4(param->ns_ppc[i])) ||
                    (__s_api_isipv6(param->ns_ppc[i])) ||
                    (__s_api_ishost(param->ns_ppc[i]))) {
                        continue;
                }
                /* err */
                (void) snprintf(errbuf, MAXERROR,
                    gettext("Invalid server (%s) in %s"),
                    param->ns_ppc[i], def->name);
                return (NS_PARSE_ERR);
        }

        return (NS_SUCCESS);
}


/* Check for a BINDDN */
/* It can not be empty and needs to have an '=' */
/* Size of errbuf needs to be MAXERROR */
/* ARGSUSED */
static int
__s_val_binddn(ParamIndexType i, ns_default_config *def,
    ns_param_t *param, char *errbuf)
{
        char *dntype;

        if (param && param->ns_ptype == CHARPTR &&
            (i == NS_LDAP_BINDDN_P || i == NS_LDAP_ADMIN_BINDDN_P) &&
            ((param->ns_pc == NULL) ||
            ((*(param->ns_pc) != '\0') &&
            (strchr(param->ns_pc, '=') != NULL)))) {
                return (NS_SUCCESS);
        }
        if (i == NS_LDAP_BINDDN_P)
                dntype = "proxy";
        else
                dntype = "update";
        (void) snprintf(errbuf, MAXERROR,
            gettext("NULL or invalid %s bind DN"), dntype);
        return (NS_PARSE_ERR);
}


/* Check for a BINDPASSWD */
/* The string can not be NULL or empty */
/* Size of errbuf needs to be MAXERROR */
/* ARGSUSED */
static int
__s_val_bindpw(ParamIndexType i, ns_default_config *def,
    ns_param_t *param, char *errbuf)
{
        char *pwtype;

        if (param && param->ns_ptype == CHARPTR &&
            (i == NS_LDAP_BINDPASSWD_P || i == NS_LDAP_ADMIN_BINDPASSWD_P) &&
            ((param->ns_pc == NULL) ||
            (*(param->ns_pc) != '\0'))) {
                return (NS_SUCCESS);
        }
        if (i == NS_LDAP_BINDPASSWD_P)
                pwtype = "proxy";
        else
                pwtype = "admin";
        (void) snprintf(errbuf, MAXERROR,
            gettext("NULL %s bind password"), pwtype);
        return (NS_PARSE_ERR);
}

/*
 * __s_get_hostcertpath returns either the configured host certificate path
 * or, if none, the default host certificate path (/var/ldap). Note that this
 * does not use __ns_ldap_getParam because it may be called during connection
 * setup. This can fail due to insufficient memory.
 */

char *
__s_get_hostcertpath(void)
{
        ns_config_t             *cfg;
        ns_param_t              *param;
        char                    *ret = NULL;

        cfg = __s_api_get_default_config();
        if (cfg != NULL) {
                param = &cfg->paramList[NS_LDAP_HOST_CERTPATH_P];
                if (param->ns_ptype == CHARPTR)
                        ret = strdup(param->ns_pc);
                __s_api_release_config(cfg);
        }
        if (ret == NULL)
                ret = strdup(NSLDAPDIRECTORY);
        return (ret);
}

static void
_free_config()
{
        if (current_config != NULL)
                destroy_config(current_config);

        current_config = NULL;
}