/*
 * Copyright (C) 1997-2003 Luke Howard. This file is part of the nss_ldap library. Contributed by Luke Howard, <lukeh@padl.com>, 1997. (The author maintains a non-exclusive licence to distribute this file under their own conditions.)
 * 
 * The nss_ldap library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
 * 
 * The nss_ldap library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public License along with the nss_ldap library; see the file COPYING.LIB.  If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "config.h"

#ifdef HAVE_THREAD_H
#include <thread.h>
#elif defined(HAVE_PTHREAD_H)
#include <pthread.h>
#endif

#include <stdio.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <stdlib.h>

#include <sys/param.h>
#include <netdb.h>
#include <syslog.h>
#include <string.h>
#include <fcntl.h>

#ifdef HAVE_LBER_H
#include <lber.h>
#endif
#ifdef HAVE_LDAP_H
#include <ldap.h>
#endif

#ifndef HAVE_SNPRINTF
#include "snprintf.h"
#endif

#include "ldap-nss.h"
#include "globals.h"
#include "util.h"

/* static char rcsId[] = "$Id: util.c,v 1.1.1.1 2004/05/06 07:26:16 bushman Exp $";*/

static NSS_STATUS 
do_getrdnvalue(const char *dn,
	const char *rdntype,
	char **rval, char **buffer,
	size_t * buflen);

#ifdef AT_OC_MAP
enum ldap_map_type {
	MAP_ATTRIBUTE,
	MAP_OBJECTCLASS
};

typedef enum ldap_map_type ldap_map_type_t;

static NSS_STATUS 
do_parse_map_statement(ldap_config_t * cfg,
	const char *statement,
	ldap_map_type_t type);
#endif				/* AT_OC_MAP */

static NSS_STATUS 
do_searchdescriptorconfig(const char *key,
	const char *value,
	size_t valueLength,
	ldap_service_search_descriptor_t
	** result, char **buffer,
	size_t * buflen);

/*
 * Use db_185.h first, as that's an old DB wrapper around DB 2.x. If we don't have that, use db1/db.h, which I think may me the original DB library. Last resort, use db.h, which we hope has the right API!
 */
#if defined(RFC2307BIS) || defined(AT_OC_MAP)
#ifdef HAVE_DB3_DB_185_H
#include <db3/db_185.h>
#define DN2UID_CACHE
#elif defined(HAVE_DB_185_H)
#include <db_185.h>
#define DN2UID_CACHE
#elif defined(HAVE_DB1_DB_H)
#include <db1/db.h>
#define DN2UID_CACHE
#elif defined(HAVE_DB_H)
#include <db.h>
#define DN2UID_CACHE
#endif				/* HAVE_DB3_DB_H */

#ifdef DN2UID_CACHE

/* Used for both DN2UID and AT_OC mapping */
void *
_nss_hash_open()
{
	DB *db = NULL;

	db = dbopen(NULL, O_RDWR, 0600, DB_HASH, NULL);

	return db;
}
#endif

#if defined(DN2UID_CACHE) && defined(RFC2307BIS)

#include "bsdtss.h"

#include <fcntl.h>
//static DB *__cache = NULL;
DECLARE_TSS(__cache, DB *, 1, NULL)
#define __cache GET_TSS(__cache)

	void _util_init_tss_keys()
{
	INIT_TSS_KEY(__cache);
}

NSS_LDAP_DEFINE_LOCK(__cache_lock);

#define cache_lock()     NSS_LDAP_LOCK(__cache_lock)
#define cache_unlock()   NSS_LDAP_UNLOCK(__cache_lock)
static NSS_STATUS
dn2uid_cache_put(const char *dn, const char *uid)
{
	DBT key, val;
	int rc;

	cache_lock();

	if (__cache == NULL) {
		__cache = _nss_hash_open();
		if (__cache == NULL) {
			cache_unlock();
			return NSS_TRYAGAIN;
		}
	}
	key.data = (void *)dn;
	key.size = strlen(dn);
	val.data = (void *)uid;
	val.size = strlen(uid);
	rc = (__cache->put) (__cache,
#if DB_VERSION_MAJOR >= 2
		NULL,
#endif
		&key, &val, 0);

	cache_unlock();
	return rc ? NSS_TRYAGAIN : NSS_SUCCESS;
}

static NSS_STATUS
dn2uid_cache_get(const char *dn, char **uid, char **buffer, size_t * buflen)
{
	DBT key, val;

	cache_lock();

	if (__cache == NULL) {
		cache_unlock();
		return NSS_NOTFOUND;
	}
	key.data = (void *)dn;
	key.size = strlen(dn);

	if ((__cache->get) (__cache,
#if DB_VERSION_MAJOR >= 2
			NULL,
#endif
			&key, &val, 0) != 0) {
		cache_unlock();
		return NSS_NOTFOUND;
	}
	if (*buflen <= val.size) {
		cache_unlock();
		return NSS_TRYAGAIN;
	}
	*uid = *buffer;
	strncpy(*uid, (char *)val.data, val.size);
	(*uid)[val.size] = '\0';
	*buffer += val.size + 1;
	*buflen -= val.size + 1;

	cache_unlock();
	return NSS_SUCCESS;
}
#endif				/* DN2UID_CACHE */
#endif

#ifdef RFC2307BIS

NSS_STATUS
_nss_ldap_dn2uid(LDAP * ld,
	const char *dn, char **uid, char **buffer, size_t * buflen)
{
	NSS_STATUS status;

	debug("==> _nss_ldap_dn2uid");

	status = do_getrdnvalue(dn, AT(uid), uid, buffer, buflen);
	if (status == NSS_NOTFOUND) {
#ifdef DN2UID_CACHE
		status = dn2uid_cache_get(dn, uid, buffer, buflen);
		if (status == NSS_NOTFOUND) {
#endif				/* DN2UID_CACHE */
			const char *attrs[2];
			LDAPMessage *res;

			attrs[0] = AT(uid);
			attrs[1] = NULL;

			if (_nss_ldap_read(dn, attrs, &res) == NSS_SUCCESS) {
				LDAPMessage *e = ldap_first_entry(ld, res);
				if (e != NULL) {
					status =
						_nss_ldap_assign_attrval(ld, e, AT(uid), uid, buffer,
						buflen);
#ifdef DN2UID_CACHE
					if (status == NSS_SUCCESS)
						dn2uid_cache_put(dn, *uid);
				}
#endif				/* DN2UID_CACHE */
			}
			ldap_msgfree(res);
		}
	}
	debug("<== _nss_ldap_dn2uid");

	return status;
}
#endif				/* RFC2307BIS */

NSS_STATUS
_nss_ldap_getrdnvalue(LDAP * ld,
	LDAPMessage * entry,
	const char *rdntype,
	char **rval, char **buffer, size_t * buflen)
{
	char *dn;
	NSS_STATUS status;

	dn = ldap_get_dn(ld, entry);
	if (dn == NULL) {
		return NSS_NOTFOUND;
	}
	status = do_getrdnvalue(dn, rdntype, rval, buffer, buflen);
#ifdef HAVE_LDAP_MEMFREE
	ldap_memfree(dn);
#else
	free(dn);
#endif				/* HAVE_LDAP_MEMFREE */

	/*
	 * If examining the DN failed, then pick the nominal first value of cn as the canonical name (recall that attributes are sets, not sequences)
	 */
	if (status == NSS_NOTFOUND) {
		char **vals;

		vals = ldap_get_values(ld, entry, rdntype);

		if (vals != NULL) {
			int rdnlen = strlen(*vals);
			if (*buflen > rdnlen) {
				char *rdnvalue = *buffer;
				strncpy(rdnvalue, *vals, rdnlen);
				rdnvalue[rdnlen] = '\0';
				*buffer += rdnlen + 1;
				*buflen -= rdnlen + 1;
				*rval = rdnvalue;
				status = NSS_SUCCESS;
			} else {
				status = NSS_TRYAGAIN;
			}
			ldap_value_free(vals);
		}
	}
	return status;
}

static NSS_STATUS
do_getrdnvalue(const char *dn,
	const char *rdntype,
	char **rval, char **buffer, size_t * buflen)
{
	char **exploded_dn;
	char *rdnvalue = NULL;
	char rdnava[64];
	int rdnlen = 0, rdnavalen;

	snprintf(rdnava, sizeof rdnava, "%s=", rdntype);
	rdnavalen = strlen(rdnava);

	exploded_dn = ldap_explode_dn(dn, 0);

	if (exploded_dn != NULL) {
		/*
		 * attempt to get the naming attribute's principal value by parsing the RDN. We need to support multivalued RDNs (as they're essentially mandated for services)
		 */
#ifdef HAVE_LDAP_EXPLODE_RDN
		/*
		 * use ldap_explode_rdn() API, as it's cleaner than strtok(). This code has not been tested!
		 */
		char **p, **exploded_rdn;

		exploded_rdn = ldap_explode_rdn(*exploded_dn, 0);
		if (exploded_rdn != NULL) {
			for (p = exploded_rdn; *p != NULL; p++) {
				if (strncasecmp(*p, rdnava, rdnavalen) == 0) {
					char *r = *p + rdnavalen;

					rdnlen = strlen(r);
					if (*buflen <= rdnlen) {
						ldap_value_free(exploded_rdn);
						ldap_value_free(exploded_dn);
						return NSS_TRYAGAIN;
					}
					rdnvalue = *buffer;
					strncpy(rdnvalue, r, rdnlen);
					break;
				}
			}
			ldap_value_free(exploded_rdn);
		}
#else
		/*
		 * we don't have Netscape's ldap_explode_rdn() API, so we fudge it with strtok(). Note that this will not handle escaping properly.
		 */
		char *p, *r = *exploded_dn;
#ifdef HAVE_STRTOK_R
		char *st = NULL;
#endif

#ifndef HAVE_STRTOK_R
		for (p = strtok(r, "+");
#else
		for (p = strtok_r(r, "+", &st);
#endif
			p != NULL;
#ifndef HAVE_STRTOK_R
			p = strtok(NULL, "+"))
#else
			p = strtok_r(NULL, "+", &st))
#endif
		{
			if (strncasecmp(p, rdnava, rdnavalen) == 0) {
				p += rdnavalen;
				rdnlen = strlen(p);
				if (*buflen <= rdnlen) {
					ldap_value_free(exploded_dn);
					return NSS_TRYAGAIN;
				}
				rdnvalue = *buffer;
				strncpy(rdnvalue, p, rdnlen);
				break;
			}
			if (r != NULL)
				r = NULL;
		}
#endif				/* HAVE_LDAP_EXPLODE_RDN */
	}
	if (exploded_dn != NULL) {
		ldap_value_free(exploded_dn);
	}
	if (rdnvalue != NULL) {
		rdnvalue[rdnlen] = '\0';
		*buffer += rdnlen + 1;
		*buflen -= rdnlen + 1;
		*rval = rdnvalue;
		return NSS_SUCCESS;
	}
	return NSS_NOTFOUND;
}

#ifdef AT_OC_MAP
static NSS_STATUS
do_parse_map_statement(ldap_config_t * cfg,
	const char *statement, ldap_map_type_t type)
{
	/**
         * statement has already been prepared in _nss_ldap_readconfig
         * => only a split dumping white space inbetween is necessary!
         */
	NSS_STATUS stat;
	char *key, *val;

	key = (char *)statement;
	val = key;
	while (*val != ' ' && *val != '\t')
		val++;
	*(val++) = '\0';

	while (*val == ' ' || *val == '\t')
		val++;

	if (type == MAP_ATTRIBUTE)
		stat = _nss_ldap_atmap_put(cfg, key, val);
	else			/* type == MAP_OBJECTCLASS */
		stat = _nss_ldap_ocmap_put(cfg, key, val);

	return stat;
}
#endif				/* AT_OC_MAP */

static NSS_STATUS
do_searchdescriptorconfig(const char *key, const char *value, size_t len,
	ldap_service_search_descriptor_t ** result,
	char **buffer, size_t * buflen)
{
	ldap_service_search_descriptor_t **t, *cur;
	char *base;
	char *filter, *s;
	int scope;

	t = NULL;
	filter = NULL;
	scope = -1;

	if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_PASSWD))
		t = &result[LM_PASSWD];
	if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_SHADOW))
		t = &result[LM_SHADOW];
	else if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_GROUP))
		t = &result[LM_GROUP];
	else if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_HOSTS))
		t = &result[LM_HOSTS];
	else if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_SERVICES))
		t = &result[LM_SERVICES];
	else if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_NETWORKS))
		t = &result[LM_NETWORKS];
	else if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_PROTOCOLS))
		t = &result[LM_PROTOCOLS];
	else if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_RPC))
		t = &result[LM_RPC];
	else if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_ETHERS))
		t = &result[LM_ETHERS];
	else if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_NETMASKS))
		t = &result[LM_NETMASKS];
	else if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_BOOTPARAMS))
		t = &result[LM_BOOTPARAMS];
	else if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_ALIASES))
		t = &result[LM_ALIASES];
	else if (!strcasecmp(key, NSS_LDAP_KEY_NSS_BASE_NETGROUP))
		t = &result[LM_NETGROUP];

	if (t == NULL)
		return NSS_SUCCESS;

	/* we have already checked for room for the value */
	/* len is set to the length of value */
	base = *buffer;
	strncpy(base, value, len);
	base[len] = '\0';

	*buffer += len + 1;
	*buflen -= len + 1;

	/* probably is some funky escaping needed here. later... */
	s = strchr(base, '?');
	if (s != NULL) {
		*s = '\0';
		s++;
		if (!strcasecmp(s, "sub"))
			scope = LDAP_SCOPE_SUBTREE;
		else if (!strcasecmp(s, "one"))
			scope = LDAP_SCOPE_ONELEVEL;
		else if (!strcasecmp(s, "base"))
			scope = LDAP_SCOPE_BASE;
		filter = strchr(s, '?');
		if (filter != NULL) {
			*filter = '\0';
			filter++;
		}
	}
	if (bytesleft(*buffer, *buflen, ldap_service_search_descriptor_t) <
		sizeof(ldap_service_search_descriptor_t))
		return NSS_UNAVAIL;

	align(*buffer, *buflen, ldap_service_search_descriptor_t);

	for (cur = *t; cur && cur->lsd_next; cur = cur->lsd_next);
	if (!cur) {
		*t = (ldap_service_search_descriptor_t *) * buffer;
		cur = *t;
	} else {
		cur->lsd_next = (ldap_service_search_descriptor_t *) * buffer;
		cur = cur->lsd_next;
	}

	cur->lsd_base = base;
	cur->lsd_scope = scope;
	cur->lsd_filter = filter;
	cur->lsd_next = NULL;

	*buffer += sizeof(ldap_service_search_descriptor_t);
	*buflen -= sizeof(ldap_service_search_descriptor_t);

	return NSS_SUCCESS;
}

void
_nss_ldap_init_config(ldap_config_t * result)
{
	memset(result, 0, sizeof(*result));

	result->ldc_scope = LDAP_SCOPE_SUBTREE;
	result->ldc_deref = LDAP_DEREF_NEVER;
	result->ldc_host = NULL;
	result->ldc_base = NULL;
	result->ldc_port = 0;
	result->ldc_binddn = NULL;
	result->ldc_bindpw = NULL;
	result->ldc_saslid = NULL;
	result->ldc_usesasl = 0;
	result->ldc_rootbinddn = NULL;
	result->ldc_rootbindpw = NULL;
	result->ldc_rootsaslid = NULL;
	result->ldc_rootusesasl = 0;
#ifdef LDAP_VERSION3
	result->ldc_version = LDAP_VERSION3;
#else
	result->ldc_version = LDAP_VERSION2;
#endif				/* LDAP_VERSION3 */
	result->ldc_timelimit = LDAP_NO_LIMIT;
	result->ldc_bind_timelimit = 30;
	result->ldc_ssl_on = SSL_OFF;
	result->ldc_sslpath = NULL;
	result->ldc_referrals = 1;
	result->ldc_restart = 1;
	result->ldc_uri = NULL;
	result->ldc_tls_checkpeer = 0;
	result->ldc_tls_cacertfile = NULL;
	result->ldc_tls_cacertdir = NULL;
	result->ldc_tls_ciphers = NULL;
	result->ldc_tls_cert = NULL;
	result->ldc_tls_key = NULL;
	result->ldc_tls_randfile = NULL;
	result->ldc_idle_timelimit = 0;
	result->ldc_reconnect_pol = LP_RECONNECT_HARD;
#ifdef AT_OC_MAP
	result->ldc_at_map = NULL;
	result->ldc_oc_map = NULL;
	result->ldc_password_type = LU_RFC2307_USERPASSWORD;
#endif				/* AT_OC_MAP */

	result->ldc_next = result;
}

NSS_STATUS
_nss_ldap_readconfig(ldap_config_t ** presult, char *buffer, size_t buflen)
{
	FILE *fp;
	char b[NSS_LDAP_CONFIG_BUFSIZ];
	NSS_STATUS stat = NSS_SUCCESS;
	ldap_config_t *result;

	if (bytesleft(buffer, buflen, ldap_config_t *) < sizeof(ldap_config_t)) {
		return NSS_TRYAGAIN;
	}
	align(buffer, buflen, ldap_config_t *);
	result = *presult = (ldap_config_t *) buffer;
	buffer += sizeof(ldap_config_t);
	buflen -= sizeof(ldap_config_t);

	_nss_ldap_init_config(result);

	fp = fopen(NSS_LDAP_PATH_CONF, "r");
	if (fp == NULL)
		return NSS_UNAVAIL;

	while (fgets(b, sizeof(b), fp) != NULL) {
		char *k, *v;
		int len;
		char **t = NULL;

		if (*b == '\n' || *b == '#')
			continue;

		k = b;
		v = k;

		/* skip past all characters in keyword */
		while (*v != '\0' && *v != ' ' && *v != '\t')
			v++;

		if (*v == '\0')
			continue;

		/* terminate keyword */
		*(v++) = '\0';

		/* skip all whitespaces between keyword and value */
		/* Lars Oergel <lars.oergel@innominate.de>, 05.10.2000 */
		while (*v == ' ' || *v == '\t')
			v++;

		/* kick off all whitespaces and newline at the end of value */
		/* Bob Guo <bob@mail.ied.ac.cn>, 08.10.2001 */
		len = strlen(v) - 1;
		while (v[len] == ' ' || v[len] == '\t' || v[len] == '\n')
			--len;
		v[++len] = '\0';

		if (buflen < (size_t) (len + 1)) {
			stat = NSS_TRYAGAIN;
			break;
		}
		if (!strcasecmp(k, NSS_LDAP_KEY_HOST)) {
			t = &result->ldc_host;
		} else if (!strcasecmp(k, NSS_LDAP_KEY_URI)) {
			t = &result->ldc_uri;
		} else if (!strcasecmp(k, NSS_LDAP_KEY_BASE)) {
			t = &result->ldc_base;
		} else if (!strcasecmp(k, NSS_LDAP_KEY_BINDDN)) {
			t = &result->ldc_binddn;
		} else if (!strcasecmp(k, NSS_LDAP_KEY_BINDPW)) {
			t = &result->ldc_bindpw;
		} else if (!strcasecmp(k, NSS_LDAP_KEY_USESASL)) {
			result->ldc_usesasl = (!strcasecmp(v, "on")
				|| !strcasecmp(v, "yes")
				|| !strcasecmp(v, "true"));
		} else if (!strcasecmp(k, NSS_LDAP_KEY_SASLID)) {
			t = &result->ldc_saslid;
		} else if (!strcasecmp(k, NSS_LDAP_KEY_ROOTBINDDN)) {
			t = &result->ldc_rootbinddn;
		} else if (!strcasecmp(k, NSS_LDAP_KEY_ROOTUSESASL)) {
			result->ldc_rootusesasl = (!strcasecmp(v, "on")
				|| !strcasecmp(v, "yes")
				|| !strcasecmp(v, "true"));
		} else if (!strcasecmp(k, NSS_LDAP_KEY_ROOTSASLID)) {
			t = &result->ldc_rootsaslid;
		} else if (!strcasecmp(k, NSS_LDAP_KEY_SSLPATH)) {
			t = &result->ldc_sslpath;
		} else if (!strcasecmp(k, NSS_LDAP_KEY_SCOPE)) {
			if (!strcasecmp(v, "sub")) {
				result->ldc_scope = LDAP_SCOPE_SUBTREE;
			} else if (!strcasecmp(v, "one")) {
				result->ldc_scope = LDAP_SCOPE_ONELEVEL;
			} else if (!strcasecmp(v, "base")) {
				result->ldc_scope = LDAP_SCOPE_BASE;
			}
		} else if (!strcasecmp(k, NSS_LDAP_KEY_DEREF)) {
			if (!strcasecmp(v, "never")) {
				result->ldc_deref = LDAP_DEREF_NEVER;
			} else if (!strcasecmp(v, "searching")) {
				result->ldc_deref = LDAP_DEREF_SEARCHING;
			} else if (!strcasecmp(v, "finding")) {
				result->ldc_deref = LDAP_DEREF_FINDING;
			} else if (!strcasecmp(v, "always")) {
				result->ldc_deref = LDAP_DEREF_ALWAYS;
			}
		} else if (!strcasecmp(k, NSS_LDAP_KEY_PORT)) {
			result->ldc_port = atoi(v);
		} else if (!strcasecmp(k, NSS_LDAP_KEY_SSL)) {
			if (!strcasecmp(v, "on") || !strcasecmp(v, "yes")
				|| !strcasecmp(v, "true")) {
				result->ldc_ssl_on = SSL_LDAPS;
			} else if (!strcasecmp(v, "start_tls")) {
				result->ldc_ssl_on = SSL_START_TLS;
			}
		} else if (!strcasecmp(k, NSS_LDAP_KEY_REFERRALS)) {
			result->ldc_referrals = (!strcasecmp(v, "on")
				|| !strcasecmp(v, "yes")
				|| !strcasecmp(v, "true"));
		} else if (!strcasecmp(k, NSS_LDAP_KEY_RESTART)) {
			result->ldc_restart = (!strcasecmp(v, "on")
				|| !strcasecmp(v, "yes")
				|| !strcasecmp(v, "true"));
		} else if (!strcasecmp(k, NSS_LDAP_KEY_LDAP_VERSION)) {
			result->ldc_version = atoi(v);
		} else if (!strcasecmp(k, NSS_LDAP_KEY_TIMELIMIT)) {
			result->ldc_timelimit = atoi(v);
		} else if (!strcasecmp(k, NSS_LDAP_KEY_BIND_TIMELIMIT)) {
			result->ldc_bind_timelimit = atoi(v);
		} else if (!strcasecmp(k, NSS_LDAP_KEY_IDLE_TIMELIMIT)) {
			result->ldc_idle_timelimit = atoi(v);
		} else if (!strcasecmp(k, NSS_LDAP_KEY_RECONNECT_POLICY)) {
			if (!strcasecmp(v, "hard")) {
				result->ldc_reconnect_pol = LP_RECONNECT_HARD;
			} else if (!strcasecmp(v, "soft")) {
				result->ldc_reconnect_pol = LP_RECONNECT_SOFT;
			}
		} else if (!strcasecmp(k, "tls_checkpeer")) {
			if (!strcasecmp(v, "on") || !strcasecmp(v, "yes")
				|| !strcasecmp(v, "true")) {
				result->ldc_tls_checkpeer = 1;
			} else if (!strcasecmp(v, "off") || !strcasecmp(v, "no")
				|| !strcasecmp(v, "false")) {
				result->ldc_tls_checkpeer = 0;
			}
		} else if (!strcasecmp(k, "tls_cacertfile")) {
			t = &result->ldc_tls_cacertfile;
		} else if (!strcasecmp(k, "tls_cacertdir")) {
			t = &result->ldc_tls_cacertdir;
		} else if (!strcasecmp(k, "tls_ciphers")) {
			t = &result->ldc_tls_ciphers;
		} else if (!strcasecmp(k, "tls_cert")) {
			t = &result->ldc_tls_cert;
		} else if (!strcasecmp(k, "tls_key")) {
			t = &result->ldc_tls_key;
		} else if (!strcasecmp(k, "tls_randfile")) {
			t = &result->ldc_tls_randfile;
		}
#ifdef AT_OC_MAP
		else if (!strncasecmp(k, NSS_LDAP_KEY_MAP_ATTRIBUTE,
				strlen(NSS_LDAP_KEY_MAP_ATTRIBUTE))) {
			do_parse_map_statement(result, v, MAP_ATTRIBUTE);
		} else if (!strncasecmp(k, NSS_LDAP_KEY_MAP_OBJECTCLASS,
				strlen(NSS_LDAP_KEY_MAP_OBJECTCLASS))) {
			do_parse_map_statement(result, v, MAP_OBJECTCLASS);
		}
#endif				/* AT_OC_MAP */
		else {
			/*
			 * check whether the key is a naming context key if yes, parse; otherwise just return NSS_SUCCESS so we can ignore keys we don't understand.
			 */
			stat =
				do_searchdescriptorconfig(k, v, len, result->ldc_sds, &buffer,
				&buflen);
			if (stat == NSS_UNAVAIL) {
				break;
			}
		}

		if (t != NULL) {
			strncpy(buffer, v, len);
			buffer[len] = '\0';
			*t = buffer;
			buffer += len + 1;
			buflen -= len + 1;
		}
	}

	fclose(fp);

	if (stat != NSS_SUCCESS) {
		return stat;
	}
	if (result->ldc_rootbinddn != NULL) {
		fp = fopen(NSS_LDAP_PATH_ROOTPASSWD, "r");
		if (fp) {
			if (fgets(b, sizeof(b), fp) != NULL) {
				int len;

				len = strlen(b);
				if (len > 0)
					len--;

				if (buflen < (size_t) (len + 1)) {
					return NSS_UNAVAIL;
				}
				strncpy(buffer, b, len);
				buffer[len] = '\0';
				result->ldc_rootbindpw = buffer;
				buffer += len + 1;
				buflen -= len + 1;
			}
			fclose(fp);
		} else if (!result->ldc_rootusesasl) {
			result->ldc_rootbinddn = NULL;
		}
	}
	if (result->ldc_host == NULL
#ifdef HAVE_LDAP_INITIALIZE
		&& result->ldc_uri == NULL
#endif
		) {
		return NSS_NOTFOUND;
	}
	if (result->ldc_port == 0) {
#ifdef LDAPS_PORT
		if (result->ldc_ssl_on == SSL_LDAPS) {
			result->ldc_port = LDAPS_PORT;
		} else
#endif				/* SSL */
			result->ldc_port = LDAP_PORT;
	}
	return stat;
}

NSS_STATUS 
_nss_ldap_escape_string(const char *str, char *buf, size_t buflen)
{
	int ret = NSS_TRYAGAIN;
	char *p = buf;
	char *limit = p + buflen - 3;
	const char *s = str;

	while (p < limit && *s) {
		switch (*s) {
		case '*':
			strcpy(p, "\\2a");
			p += 3;
			break;
		case '(':
			strcpy(p, "\\28");
			p += 3;
			break;
		case ')':
			strcpy(p, "\\29");
			p += 3;
			break;
		case '\\':
			strcpy(p, "\\5c");
			p += 3;
			break;
		default:
			*p++ = *s;
			break;
		}
		s++;
	}

	if (*s == '\0') {
		/* got to end */
		*p = '\0';
		ret = NSS_SUCCESS;
	}
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1