/* Copyright (C) 1997-2005 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"
#if defined(HAVE_THREAD_H) && !defined(_AIX)
#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 <sys/stat.h>
#include <netdb.h>
#include <syslog.h>
#include <string.h>
#include <fcntl.h>
#include <assert.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 "util.h"
static char rcsId[] = "$Id: util.c,v 2.138 2009/11/06 10:16:04 lukeh Exp $";
static NSS_STATUS do_getrdnvalue (const char *dn,
const char *rdntype,
char **rval, char **buffer,
size_t * buflen);
static NSS_STATUS do_parse_map_statement (ldap_config_t * cfg,
const char *statement,
ldap_map_type_t type);
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);
#include <fcntl.h>
static void *__cache = NULL;
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)
{
NSS_STATUS stat;
ldap_datum_t key, val;
cache_lock ();
if (__cache == NULL)
{
__cache = _nss_ldap_db_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);
stat = _nss_ldap_db_put (__cache, 0, &key, &val);
cache_unlock ();
return stat;
}
static NSS_STATUS
dn2uid_cache_get (const char *dn, char **uid, char **buffer, size_t * buflen)
{
ldap_datum_t key, val;
NSS_STATUS stat;
cache_lock ();
if (__cache == NULL)
{
cache_unlock ();
return NSS_NOTFOUND;
}
key.data = (void *) dn;
key.size = strlen (dn);
stat = _nss_ldap_db_get (__cache, 0, &key, &val);
if (stat != NSS_SUCCESS)
{
cache_unlock ();
return stat;
}
if (*buflen <= val.size)
{
cache_unlock ();
return NSS_TRYAGAIN;
}
*uid = *buffer;
memcpy (*uid, (char *) val.data, val.size);
(*uid)[val.size] = '\0';
*buffer += val.size + 1;
*buflen -= val.size + 1;
cache_unlock ();
return NSS_SUCCESS;
}
#ifdef HPUX
static int lock_inited = 0;
#endif
NSS_STATUS
_nss_ldap_dn2uid (const char *dn, char **uid, char **buffer, size_t * buflen,
int *pIsNestedGroup, LDAPMessage ** pRes)
{
NSS_STATUS stat;
debug ("==> _nss_ldap_dn2uid");
*pIsNestedGroup = 0;
#ifdef HPUX
/* XXX this is not thread-safe */
if (!lock_inited)
{
__thread_mutex_init (&__cache_lock, NULL);
lock_inited = 1;
}
#endif
stat = dn2uid_cache_get (dn, uid, buffer, buflen);
if (stat == NSS_NOTFOUND)
{
const char *attrs[4];
LDAPMessage *res;
attrs[0] = ATM (LM_PASSWD, uid);
attrs[1] = ATM (LM_GROUP, uniqueMember);
attrs[2] = AT (objectClass);
attrs[3] = NULL;
if (_nss_ldap_read (dn, attrs, &res) == NSS_SUCCESS)
{
LDAPMessage *e = _nss_ldap_first_entry (res);
if (e != NULL)
{
if (_nss_ldap_oc_check (e, OC (posixGroup)) == NSS_SUCCESS)
{
*pIsNestedGroup = 1;
*pRes = res;
debug ("<== _nss_ldap_dn2uid (nested group)");
return NSS_SUCCESS;
}
stat =
_nss_ldap_assign_attrval (e, ATM (LM_PASSWD, uid), uid,
buffer, buflen);
if (stat == NSS_SUCCESS)
dn2uid_cache_put (dn, *uid);
}
}
ldap_msgfree (res);
}
debug ("<== _nss_ldap_dn2uid");
return stat;
}
NSS_STATUS
_nss_ldap_getrdnvalue (LDAPMessage * entry,
const char *rdntype,
char **rval, char **buffer, size_t * buflen)
{
char *dn;
NSS_STATUS status;
dn = _nss_ldap_get_dn (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 = _nss_ldap_get_values (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;
}
static NSS_STATUS
do_parse_map_statement (ldap_config_t * cfg,
const char *statement, ldap_map_type_t type)
{
char *key, *val;
ldap_map_selector_t sel = LM_NONE;
key = (char *) statement;
val = key;
while (*val != ' ' && *val != '\t')
val++;
*(val++) = '\0';
while (*val == ' ' || *val == '\t')
val++;
{
char *p = strchr (key, ':');
if (p != NULL)
{
*p = '\0';
sel = _nss_ldap_str2selector (key);
key = ++p;
}
}
return _nss_ldap_map_put (cfg, sel, type, key, val);
}
/* parse a comma-separated list */
static NSS_STATUS
do_parse_list (char *values, char ***valptr,
char **pbuffer, size_t *pbuflen)
{
char *s, **p;
#ifdef HAVE_STRTOK_R
char *tok_r;
#endif
int valcount;
int buflen = *pbuflen;
char *buffer = *pbuffer;
/* comma separated list of values to ignore on initgroups() */
for (valcount = 1, s = values; *s != '\0'; s++)
{
if (*s == ',')
valcount++;
}
if (bytesleft (buffer, buflen, char *) < (valcount + 1) * sizeof (char *))
{
return NSS_UNAVAIL;
}
align (buffer, buflen, char *);
p = *valptr = (char **) buffer;
buffer += (valcount + 1) * sizeof (char *);
buflen -= (valcount + 1) * sizeof (char *);
#ifdef HAVE_STRTOK_R
for (s = strtok_r(values, ",", &tok_r); s != NULL;
s = strtok_r(NULL, ",", &tok_r))
#else
for (s = strtok(values, ","); s != NULL; s = strtok(NULL, ","))
#endif
{
int vallen;
char *elt = NULL;
vallen = strlen (s);
if (buflen < (size_t) (vallen + 1))
{
return NSS_UNAVAIL;
}
/* copy this value into the next block of buffer space */
elt = buffer;
buffer += vallen + 1;
buflen -= vallen + 1;
strncpy (elt, s, vallen);
elt[vallen] = '\0';
*p++ = elt;
}
*p = NULL;
*pbuffer = buffer;
*pbuflen = buflen;
return NSS_SUCCESS;
}
ldap_map_selector_t
_nss_ldap_str2selector (const char *key)
{
ldap_map_selector_t sel;
if (!strcasecmp (key, MP_passwd))
sel = LM_PASSWD;
else if (!strcasecmp (key, MP_shadow))
sel = LM_SHADOW;
else if (!strcasecmp (key, MP_group))
sel = LM_GROUP;
else if (!strcasecmp (key, MP_hosts))
sel = LM_HOSTS;
else if (!strcasecmp (key, MP_services))
sel = LM_SERVICES;
else if (!strcasecmp (key, MP_networks))
sel = LM_NETWORKS;
else if (!strcasecmp (key, MP_protocols))
sel = LM_PROTOCOLS;
else if (!strcasecmp (key, MP_rpc))
sel = LM_RPC;
else if (!strcasecmp (key, MP_ethers))
sel = LM_ETHERS;
else if (!strcasecmp (key, MP_netmasks))
sel = LM_NETMASKS;
else if (!strcasecmp (key, MP_bootparams))
sel = LM_BOOTPARAMS;
else if (!strcasecmp (key, MP_aliases))
sel = LM_ALIASES;
else if (!strcasecmp (key, MP_netgroup))
sel = LM_NETGROUP;
else if (!strcasecmp (key, MP_automount))
sel = LM_AUTOMOUNT;
else
sel = LM_NONE;
return sel;
}
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;
ldap_map_selector_t sel;
t = NULL;
filter = NULL;
scope = -1;
if (strncasecmp (key, NSS_LDAP_KEY_NSS_BASE_PREFIX,
NSS_LDAP_KEY_NSS_BASE_PREFIX_LEN) != 0)
return NSS_SUCCESS;
sel = _nss_ldap_str2selector (&key[NSS_LDAP_KEY_NSS_BASE_PREFIX_LEN]);
t = (sel < LM_NONE) ? &result[sel] : NULL;
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;
}
NSS_STATUS _nss_ldap_init_config (ldap_config_t * result)
{
int i, j;
memset (result, 0, sizeof (*result));
result->ldc_scope = LDAP_SCOPE_SUBTREE;
result->ldc_deref = LDAP_DEREF_NEVER;
result->ldc_base = NULL;
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_tls_checkpeer = -1;
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_OPEN;
result->ldc_sasl_secprops = NULL;
result->ldc_srv_domain = NULL;
result->ldc_logdir = NULL;
result->ldc_debug = 0;
result->ldc_pagesize = LDAP_PAGESIZE;
#ifdef CONFIGURE_KRB5_CCNAME
result->ldc_krb5_ccname = NULL;
result->ldc_krb5_rootccname = NULL;
result->ldc_krb5_autorenew = 0;
result->ldc_krb5_rootautorenew = 0;
#endif /* CONFIGURE_KRB5_CCNAME */
#ifdef CONFIGURE_KRB5_KEYTAB
result->ldc_krb5_keytabname = NULL;
result->ldc_krb5_rootkeytabname = NULL;
result->ldc_krb5_usekeytab = 0;
result->ldc_krb5_rootusekeytab = 0;
#endif /* CONFIGURE_KRB5_KEYTAB */
result->ldc_flags = 0;
#ifdef RFC2307BIS
result->ldc_flags |= NSS_LDAP_FLAGS_RFC2307BIS;
#endif
#ifdef PAGE_RESULTS
result->ldc_flags |= NSS_LDAP_FLAGS_PAGED_RESULTS;
#endif
result->ldc_reconnect_tries = LDAP_NSS_TRIES;
result->ldc_reconnect_sleeptime = LDAP_NSS_SLEEPTIME;
result->ldc_reconnect_maxsleeptime = LDAP_NSS_MAXSLEEPTIME;
result->ldc_reconnect_maxconntries = LDAP_NSS_MAXCONNTRIES;
result->ldc_initgroups_ignoreusers = NULL;
for (i = 0; i <= LM_NONE; i++)
{
for (j = 0; j <= MAP_MAX; j++)
{
result->ldc_maps[i][j] = _nss_ldap_db_open ();
if (result->ldc_maps[i][j] == NULL)
return NSS_UNAVAIL;
}
}
return NSS_SUCCESS;
}
NSS_STATUS
_nss_ldap_add_uri (ldap_config_t *result, const char *uri,
char **buffer, size_t *buflen)
{
/* add a single URI to the list of URIs in the configuration */
int i;
size_t uri_len;
debug ("==> _nss_ldap_add_uri");
for (i = 0; result->ldc_uris[i] != NULL; i++)
;
if (i == NSS_LDAP_CONFIG_URI_MAX)
{
debug ("<== _nss_ldap_add_uri: maximum number of URIs exceeded");
return NSS_UNAVAIL;
}
assert (i < NSS_LDAP_CONFIG_URI_MAX);
uri_len = strlen (uri);
if (*buflen < uri_len + 1)
return NSS_TRYAGAIN;
memcpy (*buffer, uri, uri_len + 1);
result->ldc_uris[i] = *buffer;
result->ldc_uris[i + 1] = NULL;
*buffer += uri_len + 1;
*buflen -= uri_len + 1;
debug ("<== _nss_ldap_add_uri: added URI %s", uri);
return NSS_SUCCESS;
}
static NSS_STATUS
do_add_uris (ldap_config_t *result, char *uris,
char **buffer, size_t *buflen)
{
/* Add a space separated list of URIs */
char *p;
NSS_STATUS stat = NSS_SUCCESS;
for (p = uris; p != NULL; )
{
char *q = strchr (p, ' ');
if (q != NULL)
*q = '\0';
stat = _nss_ldap_add_uri (result, p, buffer, buflen);
p = (q != NULL) ? ++q : NULL;
if (stat != NSS_SUCCESS)
break;
}
return stat;
}
static NSS_STATUS
do_add_hosts (ldap_config_t *result, char *hosts,
char **buffer, size_t *buflen)
{
/* Add a space separated list of hosts */
char *p;
NSS_STATUS stat = NSS_SUCCESS;
for (p = hosts; p != NULL; )
{
char b[NSS_LDAP_CONFIG_BUFSIZ];
char *q = strchr (p, ' ');
if (q != NULL)
*q = '\0';
snprintf (b, sizeof(b), "ldap://%s", p);
stat = _nss_ldap_add_uri (result, b, buffer, buflen);
p = (q != NULL) ? ++q : NULL;
if (stat != NSS_SUCCESS)
break;
}
return stat;
}
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;
struct stat statbuf;
fp = fopen (NSS_LDAP_PATH_CONF, "r");
if (fp == NULL)
{
return NSS_UNAVAIL;
}
if (bytesleft (*buffer, *buflen, ldap_config_t *) < sizeof (ldap_config_t))
{
fclose (fp);
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);
stat = _nss_ldap_init_config (result);
if (stat != NSS_SUCCESS)
{
fclose (fp);
return NSS_SUCCESS;
}
if (fstat (fileno (fp), &statbuf) == 0)
result->ldc_mtime = statbuf.st_mtime;
else
result->ldc_mtime = 0;
while (fgets (b, sizeof (b), fp) != NULL)
{
char *k, *v;
int len;
char **t = NULL;
if (*b == '\n' || *b == '\r' || *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 empty lines with more than 3 spaces at the start of the line */
/* rds.oliver@samera.com.py 01-set-2004 */
if (*v == '\n')
continue;
/* 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 */
/* Also remove \r (CR) to be able to handle files in DOS format (lines
* terminated in CR LF). Alejandro Forero Cuervo
* <azul@freaks-unidos.net>, 10-may-2005 */
len = strlen (v) - 1;
while (v[len] == ' ' || v[len] == '\t' || v[len] == '\n' || v[len] == '\r')
--len;
v[++len] = '\0';
if (*buflen < (size_t) (len + 1))
{
stat = NSS_TRYAGAIN;
break;
}
if (!strcasecmp (k, NSS_LDAP_KEY_HOST))
{
stat = do_add_hosts (result, v, buffer, buflen);
if (stat != NSS_SUCCESS)
break;
}
else if (!strcasecmp (k, NSS_LDAP_KEY_URI))
{
stat = do_add_uris (result, v, buffer, buflen);
if (stat != NSS_SUCCESS)
break;
}
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") ||
!strcasecmp (v, "hard_open"))
{
result->ldc_reconnect_pol = LP_RECONNECT_HARD_OPEN;
}
else if (!strcasecmp (v, "hard_init"))
{
result->ldc_reconnect_pol = LP_RECONNECT_HARD_INIT;
}
else if (!strcasecmp (v, "soft"))
{
result->ldc_reconnect_pol = LP_RECONNECT_SOFT;
}
}
else if (!strcasecmp (k, NSS_LDAP_KEY_RECONNECT_TRIES))
{
result->ldc_reconnect_tries = atoi (v);
}
else if (!strcasecmp (k, NSS_LDAP_KEY_RECONNECT_SLEEPTIME))
{
result->ldc_reconnect_sleeptime = atoi (v);
}
else if (!strcasecmp (k, NSS_LDAP_KEY_RECONNECT_MAXSLEEPTIME))
{
result->ldc_reconnect_maxsleeptime = atoi (v);
}
else if (!strcasecmp (k, NSS_LDAP_KEY_RECONNECT_MAXCONNTRIES))
{
result->ldc_reconnect_maxconntries = atoi (v);
}
else if (!strcasecmp (k, NSS_LDAP_KEY_SASL_SECPROPS))
{
t = &result->ldc_sasl_secprops;
}
else if (!strcasecmp (k, NSS_LDAP_KEY_LOGDIR))
{
t = &result->ldc_logdir;
}
else if (!strcasecmp (k, NSS_LDAP_KEY_DEBUG))
{
result->ldc_debug = atoi (v);
}
else if (!strcasecmp (k, NSS_LDAP_KEY_PAGESIZE))
{
result->ldc_pagesize = atoi (v);
}
#ifdef CONFIGURE_KRB5_CCNAME
else if (!strcasecmp (k, NSS_LDAP_KEY_KRB5_CCNAME))
{
t = &result->ldc_krb5_ccname;
}
else if (!strcasecmp (k, NSS_LDAP_KEY_KRB5_ROOTCCNAME))
{
t = &result->ldc_krb5_rootccname;
}
else if (!strcasecmp (k, NSS_LDAP_KEY_KRB5_AUTORENEW))
{
result->ldc_krb5_autorenew = atoi (v);
}
else if (!strcasecmp (k, NSS_LDAP_KEY_KRB5_ROOTAUTORENEW))
{
result->ldc_krb5_rootautorenew = atoi (v);
}
#endif /* CONFIGURE_KRB5_CCNAME */
#ifdef CONFIGURE_KRB5_KEYTAB
else if (!strcasecmp (k, NSS_LDAP_KEY_KRB5_KEYTAB))
{
t = &result->ldc_krb5_keytabname;
}
else if (!strcasecmp (k, NSS_LDAP_KEY_KRB5_ROOTKEYTAB))
{
t = &result->ldc_krb5_rootkeytabname;
}
else if (!strcasecmp (k, NSS_LDAP_KEY_KRB5_USEKEYTAB))
{
result->ldc_krb5_usekeytab = atoi (v);
}
else if (!strcasecmp (k, NSS_LDAP_KEY_KRB5_ROOTUSEKEYTAB))
{
result->ldc_krb5_rootusekeytab = atoi (v);
}
#endif /* CONFIGURE_KRB5_KEYTAB */
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;
}
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);
}
else if (!strncasecmp (k, NSS_LDAP_KEY_SET_OVERRIDE,
strlen (NSS_LDAP_KEY_SET_OVERRIDE)))
{
do_parse_map_statement (result, v, MAP_OVERRIDE);
}
else if (!strncasecmp (k, NSS_LDAP_KEY_SET_DEFAULT,
strlen (NSS_LDAP_KEY_SET_DEFAULT)))
{
do_parse_map_statement (result, v, MAP_DEFAULT);
}
else if (!strcasecmp (k, NSS_LDAP_KEY_INITGROUPS))
{
if (!strcasecmp (v, "backlink"))
{
result->ldc_flags |= NSS_LDAP_FLAGS_INITGROUPS_BACKLINK;
}
else
{
result->ldc_flags &= ~(NSS_LDAP_FLAGS_INITGROUPS_BACKLINK);
}
}
else if (!strcasecmp (k, NSS_LDAP_KEY_SCHEMA))
{
if (!strcasecmp (v, "rfc2307bis"))
{
result->ldc_flags |= NSS_LDAP_FLAGS_RFC2307BIS;
}
else if (!strcasecmp (v, "rfc2307"))
{
result->ldc_flags &= ~(NSS_LDAP_FLAGS_RFC2307BIS);
}
}
else if (!strcasecmp (k, NSS_LDAP_KEY_PAGED_RESULTS))
{
if (!strcasecmp (v, "on")
|| !strcasecmp (v, "yes")
|| !strcasecmp (v, "true"))
{
result->ldc_flags |= NSS_LDAP_FLAGS_PAGED_RESULTS;
}
else
{
result->ldc_flags &= ~(NSS_LDAP_FLAGS_PAGED_RESULTS);
}
}
else if (!strcasecmp (k, NSS_LDAP_KEY_INITGROUPS_IGNOREUSERS))
{
stat = do_parse_list (v, &result->ldc_initgroups_ignoreusers,
buffer, buflen);
if (stat == NSS_UNAVAIL)
{
break;
}
}
else if (!strcasecmp (k, NSS_LDAP_KEY_GETGRENT_SKIPMEMBERS))
{
if (!strcasecmp (v, "on") || !strcasecmp (v, "yes")
|| !strcasecmp (v, "true"))
{
result->ldc_flags |= NSS_LDAP_FLAGS_GETGRENT_SKIPMEMBERS;
}
else if (!strcasecmp (v, "off") || !strcasecmp (v, "no")
|| !strcasecmp (v, "false"))
{
result->ldc_flags &= ~(NSS_LDAP_FLAGS_GETGRENT_SKIPMEMBERS);
}
}
else if (!strcasecmp (k, NSS_LDAP_KEY_CONNECT_POLICY))
{
if (!strcasecmp (v, "oneshot"))
{
result->ldc_flags |= NSS_LDAP_FLAGS_CONNECT_POLICY_ONESHOT;
}
else if (!strcasecmp (v, "persist"))
{
result->ldc_flags &= ~(NSS_LDAP_FLAGS_CONNECT_POLICY_ONESHOT);
}
}
else if (!strcasecmp (k, NSS_LDAP_KEY_SRV_DOMAIN))
{
t = &result->ldc_srv_domain;
}
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);
/* BUG#138: check for newline before removing */
if (len > 0 && b[len - 1] == '\n')
len--;
if (*buflen < (size_t) (len + 1))
{
fclose (fp);
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_port == 0 &&
result->ldc_ssl_on == SSL_LDAPS)
{
result->ldc_port = LDAPS_PORT;
}
if (result->ldc_uris[0] == NULL)
{
stat = NSS_NOTFOUND;
}
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;
}
/* XXX just a linked list for now */
struct ldap_dictionary
{
ldap_datum_t key;
ldap_datum_t value;
struct ldap_dictionary *next;
};
static struct ldap_dictionary *
do_alloc_dictionary (void)
{
struct ldap_dictionary *dict;
dict = malloc (sizeof (*dict));
if (dict == NULL)
{
return NULL;
}
NSS_LDAP_DATUM_ZERO (&dict->key);
NSS_LDAP_DATUM_ZERO (&dict->value);
dict->next = NULL;
return dict;
}
static void
do_free_datum (ldap_datum_t * datum)
{
if (datum->data != NULL)
{
free (datum->data);
datum->data = NULL;
}
datum->size = 0;
}
static struct ldap_dictionary *
do_find_last (struct ldap_dictionary *dict)
{
struct ldap_dictionary *p;
for (p = dict; p->next != NULL; p = p->next)
;
return p;
}
static void
do_free_dictionary (struct ldap_dictionary *dict)
{
do_free_datum (&dict->key);
do_free_datum (&dict->value);
free (dict);
}
static NSS_STATUS
do_dup_datum (unsigned flags, ldap_datum_t * dst, const ldap_datum_t * src)
{
dst->data = malloc (src->size);
if (dst->data == NULL)
return NSS_TRYAGAIN;
memcpy (dst->data, src->data, src->size);
dst->size = src->size;
return NSS_SUCCESS;
}
void *
_nss_ldap_db_open (void)
{
return (void *) do_alloc_dictionary ();
}
void
_nss_ldap_db_close (void *db)
{
struct ldap_dictionary *dict;
dict = (struct ldap_dictionary *) db;
while (dict != NULL)
{
struct ldap_dictionary *next = dict->next;
do_free_dictionary (dict);
dict = next;
}
}
NSS_STATUS
_nss_ldap_db_get (void *db,
unsigned flags,
const ldap_datum_t * key,
ldap_datum_t * value)
{
struct ldap_dictionary *dict = (struct ldap_dictionary *) db;
struct ldap_dictionary *p;
for (p = dict; p != NULL; p = p->next)
{
int cmp;
if (p->key.size != key->size)
continue;
if (flags & NSS_LDAP_DB_NORMALIZE_CASE)
cmp = strncasecmp ((char *)p->key.data, (char *)key->data, key->size);
else
cmp = memcmp (p->key.data, key->data, key->size);
if (cmp == 0)
{
value->data = p->value.data;
value->size = p->value.size;
return NSS_SUCCESS;
}
}
return NSS_NOTFOUND;
}
NSS_STATUS
_nss_ldap_db_put (void *db,
unsigned flags,
const ldap_datum_t * key,
const ldap_datum_t * value)
{
struct ldap_dictionary *dict = (struct ldap_dictionary *) db;
struct ldap_dictionary *p, *q;
assert (key != NULL);
assert (key->data != NULL);
if (dict->key.data == NULL)
{
/* uninitialized */
q = dict;
p = NULL;
}
else
{
p = do_find_last (dict);
assert (p != NULL);
assert (p->next == NULL);
q = do_alloc_dictionary ();
if (q == NULL)
return NSS_TRYAGAIN;
}
if (do_dup_datum (flags, &q->key, key) != NSS_SUCCESS)
{
do_free_dictionary (q);
return NSS_TRYAGAIN;
}
if (do_dup_datum (flags, &q->value, value) != NSS_SUCCESS)
{
do_free_dictionary (q);
return NSS_TRYAGAIN;
}
if (p != NULL)
p->next = q;
return NSS_SUCCESS;
}
/*
* Add a nested netgroup or group to the namelist
*/
NSS_STATUS
_nss_ldap_namelist_push (struct name_list **head, const char *name)
{
struct name_list *nl;
debug ("==> _nss_ldap_namelist_push (%s)", name);
nl = (struct name_list *) malloc (sizeof (*nl));
if (nl == NULL)
{
debug ("<== _nss_ldap_namelist_push");
return NSS_TRYAGAIN;
}
nl->name = strdup (name);
if (nl->name == NULL)
{
debug ("<== _nss_ldap_namelist_push");
free (nl);
return NSS_TRYAGAIN;
}
nl->next = *head;
*head = nl;
debug ("<== _nss_ldap_namelist_push");
return NSS_SUCCESS;
}
/*
* Remove last nested netgroup or group from the namelist
*/
void
_nss_ldap_namelist_pop (struct name_list **head)
{
struct name_list *nl;
debug ("==> _nss_ldap_namelist_pop");
assert (*head != NULL);
nl = *head;
*head = nl->next;
assert (nl->name != NULL);
free (nl->name);
free (nl);
debug ("<== _nss_ldap_namelist_pop");
}
/*
* Cleanup nested netgroup or group namelist.
*/
void
_nss_ldap_namelist_destroy (struct name_list **head)
{
struct name_list *p, *next;
debug ("==> _nss_ldap_namelist_destroy");
for (p = *head; p != NULL; p = next)
{
next = p->next;
if (p->name != NULL)
free (p->name);
free (p);
}
*head = NULL;
debug ("<== _nss_ldap_namelist_destroy");
}
/*
* Check whether we have already seen a netgroup or group,
* to avoid loops in nested netgroup traversal
*/
int
_nss_ldap_namelist_find (struct name_list *head, const char *netgroup)
{
struct name_list *p;
int found = 0;
debug ("==> _nss_ldap_namelist_find");
for (p = head; p != NULL; p = p->next)
{
if (strcasecmp (p->name, netgroup) == 0)
{
found++;
break;
}
}
debug ("<== _nss_ldap_namelist_find");
return found;
}
NSS_STATUS _nss_ldap_validateconfig (ldap_config_t *config)
{
struct stat statbuf;
if (config == NULL)
{
return NSS_UNAVAIL;
}
if (config->ldc_mtime == 0)
{
return NSS_SUCCESS;
}
if (stat (NSS_LDAP_PATH_CONF, &statbuf) == 0)
{
return (statbuf.st_mtime > config->ldc_mtime) ? NSS_TRYAGAIN : NSS_SUCCESS;
}
return NSS_SUCCESS;
}
/*
* Parse a text string into a long integer. If we fail for
* any reason, store the passed-in default value and return
* an error.
*/
NSS_STATUS
_nss_ldap_parse_long (const char *text, long default_value, long *value)
{
char *p;
long l;
if (text == NULL || strlen(text) == 0)
{
*value = default_value;
return NSS_NOTFOUND;
}
l = strtol(text, &p, 10);
if (p == NULL || p == text || *p != '\0')
{
*value = default_value;
return NSS_NOTFOUND;
}
*value = l;
return NSS_SUCCESS;
}
NSS_STATUS
_nss_ldap_parse_ulong (const char *text, unsigned long default_value,
unsigned long *value)
{
char *p;
unsigned long l;
if (text == NULL || strlen(text) == 0)
{
*value = default_value;
return NSS_NOTFOUND;
}
l = strtoul(text, &p, 10);
if (p == NULL || p == text || *p != '\0')
{
*value = default_value;
return NSS_NOTFOUND;
}
*value = l;
return NSS_SUCCESS;
}
NSS_STATUS
_nss_ldap_parse_int (const char *text, int default_value, int *value)
{
char *p;
long l;
if (text == NULL || strlen(text) == 0)
{
*value = default_value;
return NSS_NOTFOUND;
}
l = strtol(text, &p, 10);
if (p == NULL || p == text || *p != '\0')
{
*value = default_value;
return NSS_NOTFOUND;
}
if (l < INT_MIN || l > INT_MAX)
{
*value = default_value;
return NSS_NOTFOUND;
}
*value = l;
return NSS_SUCCESS;
}
NSS_STATUS
_nss_ldap_parse_uid_t (const char *text, uid_t default_value, uid_t *value)
{
char *p;
unsigned long l;
if (text == NULL || strlen(text) == 0)
{
*value = default_value;
return NSS_NOTFOUND;
}
l = strtoul(text, &p, 10);
if (p == NULL || p == text || *p != '\0')
{
*value = default_value;
return NSS_NOTFOUND;
}
#if SIZEOF_UID_T == SIZEOF_UNSIGNED_INT
if (l > UINT_MAX)
{
*value = default_value;
return NSS_NOTFOUND;
}
#endif
*value = l;
return NSS_SUCCESS;
}
NSS_STATUS
_nss_ldap_parse_gid_t (const char *text, gid_t default_value, gid_t *value)
{
char *p;
unsigned long l;
if (text == NULL || strlen(text) == 0)
{
*value = default_value;
return NSS_NOTFOUND;
}
l = strtoul(text, &p, 10);
if (p == NULL || p == text || *p != '\0')
{
*value = default_value;
return NSS_NOTFOUND;
}
#if SIZEOF_GID_T == SIZEOF_UNSIGNED_INT
if (l > UINT_MAX)
{
*value = default_value;
return NSS_NOTFOUND;
}
#endif
*value = l;
return NSS_SUCCESS;
}