diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2006-11-01 09:23:10 +0000 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2006-11-01 09:23:10 +0000 |
commit | 3fcd73560c9d83dd9c816f3a2d86d1b01a7af8ab (patch) | |
tree | 28509cef8d55d480d7965f54b61b82835f48dae2 /server/util.c | |
parent | 909352b52a35508ee8d76afed7b8003900a9ff41 (diff) |
move some remaining files into the server/ directory
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/libnss_ldapd@39 ef36b2f9-881f-0410-afb5-c4e39611909c
Diffstat (limited to 'server/util.c')
-rw-r--r-- | server/util.c | 1560 |
1 files changed, 1560 insertions, 0 deletions
diff --git a/server/util.c b/server/util.c new file mode 100644 index 0000000..786d0a8 --- /dev/null +++ b/server/util.c @@ -0,0 +1,1560 @@ +/* + Copyright (C) 1997-2005 Luke Howard + This file is part of the nss_ldap library. + Contributed by Luke Howard, <lukeh@padl.com>, 1997. + + 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. + + $Id$ +*/ + +#include "config.h" + +#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 +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif + +#include "ldap-nss.h" +#include "util.h" + +static enum nss_status do_getrdnvalue (const char *dn, + const char *rdntype, + char **rval, char **buffer, + size_t * buflen); + +static enum nss_status do_parse_map_statement (struct ldap_config * cfg, + const char *statement, + enum ldap_map_type type); + +static enum nss_status do_searchdescriptorconfig (const char *key, + const char *value, + size_t valueLength, + struct ldap_service_search_descriptor + ** result, char **buffer, + size_t * buflen); + +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 enum nss_status +dn2uid_cache_put (const char *dn, const char *uid) +{ + enum nss_status status; + struct ldap_datum key, val; + + cache_lock (); + + if (__cache == NULL) + { + __cache = _nss_ldap_db_open (); + if (__cache == NULL) + { + cache_unlock (); + return NSS_STATUS_TRYAGAIN; + } + } + + key.data = (void *) dn; + key.size = strlen (dn); + val.data = (void *) uid; + val.size = strlen (uid); + + status = _nss_ldap_db_put (__cache, 0, &key, &val); + + cache_unlock (); + + return status; +} + +static enum nss_status +dn2uid_cache_get (const char *dn, char **uid, char **buffer, size_t * buflen) +{ + struct ldap_datum key, val; + enum nss_status status; + + cache_lock (); + + if (__cache == NULL) + { + cache_unlock (); + return NSS_STATUS_NOTFOUND; + } + + key.data = (void *) dn; + key.size = strlen (dn); + + status = _nss_ldap_db_get (__cache, 0, &key, &val); + if (status != NSS_STATUS_SUCCESS) + { + cache_unlock (); + return status; + } + + if (*buflen <= val.size) + { + cache_unlock (); + return NSS_STATUS_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_STATUS_SUCCESS; +} + +enum nss_status +_nss_ldap_dn2uid (const char *dn, char **uid, char **buffer, size_t * buflen, + int *pIsNestedGroup, LDAPMessage ** pRes) +{ + enum nss_status status; + + debug ("==> _nss_ldap_dn2uid"); + + *pIsNestedGroup = 0; + + status = dn2uid_cache_get (dn, uid, buffer, buflen); + if (status == NSS_STATUS_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_STATUS_SUCCESS) + { + LDAPMessage *e = _nss_ldap_first_entry (res); + if (e != NULL) + { + if (_nss_ldap_oc_check (e, OC (posixGroup)) == NSS_STATUS_SUCCESS) + { + *pIsNestedGroup = 1; + *pRes = res; + debug ("<== _nss_ldap_dn2uid (nested group)"); + return NSS_STATUS_SUCCESS; + } + + status = + _nss_ldap_assign_attrval (e, ATM (LM_PASSWD, uid), uid, + buffer, buflen); + if (status == NSS_STATUS_SUCCESS) + dn2uid_cache_put (dn, *uid); + } + } + ldap_msgfree (res); + } + + debug ("<== _nss_ldap_dn2uid"); + + return status; +} + +enum nss_status +_nss_ldap_getrdnvalue (LDAPMessage * entry, + const char *rdntype, + char **rval, char **buffer, size_t * buflen) +{ + char *dn; + enum nss_status status; + + dn = _nss_ldap_get_dn (entry); + if (dn == NULL) + { + return NSS_STATUS_NOTFOUND; + } + + status = do_getrdnvalue (dn, rdntype, rval, buffer, buflen); +#ifdef HAVE_LDAP_MEMFREE + ldap_memfree (dn); +#else /* HAVE_LDAP_MEMFREE */ + free (dn); +#endif /* not 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_STATUS_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_STATUS_SUCCESS; + } + else + { + status = NSS_STATUS_TRYAGAIN; + } + ldap_value_free (vals); + } + } + + return status; +} + +static enum 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_STATUS_TRYAGAIN; + } + rdnvalue = *buffer; + strncpy (rdnvalue, r, rdnlen); + break; + } + } + ldap_value_free (exploded_rdn); + } +#else /* HAVE_LDAP_EXPLODE_RDN */ + /* + * 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 /* HAVE_STRTOK_R */ + +#ifndef HAVE_STRTOK_R + for (p = strtok (r, "+"); +#else /* HAVE_STRTOK_R */ + for (p = strtok_r (r, "+", &st); +#endif /* not HAVE_STRTOK_R */ + p != NULL; +#ifndef HAVE_STRTOK_R + p = strtok (NULL, "+")) +#else /* HAVE_STRTOK_R */ + p = strtok_r (NULL, "+", &st)) +#endif /* not HAVE_STRTOK_R */ + { + if (strncasecmp (p, rdnava, rdnavalen) == 0) + { + p += rdnavalen; + rdnlen = strlen (p); + if (*buflen <= rdnlen) + { + ldap_value_free (exploded_dn); + return NSS_STATUS_TRYAGAIN; + } + rdnvalue = *buffer; + strncpy (rdnvalue, p, rdnlen); + break; + } + if (r != NULL) + r = NULL; + } +#endif /* not 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_STATUS_SUCCESS; + } + + return NSS_STATUS_NOTFOUND; +} + +static enum nss_status +do_parse_map_statement (struct ldap_config * cfg, + const char *statement, enum ldap_map_type type) +{ + char *key, *val; + enum ldap_map_selector 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 enum 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 /* HAVE_STRTOK_R */ + 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_STATUS_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 /* HAVE_STRTOK_R */ + for (s = strtok(values, ","); s != NULL; s = strtok(NULL, ",")) +#endif /* not HAVE_STRTOK_R */ + { + int vallen; + char *elt = NULL; + + vallen = strlen (s); + if (buflen < (size_t) (vallen + 1)) + { + return NSS_STATUS_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_STATUS_SUCCESS; +} + +enum ldap_map_selector +_nss_ldap_str2selector (const char *key) +{ + enum ldap_map_selector 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 enum nss_status +do_searchdescriptorconfig (const char *key, const char *value, size_t len, + struct ldap_service_search_descriptor ** result, + char **buffer, size_t * buflen) +{ + struct ldap_service_search_descriptor **t, *cur; + char *base; + char *filter, *s; + int scope; + enum ldap_map_selector 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_STATUS_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_STATUS_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, struct ldap_service_search_descriptor) < + sizeof (struct ldap_service_search_descriptor)) + return NSS_STATUS_UNAVAIL; + + align (*buffer, *buflen, struct ldap_service_search_descriptor); + + for (cur = *t; cur && cur->lsd_next; cur = cur->lsd_next) + ; + if (!cur) + { + *t = (struct ldap_service_search_descriptor *) * buffer; + cur = *t; + } + else + { + cur->lsd_next = (struct ldap_service_search_descriptor *) * buffer; + cur = cur->lsd_next; + } + + cur->lsd_base = base; + cur->lsd_scope = scope; + cur->lsd_filter = filter; + cur->lsd_next = NULL; + + *buffer += sizeof (struct ldap_service_search_descriptor); + *buflen -= sizeof (struct ldap_service_search_descriptor); + + return NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_ldap_init_config (struct ldap_config * 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 /* LDAP_VERSION3 */ + result->ldc_version = LDAP_VERSION2; +#endif /* not 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; +#endif /* CONFIGURE_KRB5_CCNAME */ + result->ldc_flags = 0; +#ifdef RFC2307BIS + result->ldc_flags |= NSS_LDAP_FLAGS_RFC2307BIS; +#endif /* RFC2307BIS */ +#ifdef PAGE_RESULTS + result->ldc_flags |= NSS_LDAP_FLAGS_PAGED_RESULTS; +#endif /* PAGE_RESULTS */ + 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_STATUS_UNAVAIL; + } + } + + return NSS_STATUS_SUCCESS; +} + +enum nss_status +_nss_ldap_add_uri (struct ldap_config *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_STATUS_UNAVAIL; + } + + assert (i < NSS_LDAP_CONFIG_URI_MAX); + + uri_len = strlen (uri); + + if (*buflen < uri_len + 1) + return NSS_STATUS_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_STATUS_SUCCESS; +} + +static enum nss_status +do_add_uris (struct ldap_config *result, char *uris, + char **buffer, size_t *buflen) +{ + /* Add a space separated list of URIs */ + char *p; + enum nss_status status = NSS_STATUS_SUCCESS; + + for (p = uris; p != NULL; ) + { + char *q = strchr (p, ' '); + if (q != NULL) + *q = '\0'; + + status = _nss_ldap_add_uri (result, p, buffer, buflen); + + p = (q != NULL) ? ++q : NULL; + + if (status != NSS_STATUS_SUCCESS) + break; + } + + return status; +} + +static enum nss_status +do_add_hosts (struct ldap_config *result, char *hosts, + char **buffer, size_t *buflen) +{ + /* Add a space separated list of hosts */ + char *p; + enum nss_status status = NSS_STATUS_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); + + status = _nss_ldap_add_uri (result, b, buffer, buflen); + + p = (q != NULL) ? ++q : NULL; + + if (status != NSS_STATUS_SUCCESS) + break; + } + + return status; +} + +enum nss_status +_nss_ldap_readconfig (struct ldap_config ** presult, char **buffer, size_t *buflen) +{ + FILE *fp; + char b[NSS_LDAP_CONFIG_BUFSIZ]; + enum nss_status status = NSS_STATUS_SUCCESS; + struct ldap_config *result; + struct stat statbuf; + + if (bytesleft (*buffer, *buflen, struct ldap_config *) < sizeof (struct ldap_config)) + { + return NSS_STATUS_TRYAGAIN; + } + align (*buffer, *buflen, struct ldap_config *); + result = *presult = (struct ldap_config *) *buffer; + *buffer += sizeof (struct ldap_config); + *buflen -= sizeof (struct ldap_config); + + status = _nss_ldap_init_config (result); + if (status != NSS_STATUS_SUCCESS) + { + return NSS_STATUS_SUCCESS; + } + + fp = fopen (NSS_LDAP_PATH_CONF, "r"); + if (fp == NULL) + { + return NSS_STATUS_UNAVAIL; + } + + 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)) + { + status = NSS_STATUS_TRYAGAIN; + break; + } + + if (!strcasecmp (k, NSS_LDAP_KEY_HOST)) + { + status = do_add_hosts (result, v, buffer, buflen); + if (status != NSS_STATUS_SUCCESS) + break; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_URI)) + { + status = do_add_uris (result, v, buffer, buflen); + if (status != NSS_STATUS_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; + } +#endif /* CONFIGURE_KRB5_CCNAME */ + 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)) + { + status = do_parse_list (v, &result->ldc_initgroups_ignoreusers, + buffer, buflen); + if (status == NSS_STATUS_UNAVAIL) + { + break; + } + } + 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_STATUS_SUCCESS + * so we can ignore keys we don't understand. + */ + status = + do_searchdescriptorconfig (k, v, len, result->ldc_sds, + buffer, buflen); + if (status == NSS_STATUS_UNAVAIL) + { + break; + } + } + + if (t != NULL) + { + strncpy (*buffer, v, len); + (*buffer)[len] = '\0'; + *t = *buffer; + *buffer += len + 1; + *buflen -= len + 1; + } + } + + fclose (fp); + + if (status != NSS_STATUS_SUCCESS) + { + return status; + } + + 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)) + { + return NSS_STATUS_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) + { + if (result->ldc_ssl_on == SSL_LDAPS) + { + result->ldc_port = LDAPS_PORT; + } + else + { + result->ldc_port = LDAP_PORT; + } + } + + if (result->ldc_uris[0] == NULL) + { + status = NSS_STATUS_NOTFOUND; + } + + return status; +} + +enum nss_status +_nss_ldap_escape_string (const char *str, char *buf, size_t buflen) +{ + int ret = NSS_STATUS_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_STATUS_SUCCESS; + } + + return ret; +} + +/* XXX just a linked list for now */ + +struct ldap_dictionary +{ + struct ldap_datum key; + struct ldap_datum 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 (struct ldap_datum * 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 enum nss_status +do_dup_datum (unsigned flags, struct ldap_datum * dst, const struct ldap_datum * src) +{ + dst->data = malloc (src->size); + if (dst->data == NULL) + return NSS_STATUS_TRYAGAIN; + + memcpy (dst->data, src->data, src->size); + dst->size = src->size; + + return NSS_STATUS_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; + } +} + +enum nss_status +_nss_ldap_db_get (void *db, + unsigned flags, + const struct ldap_datum * key, + struct ldap_datum * 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_STATUS_SUCCESS; + } + } + + return NSS_STATUS_NOTFOUND; +} + +enum nss_status +_nss_ldap_db_put (void *db, + unsigned flags, + const struct ldap_datum * key, + const struct ldap_datum * 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_STATUS_TRYAGAIN; + } + + if (do_dup_datum (flags, &q->key, key) != NSS_STATUS_SUCCESS) + { + do_free_dictionary (q); + return NSS_STATUS_TRYAGAIN; + } + + if (do_dup_datum (flags, &q->value, value) != NSS_STATUS_SUCCESS) + { + do_free_dictionary (q); + return NSS_STATUS_TRYAGAIN; + } + + if (p != NULL) + p->next = q; + + return NSS_STATUS_SUCCESS; +} + +/* + * Add a nested netgroup or group to the namelist + */ +enum 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_STATUS_TRYAGAIN; + } + + nl->name = strdup (name); + if (nl->name == NULL) + { + debug ("<== _nss_ldap_namelist_push"); + free (nl); + return NSS_STATUS_TRYAGAIN; + } + + nl->next = *head; + + *head = nl; + + debug ("<== _nss_ldap_namelist_push"); + + return NSS_STATUS_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; +} + +enum nss_status _nss_ldap_validateconfig (struct ldap_config *config) +{ + struct stat statbuf; + + if (config == NULL) + { + return NSS_STATUS_UNAVAIL; + } + + if (config->ldc_mtime == 0) + { + return NSS_STATUS_SUCCESS; + } + + if (stat (NSS_LDAP_PATH_CONF, &statbuf) == 0) + { + return (statbuf.st_mtime > config->ldc_mtime) ? NSS_STATUS_TRYAGAIN : NSS_STATUS_SUCCESS; + } + + return NSS_STATUS_SUCCESS; +} + |