From fd71dd3052ee9e4f8aef9f8a12624de86080ece8 Mon Sep 17 00:00:00 2001
From: Ryan Bloom
Date: Tue, 16 Oct 2001 21:29:07 +0000
Subject: [PATCH] Initial revision
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@91504 13f79535-47bb-0310-9956-ffa450edef68
---
modules/experimental/mod_auth_ldap.c | 862 +++++++++++++++
modules/experimental/util_ldap.c | 1105 ++++++++++++++++++++
modules/experimental/util_ldap_cache.c | 309 ++++++
modules/experimental/util_ldap_cache.h | 214 ++++
modules/experimental/util_ldap_cache_mgr.c | 537 ++++++++++
5 files changed, 3027 insertions(+)
create mode 100644 modules/experimental/mod_auth_ldap.c
create mode 100644 modules/experimental/util_ldap.c
create mode 100644 modules/experimental/util_ldap_cache.c
create mode 100644 modules/experimental/util_ldap_cache.h
create mode 100644 modules/experimental/util_ldap_cache_mgr.c
diff --git a/modules/experimental/mod_auth_ldap.c b/modules/experimental/mod_auth_ldap.c
new file mode 100644
index 0000000000..6be891cf17
--- /dev/null
+++ b/modules/experimental/mod_auth_ldap.c
@@ -0,0 +1,862 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+/*
+ * mod_auth_ldap.c: LDAP authentication module
+ *
+ * Original code from auth_ldap module for Apache v1.3:
+ * Copyright 1998, 1999 Enbridge Pipelines Inc.
+ * Copyright 1999-2001 Dave Carrigan
+ */
+
+#include
+#include
+
+#include "ap_config.h"
+#if APR_HAVE_UNISTD_H
+/* for getpid() */
+#include
+#endif
+#include
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "util_ldap.h"
+
+/* per directory configuration */
+typedef struct {
+ apr_pool_t *pool; /* Pool that this config is allocated from */
+ apr_lock_t *lock; /* Lock for this config */
+ int auth_authoritative; /* Is this auth method the one and only? */
+ int enabled; /* Is auth_ldap enabled in this directory? */
+
+ /* These parameters are all derived from the AuthLDAPURL directive */
+ char *url; /* String representation of the URL */
+
+ char *host; /* Name of the LDAP server (or space separated list) */
+ int port; /* Port of the LDAP server */
+ char *basedn; /* Base DN to do all searches from */
+ char *attribute; /* Attribute to search for */
+ char **attributes; /* Array of all the attributes to return */
+ int scope; /* Scope of the search */
+ char *filter; /* Filter to further limit the search */
+ deref_options deref; /* how to handle alias dereferening */
+ char *binddn; /* DN to bind to server (can be NULL) */
+ char *bindpw; /* Password to bind to server (can be NULL) */
+
+ int frontpage_hack; /* Hack for frontpage support */
+ int user_is_dn; /* If true, connection->user is DN instead of userid */
+ int compare_dn_on_server; /* If true, will use server to do DN compare */
+
+ int have_ldap_url; /* Set if we have found an LDAP url */
+
+ apr_array_header_t *groupattr; /* List of Group attributes */
+ int group_attrib_is_dn; /* If true, the group attribute is the DN, otherwise,
+ it's the exact string passed by the HTTP client */
+
+ int netscapessl; /* True if Netscape SSL is enabled */
+ int starttls; /* True if StartTLS is enabled */
+} mod_auth_ldap_config_t;
+
+typedef struct mod_auth_ldap_request_t {
+ char *dn; /* The saved dn from a successful search */
+ char *user; /* The username provided by the client */
+} mod_auth_ldap_request_t;
+
+/* maximum group elements supported */
+#define GROUPATTR_MAX_ELTS 10
+
+struct mod_auth_ldap_groupattr_entry_t {
+ char *name;
+};
+
+module AP_MODULE_DECLARE_DATA auth_ldap_module;
+
+/* function prototypes */
+void mod_auth_ldap_build_filter(char *filtbuf,
+ request_rec *r,
+ mod_auth_ldap_config_t *sec);
+int mod_auth_ldap_check_user_id(request_rec *r);
+int mod_auth_ldap_auth_checker(request_rec *r);
+void *mod_auth_ldap_create_dir_config(apr_pool_t *p, char *d);
+
+/* ---------------------------------------- */
+
+
+/*
+ * Build the search filter, or at least as much of the search filter that
+ * will fit in the buffer. We don't worry about the buffer not being able
+ * to hold the entire filter. If the buffer wasn't big enough to hold the
+ * filter, ldap_search_s will complain, but the only situation where this
+ * is likely to happen is if the client sent a really, really long
+ * username, most likely as part of an attack.
+ *
+ * The search filter consists of the filter provided with the URL,
+ * combined with a filter made up of the attribute provided with the URL,
+ * and the actual username passed by the HTTP client. For example, assume
+ * that the LDAP URL is
+ *
+ * ldap://ldap.airius.com/ou=People, o=Airius?uid??(posixid=*)
+ *
+ * Further, assume that the userid passed by the client was `userj'. The
+ * search filter will be (&(posixid=*)(uid=userj)).
+ */
+#define FILTER_LENGTH MAX_STRING_LEN
+void mod_auth_ldap_build_filter(char *filtbuf,
+ request_rec *r,
+ mod_auth_ldap_config_t *sec)
+{
+ char *p, *q, *filtbuf_end;
+ /*
+ * Create the first part of the filter, which consists of the
+ * config-supplied portions.
+ */
+ apr_snprintf(filtbuf, FILTER_LENGTH, "(&(%s)(%s=", sec->filter, sec->attribute);
+
+ /*
+ * Now add the client-supplied username to the filter, ensuring that any
+ * LDAP filter metachars are escaped.
+ */
+ filtbuf_end = filtbuf + FILTER_LENGTH - 1;
+ for (p = r->user, q=filtbuf + strlen(filtbuf);
+ *p && q < filtbuf_end; *q++ = *p++) {
+ if (strchr("*()\\", *p) != NULL) {
+ *q++ = '\\';
+ if (q >= filtbuf_end) {
+ break;
+ }
+ }
+ }
+ *q = '\0';
+
+ /*
+ * Append the closing parens of the filter, unless doing so would
+ * overrun the buffer.
+ */
+ if (q + 2 <= filtbuf_end)
+ strcat(filtbuf, "))");
+}
+
+
+/*
+ * Authentication Phase
+ * --------------------
+ *
+ * This phase authenticates the credentials the user has sent with
+ * the request (ie the username and password are checked). This is done
+ * by making an attempt to bind to the LDAP server using this user's
+ * DN and the supplied password.
+ *
+ */
+int mod_auth_ldap_check_user_id(request_rec *r)
+{
+ const char **vals = NULL;
+ char filtbuf[FILTER_LENGTH];
+ mod_auth_ldap_config_t *sec =
+ (mod_auth_ldap_config_t *)ap_get_module_config(r->per_dir_config, &auth_ldap_module);
+
+ util_ldap_connection_t *ldc = NULL;
+ const char *sent_pw;
+ int result = 0;
+ const char *dn = NULL;
+
+ mod_auth_ldap_request_t *req =
+ (mod_auth_ldap_request_t *)apr_pcalloc(r->pool, sizeof(mod_auth_ldap_request_t));
+ ap_set_module_config(r->request_config, &auth_ldap_module, req);
+
+ if (!sec->enabled) {
+ return DECLINED;
+ }
+
+ /*
+ * Basic sanity checks before any LDAP operations even happen.
+ */
+ if (!sec->have_ldap_url) {
+ return DECLINED;
+ }
+
+ /* There is a good AuthLDAPURL, right? */
+ if (sec->host) {
+ ldc = util_ldap_connection_find(r, sec->host, sec->port,
+ sec->binddn, sec->bindpw, sec->deref,
+ sec->netscapessl, sec->starttls);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authenticate: no sec->host - weird...?", getpid());
+ return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authenticate: using URL %s", getpid(), sec->url);
+
+ /* Get the password that the client sent */
+ if ((result = ap_get_basic_auth_pw(r, &sent_pw))) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authenticate: "
+ "ap_get_basic_auth_pw() returns %d", getpid(), result);
+ util_ldap_connection_close(ldc);
+ return result;
+ }
+
+ /* build the username filter */
+ mod_auth_ldap_build_filter(filtbuf, r, sec);
+
+ /* do the user search */
+ result = util_ldap_cache_checkuserid(r, ldc, sec->url, sec->basedn, sec->scope,
+ sec->attributes, filtbuf, sent_pw, &dn, &vals);
+ util_ldap_connection_close(ldc);
+
+ if (result != LDAP_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authenticate: "
+ "user %s authentication failed; URI %s [%s][%s]",
+ getpid(), r->user, r->uri, ldc->reason, ldap_err2string(result));
+ if (LDAP_INVALID_CREDENTIALS == result) {
+ ap_note_basic_auth_failure(r);
+ return HTTP_UNAUTHORIZED;
+ }
+ else {
+ return sec->auth_authoritative? HTTP_UNAUTHORIZED: DECLINED;
+ }
+ }
+
+ /* mark the user and DN */
+ req->dn = apr_pstrdup(r->pool, dn);
+ req->user = r->user;
+ if (sec->user_is_dn) {
+ r->user = req->dn;
+ }
+
+ /* add environment variables */
+ if (sec->attributes && vals) {
+ apr_table_t *e = r->subprocess_env;
+ int i = 0;
+ while (sec->attributes[i]) {
+ char *str = apr_pstrcat(r->pool, "AUTHENTICATE_", sec->attributes[i], NULL);
+ int j = 13;
+ while (str[j]) {
+ if (str[j] >= 'a' && str[j] <= 'z') {
+ str[j] = str[j] - ('a' - 'A');
+ }
+ j++;
+ }
+ apr_table_setn(e, str, vals[i]);
+ i++;
+ }
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authenticate: accepting %s", getpid(), r->user);
+
+ return OK;
+}
+
+
+/*
+ * Authorisation Phase
+ * -------------------
+ *
+ * After checking whether the username and password are correct, we need
+ * to check whether that user is authorised to view this resource. The
+ * require directive is used to do this:
+ *
+ * require valid-user Any authenticated is allowed in.
+ * require user This particular user is allowed in.
+ * require group The user must be a member of this group
+ * in order to be allowed in.
+ * require dn The user must have the following DN in the
+ * LDAP tree to be let in.
+ *
+ */
+int mod_auth_ldap_auth_checker(request_rec *r)
+{
+ int result = 0;
+ mod_auth_ldap_request_t *req =
+ (mod_auth_ldap_request_t *)ap_get_module_config(r->request_config,
+ &auth_ldap_module);
+ mod_auth_ldap_config_t *sec =
+ (mod_auth_ldap_config_t *)ap_get_module_config(r->per_dir_config,
+ &auth_ldap_module);
+
+ util_ldap_connection_t *ldc = NULL;
+ int m = r->method_number;
+
+ const apr_array_header_t *reqs_arr = ap_requires(r);
+ require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL;
+
+ register int x;
+ const char *t;
+ char *w;
+ int method_restricted = 0;
+
+ if (!sec->enabled) {
+ return DECLINED;
+ }
+
+ if (!sec->have_ldap_url) {
+ return DECLINED;
+ }
+
+ if (sec->host) {
+ ldc = util_ldap_connection_find(r, sec->host, sec->port,
+ sec->binddn, sec->bindpw, sec->deref,
+ sec->netscapessl, sec->starttls);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: no sec->host - weird...?", getpid());
+ return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+ }
+
+ /*
+ * If there are no elements in the group attribute array, the default should be
+ * member and uniquemember; populate the array now.
+ */
+ if (sec->groupattr->nelts == 0) {
+ struct mod_auth_ldap_groupattr_entry_t *grp;
+ apr_lock_acquire(sec->lock);
+ grp = apr_array_push(sec->groupattr);
+ grp->name = "member";
+ grp = apr_array_push(sec->groupattr);
+ grp->name = "uniquemember";
+ apr_lock_release(sec->lock);
+ }
+
+ if (!reqs_arr) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: no requirements array", getpid());
+ return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+ }
+
+ /* Loop through the requirements array until there's no elements
+ * left, or something causes a return from inside the loop */
+ for(x=0; x < reqs_arr->nelts; x++) {
+ if (! (reqs[x].method_mask & (1 << m))) {
+ continue;
+ }
+ method_restricted = 1;
+
+ t = reqs[x].requirement;
+ w = ap_getword(r->pool, &t, ' ');
+
+ if (strcmp(w, "valid-user") == 0) {
+ /*
+ * Valid user will always be true if we authenticated with ldap,
+ * but when using front page, valid user should only be true if
+ * he exists in the frontpage password file. This hack will get
+ * auth_ldap to look up the user in the the pw file to really be
+ * sure that he's valid. Naturally, it requires mod_auth to be
+ * compiled in, but if mod_auth wasn't in there, then the need
+ * for this hack wouldn't exist anyway.
+ */
+ if (sec->frontpage_hack) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: "
+ "deferring authorisation to mod_auth (FP Hack)",
+ getpid());
+ return OK;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: "
+ "successful authorisation because user "
+ "is valid-user", getpid());
+ return OK;
+ }
+ }
+ else if (strcmp(w, "user") == 0) {
+ if (req->dn == NULL || strlen(req->dn) == 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: "
+ "require user: user's DN has not been defined; failing authorisation",
+ getpid());
+ return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+ }
+ /*
+ * First do a whole-line compare, in case it's something like
+ * require user Babs Jensen
+ */
+ result = util_ldap_cache_compare(r, ldc, sec->url, req->dn, sec->attribute, t);
+ switch(result) {
+ case LDAP_COMPARE_TRUE: {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: "
+ "require user: authorisation successful", getpid());
+ return OK;
+ }
+ default: {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: require user: "
+ "authorisation failed [%s][%s]", getpid(),
+ ldc->reason, ldap_err2string(result));
+ }
+ }
+ /*
+ * Now break apart the line and compare each word on it
+ */
+ while (t[0]) {
+ w = ap_getword_conf(r->pool, &t);
+ result = util_ldap_cache_compare(r, ldc, sec->url, req->dn, sec->attribute, w);
+ switch(result) {
+ case LDAP_COMPARE_TRUE: {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: "
+ "require user: authorisation successful", getpid());
+ return OK;
+ }
+ default: {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: "
+ "require user: authorisation failed [%s][%s]",
+ getpid(), ldc->reason, ldap_err2string(result));
+ }
+ }
+ }
+ }
+ else if (strcmp(w, "dn") == 0) {
+ if (req->dn == NULL || strlen(req->dn) == 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: "
+ "require dn: user's DN has not been defined; failing authorisation",
+ getpid());
+ return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+ }
+
+ result = util_ldap_cache_comparedn(r, ldc, sec->url, req->dn, t, sec->compare_dn_on_server);
+ switch(result) {
+ case LDAP_COMPARE_TRUE: {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: "
+ "require dn: authorisation successful", getpid());
+ return OK;
+ }
+ default: {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: "
+ "require dn: LDAP error [%s][%s]",
+ getpid(), ldc->reason, ldap_err2string(result));
+ }
+ }
+ }
+ else if (strcmp(w, "group") == 0) {
+ struct mod_auth_ldap_groupattr_entry_t *ent = (struct mod_auth_ldap_groupattr_entry_t *) sec->groupattr->elts;
+ int i;
+
+ if (sec->group_attrib_is_dn) {
+ if (req->dn == NULL || strlen(req->dn) == 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: require group: user's DN has not been defined; failing authorisation",
+ getpid());
+ return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED;
+ }
+ }
+ else {
+ if (req->user == NULL || strlen(req->user) == 0) {
+ /* We weren't called in the authentication phase, so we didn't have a
+ * chance to set the user field. Do so now. */
+ req->user = r->user;
+ }
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: require group: testing for group membership in `%s'",
+ getpid(), t);
+
+ for (i = 0; i < sec->groupattr->nelts; i++) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: require group: testing for %s: %s (%s)", getpid(),
+ ent[i].name, sec->group_attrib_is_dn ? req->dn : req->user, t);
+
+ result = util_ldap_cache_compare(r, ldc, sec->url, t, ent[i].name,
+ sec->group_attrib_is_dn ? req->dn : req->user);
+ switch(result) {
+ case LDAP_COMPARE_TRUE: {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: require group: "
+ "authorisation successful (attribute %s) [%s][%s]",
+ getpid(), ent[i].name, ldc->reason, ldap_err2string(result));
+ return OK;
+ }
+ default: {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: require group: "
+ "authorisation failed [%s][%s]",
+ getpid(), ldc->reason, ldap_err2string(result));
+ }
+ }
+ }
+ }
+ }
+
+ if (!method_restricted) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: agreeing because non-restricted",
+ getpid());
+ return OK;
+ }
+
+ if (!sec->auth_authoritative) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: declining to authorise", getpid());
+ return DECLINED;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
+ "[%d] auth_ldap authorise: authorisation denied", getpid());
+ ap_note_basic_auth_failure (r);
+
+ return HTTP_UNAUTHORIZED;
+}
+
+
+/* ---------------------------------------- */
+/* config directives */
+
+
+void *mod_auth_ldap_create_dir_config(apr_pool_t *p, char *d)
+{
+ mod_auth_ldap_config_t *sec =
+ (mod_auth_ldap_config_t *)apr_pcalloc(p, sizeof(mod_auth_ldap_config_t));
+
+ sec->pool = p;
+ apr_lock_create(&sec->lock, APR_MUTEX, APR_INTRAPROCESS, NULL, p);
+ sec->auth_authoritative = 1;
+ sec->enabled = 1;
+ sec->groupattr = apr_array_make(p, GROUPATTR_MAX_ELTS,
+ sizeof(struct mod_auth_ldap_groupattr_entry_t));
+
+ sec->have_ldap_url = 0;
+ sec->url = "";
+ sec->host = NULL;
+ sec->binddn = NULL;
+ sec->bindpw = NULL;
+ sec->deref = always;
+ sec->group_attrib_is_dn = 1;
+
+ sec->frontpage_hack = 0;
+ sec->netscapessl = 0;
+ sec->starttls = 0;
+
+ sec->user_is_dn = 0;
+ sec->compare_dn_on_server = 0;
+
+ return sec;
+}
+
+/*
+ * Use the ldap url parsing routines to break up the ldap url into
+ * host and port.
+ */
+static const char *mod_auth_ldap_parse_url(cmd_parms *cmd,
+ void *config,
+ const char *url)
+{
+ int result;
+ LDAPURLDesc *urld;
+
+ mod_auth_ldap_config_t *sec = config;
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+ cmd->server, "[%d] auth_ldap url parse: `%s'",
+ getpid(), url);
+
+ result = ldap_url_parse(url, &(urld));
+ if (result != LDAP_SUCCESS) {
+ switch (result) {
+ case LDAP_URL_ERR_NOTLDAP:
+ return "LDAP URL does not begin with ldap://";
+ case LDAP_URL_ERR_NODN:
+ return "LDAP URL does not have a DN";
+ case LDAP_URL_ERR_BADSCOPE:
+ return "LDAP URL has an invalid scope";
+ case LDAP_URL_ERR_MEM:
+ return "Out of memory parsing LDAP URL";
+ default:
+ return "Could not parse LDAP URL";
+ }
+ }
+ sec->url = apr_pstrdup(cmd->pool, url);
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+ cmd->server, "[%d] auth_ldap url parse: Host: %s", getpid(), urld->lud_host);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+ cmd->server, "[%d] auth_ldap url parse: Port: %d", getpid(), urld->lud_port);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+ cmd->server, "[%d] auth_ldap url parse: DN: %s", getpid(), urld->lud_dn);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+ cmd->server, "[%d] auth_ldap url parse: attrib: %s", getpid(), urld->lud_attrs? urld->lud_attrs[0] : "(null)");
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+ cmd->server, "[%d] auth_ldap url parse: scope: %s", getpid(),
+ (urld->lud_scope == LDAP_SCOPE_SUBTREE? "subtree" :
+ urld->lud_scope == LDAP_SCOPE_BASE? "base" :
+ urld->lud_scope == LDAP_SCOPE_ONELEVEL? "onelevel" : "unknown"));
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+ cmd->server, "[%d] auth_ldap url parse: filter: %s", getpid(), urld->lud_filter);
+
+ /* Set all the values, or at least some sane defaults */
+ if (sec->host) {
+ char *p = apr_palloc(cmd->pool, strlen(sec->host) + strlen(urld->lud_host) + 2);
+ strcpy(p, urld->lud_host);
+ strcat(p, " ");
+ strcat(p, sec->host);
+ sec->host = p;
+ }
+ else {
+ sec->host = urld->lud_host? apr_pstrdup(cmd->pool, urld->lud_host) : "localhost";
+ }
+ sec->basedn = urld->lud_dn? apr_pstrdup(cmd->pool, urld->lud_dn) : "";
+ if (urld->lud_attrs && urld->lud_attrs[0]) {
+ int i = 1;
+ while (urld->lud_attrs[i]) {
+ i++;
+ }
+ sec->attributes = apr_pcalloc(cmd->pool, sizeof(char *) * (i+1));
+ i = 0;
+ while (urld->lud_attrs[i]) {
+ sec->attributes[i] = apr_pstrdup(cmd->pool, urld->lud_attrs[i]);
+ i++;
+ }
+ sec->attribute = sec->attributes[0];
+ }
+ else {
+ sec->attribute = "uid";
+ }
+
+ sec->scope = urld->lud_scope == LDAP_SCOPE_ONELEVEL ?
+ LDAP_SCOPE_ONELEVEL : LDAP_SCOPE_SUBTREE;
+
+ if (urld->lud_filter) {
+ if (urld->lud_filter[0] == '(') {
+ /*
+ * Get rid of the surrounding parens; later on when generating the
+ * filter, they'll be put back.
+ */
+ sec->filter = apr_pstrdup(cmd->pool, urld->lud_filter+1);
+ sec->filter[strlen(sec->filter)-1] = '\0';
+ }
+ else {
+ sec->filter = apr_pstrdup(cmd->pool, urld->lud_filter);
+ }
+ }
+ else {
+ sec->filter = "objectclass=*";
+ }
+ if (strncmp(url, "ldaps", 5) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+ cmd->server, "[%d] auth_ldap parse url: requesting secure LDAP", getpid());
+#ifdef APU_HAS_LDAP_STARTTLS
+ sec->port = urld->lud_port? urld->lud_port : LDAPS_PORT;
+ sec->starttls = 1;
+#else
+#ifdef APU_HAS_LDAP_NETSCAPE_SSL
+ sec->port = urld->lud_port? urld->lud_port : LDAPS_PORT;
+ sec->netscapessl = 1;
+#else
+ return "Secure LDAP (ldaps://) not supported. Rebuild APR-Util";
+#endif
+#endif
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0,
+ cmd->server, "[%d] auth_ldap parse url: not requesting secure LDAP", getpid());
+ sec->netscapessl = 0;
+ sec->starttls = 0;
+ sec->port = urld->lud_port? urld->lud_port : LDAP_PORT;
+ }
+
+ sec->have_ldap_url = 1;
+ ldap_free_urldesc(urld);
+ return NULL;
+}
+
+static const char *mod_auth_ldap_set_deref(cmd_parms *cmd, void *config, const char *arg)
+{
+ mod_auth_ldap_config_t *sec = config;
+
+ if (strcmp(arg, "never") == 0 || strcasecmp(arg, "off") == 0) {
+ sec->deref = never;
+ }
+ else if (strcmp(arg, "searching") == 0) {
+ sec->deref = searching;
+ }
+ else if (strcmp(arg, "finding") == 0) {
+ sec->deref = finding;
+ }
+ else if (strcmp(arg, "always") == 0 || strcasecmp(arg, "on") == 0) {
+ sec->deref = always;
+ }
+ else {
+ return "Unrecognized value for AuthLDAPAliasDereference directive";
+ }
+ return NULL;
+}
+
+static const char *mod_auth_ldap_add_group_attribute(cmd_parms *cmd, void *config, const char *arg)
+{
+ struct mod_auth_ldap_groupattr_entry_t *new;
+
+ mod_auth_ldap_config_t *sec = config;
+
+ if (sec->groupattr->nelts > GROUPATTR_MAX_ELTS)
+ return "Too many AuthLDAPGroupAttribute directives";
+
+ new = apr_array_push(sec->groupattr);
+ new->name = apr_pstrdup(cmd->pool, arg);
+
+ return NULL;
+}
+
+command_rec mod_auth_ldap_cmds[] = {
+ AP_INIT_TAKE1("AuthLDAPURL", mod_auth_ldap_parse_url, NULL, OR_AUTHCFG,
+ "URL to define LDAP connection. This should be an RFC 2255 complaint\n"
+ "URL of the form ldap://host[:port]/basedn[?attrib[?scope[?filter]]].\n"
+ "\n"
+ "- Host is the name of the LDAP server. Use a space separated list of hosts \n"
+ "to specify redundant servers.\n"
+ "
- Port is optional, and specifies the port to connect to.\n"
+ "
- basedn specifies the base DN to start searches from\n"
+ "
- Attrib specifies what attribute to search for in the directory. If not "
+ "provided, it defaults to uid.\n"
+ "
- Scope is the scope of the search, and can be either sub or "
+ "one. If not provided, the default is sub.\n"
+ "
- Filter is a filter to use in the search. If not provided, "
+ "defaults to (objectClass=*).\n"
+ "
\n"
+ "Searches are performed using the attribute and the filter combined. "
+ "For example, assume that the\n"
+ "LDAP URL is ldap://ldap.airius.com/ou=People, o=Airius?uid?sub?(posixid=*). "
+ "Searches will\n"
+ "be done using the filter (&((posixid=*))(uid=username)), "
+ "where username\n"
+ "is the user name passed by the HTTP client. The search will be a subtree "
+ "search on the branch ou=People, o=Airius."),
+
+ AP_INIT_TAKE1("AuthLDAPBindDN", ap_set_string_slot,
+ (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, binddn), OR_AUTHCFG,
+ "DN to use to bind to LDAP server. If not provided, will do an anonymous bind."),
+
+ AP_INIT_TAKE1("AuthLDAPBindPassword", ap_set_string_slot,
+ (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, bindpw), OR_AUTHCFG,
+ "Password to use to bind to LDAP server. If not provided, will do an anonymous bind."),
+
+ AP_INIT_FLAG("AuthLDAPRemoteUserIsDN", ap_set_flag_slot,
+ (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, user_is_dn), OR_AUTHCFG,
+ "Set to 'on' to set the REMOTE_USER environment variable to be the full "
+ "DN of the remote user. By default, this is set to off, meaning that "
+ "the REMOTE_USER variable will contain whatever value the remote user sent."),
+
+ AP_INIT_FLAG("AuthLDAPAuthoritative", ap_set_flag_slot,
+ (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, auth_authoritative), OR_AUTHCFG,
+ "Set to 'off' to allow access control to be passed along to lower modules if "
+ "the UserID and/or group is not known to this module"),
+
+ AP_INIT_FLAG("AuthLDAPCompareDNOnServer", ap_set_flag_slot,
+ (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, compare_dn_on_server), OR_AUTHCFG,
+ "Set to 'on' to force auth_ldap to do DN compares (for the \"require dn\" "
+ "directive) using the server, and set it 'off' to do the compares locally "
+ "(at the expense of possible false matches). See the documentation for "
+ "a complete description of this option."),
+
+ AP_INIT_ITERATE("AuthLDAPGroupAttribute", mod_auth_ldap_add_group_attribute, NULL, OR_AUTHCFG,
+ "A list of attributes used to define group membership - defaults to "
+ "member and uniquemember"),
+
+ AP_INIT_FLAG("AuthLDAPGroupAttributeIsDN", ap_set_flag_slot,
+ (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, group_attrib_is_dn), OR_AUTHCFG,
+ "If set to 'on', auth_ldap uses the DN that is retrieved from the server for"
+ "subsequent group comparisons. If set to 'off', auth_ldap uses the string"
+ "provided by the client directly. Defaults to 'on'."),
+
+ AP_INIT_TAKE1("AuthLDAPDereferenceAliases", mod_auth_ldap_set_deref, NULL, OR_AUTHCFG,
+ "Determines how aliases are handled during a search. Can bo one of the"
+ "values \"never\", \"searching\", \"finding\", or \"always\". "
+ "Defaults to always."),
+
+ AP_INIT_FLAG("AuthLDAPEnabled", ap_set_flag_slot,
+ (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, enabled), OR_AUTHCFG,
+ "Set to off to disable auth_ldap, even if it's been enabled in a higher tree"),
+
+ AP_INIT_FLAG("AuthLDAPFrontPageHack", ap_set_flag_slot,
+ (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, frontpage_hack), OR_AUTHCFG,
+ "Set to 'on' to support Microsoft FrontPage"),
+
+#ifdef APU_HAS_LDAP_STARTTLS
+ AP_INIT_FLAG("AuthLDAPStartTLS", ap_set_flag_slot,
+ (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, starttls), OR_AUTHCFG,
+ "Set to 'on' to start TLS after connecting to the LDAP server."),
+#endif /* APU_HAS_LDAP_STARTTLS */
+
+ {NULL}
+};
+
+static void mod_auth_ldap_register_hooks(apr_pool_t *p)
+{
+ ap_hook_check_user_id(mod_auth_ldap_check_user_id, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_auth_checker(mod_auth_ldap_auth_checker, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module auth_ldap_module = {
+ STANDARD20_MODULE_STUFF,
+ mod_auth_ldap_create_dir_config, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ mod_auth_ldap_cmds, /* command table */
+ mod_auth_ldap_register_hooks, /* set up request processing hooks */
+};
diff --git a/modules/experimental/util_ldap.c b/modules/experimental/util_ldap.c
new file mode 100644
index 0000000000..4e9ca0e501
--- /dev/null
+++ b/modules/experimental/util_ldap.c
@@ -0,0 +1,1105 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+/*
+ * util_ldap.c: LDAP things
+ *
+ * Original code from auth_ldap module for Apache v1.3:
+ * Copyright 1998, 1999 Enbridge Pipelines Inc.
+ * Copyright 1999-2001 Dave Carrigan
+ */
+
+#include
+
+#ifdef APU_HAS_LDAP
+
+#include
+#include
+
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "util_ldap.h"
+#include "util_ldap_cache.h"
+
+#if APR_HAVE_UNISTD_H
+#include
+#endif
+
+module AP_MODULE_DECLARE_DATA ldap_module;
+
+int util_ldap_handler(request_rec *r);
+void *util_ldap_create_config(apr_pool_t *p, server_rec *s);
+
+
+/*
+ * Some definitions to help between various versions of apache.
+ */
+
+#ifndef DOCTYPE_HTML_2_0
+#define DOCTYPE_HTML_2_0 "\n"
+#endif
+
+#ifndef DOCTYPE_HTML_3_2
+#define DOCTYPE_HTML_3_2 "\n"
+#endif
+
+#ifndef DOCTYPE_HTML_4_0S
+#define DOCTYPE_HTML_4_0S "\n"
+#endif
+
+#ifndef DOCTYPE_HTML_4_0T
+#define DOCTYPE_HTML_4_0T "\n"
+#endif
+
+#ifndef DOCTYPE_HTML_4_0F
+#define DOCTYPE_HTML_4_0F "\n"
+#endif
+
+/*
+ * Status Handler
+ * --------------
+ *
+ * This handler generates a status page about the current performance of
+ * the LDAP cache. It is enabled as follows:
+ *
+ *
+ * SetHandler ldap-status
+ *
+ *
+ */
+int util_ldap_handler(request_rec *r)
+{
+
+ r->allowed |= (1 << M_GET);
+ if (r->method_number != M_GET)
+ return DECLINED;
+
+ if (strcmp(r->handler, "ldap-status")) {
+ return DECLINED;
+ }
+
+ r->content_type = "text/html";
+ if (r->header_only)
+ return OK;
+
+ ap_rputs(DOCTYPE_HTML_3_2
+ "LDAP Cache Information\n", r);
+ ap_rputs("LDAP Cache Information
\n", r);
+
+ ap_rputs("\n"
+ "
\n"
+ "\n"
+ "Cache Name | "
+ "Entries | "
+ "Avg. Chain Len. | "
+ "Hits | "
+ "Ins/Rem | "
+ "Purges | "
+ "Avg Purge Time | "
+ "
\n", r
+ );
+
+ ap_rputs(util_ald_cache_display(r->pool), r);
+
+ ap_rputs("
\n
\n", r);
+
+ return OK;
+}
+
+/* ------------------------------------------------------------------ */
+
+
+/*
+ * Closes an LDAP connection by unlocking it. The next time
+ * util_ldap_connection_find() is called this connection will be
+ * available for reuse.
+ */
+void util_ldap_connection_close(util_ldap_connection_t *ldc)
+{
+
+ /*
+ * QUESTION:
+ *
+ * Is it safe leaving bound connections floating around between the
+ * different modules? Keeping the user bound is a performance boost,
+ * but it is also a potential security problem - maybe.
+ *
+ * For now we unbind the user when we finish with a connection, but
+ * we don't have to...
+ */
+
+ /* mark our connection as available for reuse */
+ apr_lock_release(ldc->lock);
+
+}
+
+
+/*
+ * Destroys an LDAP connection by unbinding. This function is registered
+ * with the pool cleanup function - causing the LDAP connections to be
+ * shut down cleanly on graceful restart.
+ */
+apr_status_t util_ldap_connection_destroy(void *param)
+{
+ util_ldap_connection_t *ldc = param;
+
+ /* unbinding from the LDAP server */
+ if (ldc->ldap) {
+ ldap_unbind_s(ldc->ldap);
+ ldc->bound = 0;
+ ldc->ldap = NULL;
+ }
+
+ /* release the lock we were using */
+ apr_lock_release(ldc->lock);
+
+ return APR_SUCCESS;
+}
+
+
+/*
+ * Connect to the LDAP server and binds. Does not connect if already
+ * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound.
+ *
+ * Returns LDAP_SUCCESS on success; and an error code on failure
+ */
+int util_ldap_connection_open(util_ldap_connection_t *ldc)
+{
+ int result = 0;
+ int failures = 0;
+
+
+start_over:
+ if (failures++ > 10) {
+ /* too many failures - leave */
+ return result;
+ }
+
+ if (!ldc->ldap) {
+ ldc->bound = 0;
+
+ /* opening connection to LDAP server */
+ if ((ldc->ldap = ldap_init(ldc->host, ldc->port)) == NULL) {
+ /* couldn't connect */
+ ldc->reason = "ldap_init() failed";
+ return -1;
+ }
+
+ /* add the cleanup to the pool */
+ apr_pool_cleanup_register(ldc->pool, ldc,
+ util_ldap_connection_destroy,
+ apr_pool_cleanup_null);
+
+ /* Set the alias dereferencing option */
+#if LDAP_VERSION_MAX == 2
+ ldc->ldap->ld_deref = ldc->deref;
+#else
+ result = ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref));
+ if (result != LDAP_SUCCESS) {
+ /* setting LDAP dereference option failed */
+ /* we ignore this error */
+ }
+#endif /* LDAP_VERSION_MAX */
+
+#ifdef APU_HAS_LDAP_NETSCAPE_SSL
+ if (ldc->netscapessl) {
+ if (!ldc->certdb) {
+ /* secure LDAP requested, but no CA cert defined */
+ ldc->reason = "secure LDAP requested, but no CA cert defined";
+ return -1;
+ } else {
+ result = ldapssl_install_routines(ldc->ldap);
+ if (result != LDAP_SUCCESS) {
+ /* SSL initialisation failed */
+ ldc->reason = "ldapssl_install_routines() failed";
+ return result;
+ }
+ result = ldap_set_option(ldc->ldap, LDAP_OPT_SSL, LDAP_OPT_ON);
+ if (result != LDAP_SUCCESS) {
+ /* SSL option failed */
+ ldc->reason = "ldap_set_option() failed trying to set LDAP_OPT_SSL";
+ return result;
+ }
+ }
+ }
+#endif /* APU_HAS_LDAP_NETSCAPE_SSL */
+
+#ifdef APU_HAS_LDAP_STARTTLS
+ if (ldc->starttls) {
+ int version = LDAP_VERSION3;
+
+ /* Also we have to set the connection to use protocol version 3,
+ * since we're using TLS. */
+ if ((result = ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION,
+ &version)) != LDAP_SUCCESS) {
+ /* setting LDAP version failed - ignore error */
+ }
+
+ /*
+ * In util_ldap_connection_find, we compare ldc->withtls to
+ * sec->starttls to see if we have a cache match. On the off
+ * chance that apache's config processing rotines set starttls to
+ * some other true value besides 1, we set it to 1 here to ensure
+ * that the comparison succeeds.
+ */
+ ldc->starttls = 1;
+
+ result = ldap_start_tls_s(ldc->ldap, NULL, NULL);
+ if (result != LDAP_SUCCESS) {
+ /* start TLS failed */
+ ldc->withtls = 0;
+ ldc->reason = "ldap_start_tls_s() failed";
+ return result;
+ }
+ ldc->withtls = 1;
+ } else {
+ ldc->withtls = 0;
+ }
+#endif /* APU_HAS_LDAP_STARTTLS */
+ }
+
+ /*
+ * At this point the LDAP connection is guaranteed alive. If bound says
+ * that we're bound already, we can just return.
+ */
+ if (ldc->bound) {
+ ldc->reason = "LDAP connection open successful (already bound)";
+ return LDAP_SUCCESS;
+ }
+
+ /*
+ * Now bind with the username/password provided by the
+ * configuration. It will be an anonymous bind if no u/p was
+ * provided.
+ */
+ if ((result = ldap_simple_bind_s(ldc->ldap, ldc->binddn, ldc->bindpw))
+ == LDAP_SERVER_DOWN) {
+ /* couldn't connect - try again */
+ ldc->reason = "ldap_simple_bind_s() failed with server down";
+ goto start_over;
+ }
+
+ if (result != LDAP_SUCCESS) {
+ /* LDAP fatal error occured */
+ ldc->reason = "ldap_simple_bind_s() failed";
+ return result;
+ }
+
+ /* note how we are bound */
+ ldc->bound = 1;
+
+ ldc->reason = "LDAP connection open successful";
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * Find an existing ldap connection struct that matches the
+ * provided ldap connection parameters.
+ *
+ * If not found in the cache, a new ldc structure will be allocated from st->pool
+ * and returned to the caller. If found in the cache, a pointer to the existing
+ * ldc structure will be returned.
+ */
+util_ldap_connection_t *util_ldap_connection_find(request_rec *r, const char *host, int port,
+ const char *binddn, const char *bindpw, deref_options deref,
+ int netscapessl, int starttls)
+{
+ struct util_ldap_connection_t *l, *p; /* To traverse the linked list */
+
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
+ &ldap_module);
+
+
+ /* mutex lock this function */
+ if (!st->mutex) {
+ apr_lock_create(&st->mutex, APR_MUTEX, APR_INTRAPROCESS, NULL, st->pool);
+ }
+ apr_lock_acquire(st->mutex);
+
+ /* Search for an exact connection match in the list that is not
+ * being used.
+ */
+ for (l=st->connections,p=NULL; l; l=l->next) {
+ if ( (APR_SUCCESS == apr_lock_tryacquire(l->lock))
+ && l->port == port
+ && strcmp(l->host, host) == 0
+ && ( (!l->binddn && !binddn) || (l->binddn && binddn && !strcmp(l->binddn, binddn)) )
+ && ( (!l->bindpw && !bindpw) || (l->bindpw && bindpw && !strcmp(l->bindpw, bindpw)) )
+ && l->deref == deref
+#ifdef APU_HAS_LDAP_NETSCAPE_SSL
+ && l->netscapessl == netscapessl
+#endif
+#ifdef APU_HAS_LDAP_STARTTLS
+ && l->withtls == starttls
+#endif
+ )
+ break;
+ p = l;
+ }
+
+ /* If nothing found, search again, but we don't care about the
+ * binddn and bindpw this time.
+ */
+ if (!l) {
+ for (l=st->connections,p=NULL; l; l=l->next) {
+ if ( (APR_SUCCESS == apr_lock_tryacquire(l->lock))
+ && l->port == port
+ && strcmp(l->host, host) == 0
+ && l->deref == deref
+#ifdef APU_HAS_LDAP_NETSCAPE_SSL
+ && l->netscapessl == netscapessl
+#endif
+#ifdef APU_HAS_LDAP_STARTTLS
+ && l->withtls == starttls
+#endif
+ ) {
+ /* the bind credentials have changed */
+ l->bound = 0;
+ l->binddn = apr_pstrdup(st->pool, binddn);
+ l->bindpw = apr_pstrdup(st->pool, bindpw);
+ break;
+ }
+ p = l;
+ }
+ }
+
+/* artificially disable cache */
+//l = NULL;
+
+ /* If no connection what found after the second search, we
+ * must create one.
+ */
+ if (!l) {
+
+ /*
+ * Add the new connection entry to the linked list. Note that we
+ * don't actually establish an LDAP connection yet; that happens
+ * the first time authentication is requested.
+ */
+ /* create the details to the pool in st */
+ l = apr_pcalloc(st->pool, sizeof(util_ldap_connection_t));
+ apr_lock_create(&l->lock, APR_MUTEX, APR_INTRAPROCESS, NULL, st->pool);
+ apr_lock_acquire(l->lock);
+ l->pool = st->pool;
+ l->bound = 0;
+ l->host = apr_pstrdup(st->pool, host);
+ l->port = port;
+ l->deref = deref;
+ l->binddn = apr_pstrdup(st->pool, binddn);
+ l->bindpw = apr_pstrdup(st->pool, bindpw);
+ l->netscapessl = netscapessl;
+ l->starttls = starttls;
+ l->withtls = 0;
+
+ if (p) {
+ p->next = l;
+ }
+ else {
+ st->connections = l;
+ }
+ }
+
+ apr_lock_release(st->mutex);
+ return l;
+}
+
+/* ------------------------------------------------------------------ */
+
+/*
+ * Compares two DNs to see if they're equal. The only way to do this correctly is to
+ * search for the dn and then do ldap_get_dn() on the result. This should match the
+ * initial dn, since it would have been also retrieved with ldap_get_dn(). This is
+ * expensive, so if the configuration value compare_dn_on_server is
+ * false, just does an ordinary strcmp.
+ *
+ * The lock for the ldap cache should already be acquired.
+ */
+int util_ldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc,
+ const char *url, const char *dn, const char *reqdn,
+ int compare_dn_on_server)
+{
+ int result = 0;
+ util_url_node_t *curl;
+ util_url_node_t curnode;
+ util_dn_compare_node_t *node;
+ util_dn_compare_node_t newnode;
+ int failures = 0;
+ LDAPMessage *res, *entry;
+ char *searchdn;
+
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
+ &ldap_module);
+
+
+ /* read lock this function */
+ if (!util_ldap_cache_lock) {
+ apr_lock_create(&util_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, NULL, st->pool);
+ }
+
+ /* get cache entry (or create one) */
+ apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER);
+ curnode.url = url;
+ curl = util_ald_cache_fetch(util_ldap_cache, &curnode);
+ if (curl == NULL) {
+ curl = util_ald_create_caches(st, url);
+ }
+ apr_lock_release(util_ldap_cache_lock);
+
+ /* a simple compare? */
+ if (!compare_dn_on_server) {
+ /* unlock this read lock */
+ if (strcmp(dn, reqdn)) {
+ ldc->reason = "DN Comparison FALSE (direct strcmp())";
+ return LDAP_COMPARE_FALSE;
+ }
+ else {
+ ldc->reason = "DN Comparison TRUE (direct strcmp())";
+ return LDAP_COMPARE_TRUE;
+ }
+ }
+
+ /* no - it's a server side compare */
+ apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER);
+
+ /* is it in the compare cache? */
+ newnode.reqdn = (char *)reqdn;
+ node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode);
+ if (node != NULL) {
+ /* If it's in the cache, it's good */
+ /* unlock this read lock */
+ apr_lock_release(util_ldap_cache_lock);
+ ldc->reason = "DN Comparison TRUE (cached)";
+ return LDAP_COMPARE_TRUE;
+ }
+
+ /* unlock this read lock */
+ apr_lock_release(util_ldap_cache_lock);
+
+start_over:
+ if (failures++ > 10) {
+ /* too many failures */
+ return result;
+ }
+
+ /* make a server connection */
+ if (LDAP_SUCCESS != (result = util_ldap_connection_open(ldc))) {
+ /* connect to server failed */
+ return result;
+ }
+
+ /* search for reqdn */
+ if ((result = ldap_search_ext_s(ldc->ldap, const_cast(reqdn), LDAP_SCOPE_BASE,
+ "(objectclass=*)", NULL, 1,
+ NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) {
+ util_ldap_connection_close(ldc);
+ ldc->reason = "DN Comparison ldap_search_ext_s() failed with server down";
+ goto start_over;
+ }
+ if (result != LDAP_SUCCESS) {
+ /* search for reqdn failed - no match */
+ ldc->reason = "DN Comparison ldap_search_ext_s() failed";
+ return result;
+ }
+
+ entry = ldap_first_entry(ldc->ldap, res);
+ searchdn = ldap_get_dn(ldc->ldap, entry);
+
+ ldap_msgfree(res);
+ if (strcmp(dn, searchdn) != 0) {
+ /* compare unsuccessful */
+ ldc->reason = "DN Comparison FALSE (checked on server)";
+ result = LDAP_COMPARE_FALSE;
+ }
+ else {
+ /* compare successful - add to the compare cache */
+ apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER);
+ newnode.reqdn = (char *)reqdn;
+ newnode.dn = (char *)dn;
+ util_ald_cache_insert(curl->dn_compare_cache, &newnode);
+ apr_lock_release(util_ldap_cache_lock);
+ ldc->reason = "DN Comparison TRUE (checked on server)";
+ result = LDAP_COMPARE_TRUE;
+ }
+ ldap_memfree(searchdn);
+ return result;
+
+}
+
+/*
+ * Does an generic ldap_compare operation. It accepts a cache that it will use
+ * to lookup the compare in the cache. We cache two kinds of compares
+ * (require group compares) and (require user compares). Each compare has a different
+ * cache node: require group includes the DN; require user does not because the
+ * require user cache is owned by the
+ *
+ */
+int util_ldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc,
+ const char *url, const char *dn,
+ const char *attrib, const char *value)
+{
+ int result = 0;
+ util_url_node_t *curl;
+ util_url_node_t curnode;
+ util_compare_node_t *compare_nodep;
+ util_compare_node_t the_compare_node;
+ apr_time_t curtime;
+ int failures = 0;
+
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
+ &ldap_module);
+
+
+ /* read lock this function */
+ if (!util_ldap_cache_lock) {
+ apr_lock_create(&util_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, NULL, st->pool);
+ }
+
+ /* get cache entry (or create one) */
+ apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER);
+ curnode.url = url;
+ curl = util_ald_cache_fetch(util_ldap_cache, &curnode);
+ if (curl == NULL) {
+ curl = util_ald_create_caches(st, url);
+ }
+ apr_lock_release(util_ldap_cache_lock);
+
+ /* make a comparison to the cache */
+ apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER);
+ curtime = apr_time_now();
+
+ the_compare_node.dn = (char *)dn;
+ the_compare_node.attrib = (char *)attrib;
+ the_compare_node.value = (char *)value;
+ the_compare_node.result = 0;
+
+ compare_nodep = util_ald_cache_fetch(curl->compare_cache, &the_compare_node);
+
+ if (compare_nodep != NULL) {
+ /* found it... */
+ if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) {
+ /* ...but it is too old */
+ util_ald_cache_remove(curl->compare_cache, compare_nodep);
+ }
+ else {
+ /* ...and it is good */
+ /* unlock this read lock */
+ apr_lock_release(util_ldap_cache_lock);
+ if (LDAP_COMPARE_TRUE == compare_nodep->result) {
+ ldc->reason = "Comparison true (cached)";
+ return compare_nodep->result;
+ }
+ else if (LDAP_COMPARE_FALSE == compare_nodep->result) {
+ ldc->reason = "Comparison false (cached)";
+ return compare_nodep->result;
+ }
+ else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) {
+ ldc->reason = "Comparison no such attribute (cached)";
+ return compare_nodep->result;
+ }
+ else {
+ ldc->reason = "Comparison undefined (cached)";
+ return compare_nodep->result;
+ }
+ }
+ }
+ /* unlock this read lock */
+ apr_lock_release(util_ldap_cache_lock);
+
+
+start_over:
+ if (failures++ > 10) {
+ /* too many failures */
+ return result;
+ }
+ if (LDAP_SUCCESS != (result = util_ldap_connection_open(ldc))) {
+ /* connect failed */
+ return result;
+ }
+
+ if ((result = ldap_compare_s(ldc->ldap, const_cast(dn),
+ const_cast(attrib), const_cast(value)))
+ == LDAP_SERVER_DOWN) {
+ /* connection failed - try again */
+ util_ldap_connection_close(ldc);
+ ldc->reason = "ldap_compare_s() failed with server down";
+ goto start_over;
+ }
+
+ ldc->reason = "Comparison complete";
+ if ((LDAP_COMPARE_TRUE == result) ||
+ (LDAP_COMPARE_FALSE == result) ||
+ (LDAP_NO_SUCH_ATTRIBUTE == result)) {
+ /* compare completed; caching result */
+ apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER);
+ the_compare_node.lastcompare = curtime;
+ the_compare_node.result = result;
+ util_ald_cache_insert(curl->compare_cache, &the_compare_node);
+ apr_lock_release(util_ldap_cache_lock);
+ if (LDAP_COMPARE_TRUE == result) {
+ ldc->reason = "Comparison true (adding to cache)";
+ return LDAP_COMPARE_TRUE;
+ }
+ else if (LDAP_COMPARE_FALSE == result) {
+ ldc->reason = "Comparison false (adding to cache)";
+ return LDAP_COMPARE_FALSE;
+ }
+ else {
+ ldc->reason = "Comparison no such attribute (adding to cache)";
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ }
+ return result;
+}
+
+int util_ldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc,
+ const char *url, const char *basedn, int scope, char **attrs,
+ const char *filter, const char *bindpw, const char **binddn,
+ const char ***retvals)
+{
+ const char **vals = NULL;
+ int result = 0;
+ LDAPMessage *res, *entry;
+ char *dn;
+ int count;
+ int failures = 0;
+ util_url_node_t *curl; /* Cached URL node */
+ util_url_node_t curnode;
+ util_search_node_t *search_nodep; /* Cached search node */
+ util_search_node_t the_search_node;
+ apr_time_t curtime;
+
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
+ &ldap_module);
+
+ /* read lock this function */
+ if (!util_ldap_cache_lock) {
+ apr_lock_create(&util_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, NULL, st->pool);
+ }
+
+ /* Get the cache node for this url */
+ apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER);
+ curnode.url = url;
+ curl = (util_url_node_t *)util_ald_cache_fetch(util_ldap_cache, &curnode);
+ if (curl == NULL) {
+ curl = util_ald_create_caches(st, url);
+ }
+ apr_lock_release(util_ldap_cache_lock);
+
+ apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER);
+ the_search_node.username = filter;
+ search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
+ if (search_nodep != NULL && search_nodep->bindpw) {
+
+ /* found entry in search cache... */
+ curtime = apr_time_now();
+
+ /*
+ * Remove this item from the cache if its expired, or if the
+ * sent password doesn't match the storepassword.
+ */
+ if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
+ /* ...but entry is too old */
+ util_ald_cache_remove(curl->search_cache, search_nodep);
+ }
+ else if (strcmp(search_nodep->bindpw, bindpw) != 0) {
+ /* ...but cached password doesn't match sent password */
+ util_ald_cache_remove(curl->search_cache, search_nodep);
+ }
+ else {
+ /* ...and entry is valid */
+ *binddn = search_nodep->dn;
+ *retvals = search_nodep->vals;
+ apr_lock_release(util_ldap_cache_lock);
+ ldc->reason = "Authentication successful (cached)";
+ return LDAP_SUCCESS;
+ }
+ }
+ /* unlock this read lock */
+ apr_lock_release(util_ldap_cache_lock);
+
+
+ /*
+ * At this point, there is no valid cached search, so lets do the search.
+ */
+
+ /*
+ * If any LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
+ */
+start_over:
+ if (failures++ > 10) {
+ return result;
+ }
+ if (LDAP_SUCCESS != (result = util_ldap_connection_open(ldc))) {
+ return result;
+ }
+
+ /* try do the search */
+ if ((result = ldap_search_ext_s(ldc->ldap,
+ basedn, scope,
+ filter, attrs, 0,
+ NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) {
+ ldc->reason = "ldap_search_ext_s() for user failed with server down";
+ goto start_over;
+ }
+
+ /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
+ if (result != LDAP_SUCCESS) {
+ ldc->reason = "ldap_search_ext_s() for user failed";
+ return result;
+ }
+
+ /*
+ * We should have found exactly one entry; to find a different
+ * number is an error.
+ */
+ count = ldap_count_entries(ldc->ldap, res);
+ if (count != 1) {
+ ldap_msgfree(res);
+ ldc->reason = "User is not unique (search found two or more matches)";
+ return LDAP_NO_SUCH_OBJECT;
+ }
+
+ entry = ldap_first_entry(ldc->ldap, res);
+
+ /* Grab the dn, copy it into the pool, and free it again */
+ dn = ldap_get_dn(ldc->ldap, entry);
+ *binddn = apr_pstrdup(st->pool, dn);
+ ldap_memfree(dn);
+
+ /*
+ * A bind to the server with an empty password always succeeds, so
+ * we check to ensure that the password is not empty. This implies
+ * that users who actually do have empty passwords will never be
+ * able to authenticate with this module. I don't see this as a big
+ * problem.
+ */
+ if (strlen(bindpw) <= 0) {
+ ldap_msgfree(res);
+ ldc->reason = "Empty password not allowed";
+ return LDAP_INVALID_CREDENTIALS;
+ }
+
+ /*
+ * Attempt to bind with the retrieved dn and the password. If the bind
+ * fails, it means that the password is wrong (the dn obviously
+ * exists, since we just retrieved it)
+ */
+ if ((result =
+ ldap_simple_bind_s(ldc->ldap, *binddn, bindpw)) ==
+ LDAP_SERVER_DOWN) {
+ ldc->reason = "ldap_simple_bind_s() to check user credentials failed with server down";
+ goto start_over;
+ }
+
+ /* failure? if so - return */
+ if (result != LDAP_SUCCESS) {
+ ldc->reason = "ldap_simple_bind_s() to check user credentials failed";
+ return result;
+ }
+
+ /*
+ * Get values for the provided attributes.
+ */
+ if (attrs) {
+ int k = 0;
+ int i = 0;
+ while (attrs[k++]);
+ vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
+ while (attrs[i]) {
+ char **values;
+ int j = 0;
+ char *str = NULL;
+ /* get values */
+ values = ldap_get_values(ldc->ldap, entry, attrs[i]);
+ while (values && values[j]) {
+ str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) : apr_pstrdup(r->pool, values[j]);
+ j++;
+ }
+ vals[i] = str;
+ i++;
+ }
+ *retvals = vals;
+ }
+
+ /*
+ * Add the new username to the search cache.
+ */
+ apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER);
+ the_search_node.username = filter;
+ the_search_node.dn = *binddn;
+ the_search_node.bindpw = bindpw;
+ the_search_node.lastbind = apr_time_now();
+ the_search_node.vals = vals;
+ util_ald_cache_insert(curl->search_cache, &the_search_node);
+ ldap_msgfree(res);
+ apr_lock_release(util_ldap_cache_lock);
+
+ ldc->reason = "Authentication successful";
+ return LDAP_SUCCESS;
+}
+
+#endif /* APU_HAS_LDAP */
+
+
+
+/* ---------------------------------------- */
+/* config directives */
+
+
+static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy, const char *bytes)
+{
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
+ &ldap_module);
+
+ st->cache_bytes = atol(bytes);
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
+ "[%d] ldap cache: Setting shared memory cache size to %d bytes.",
+ getpid(), st->cache_bytes);
+
+ return NULL;
+}
+
+static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
+{
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
+ &ldap_module);
+
+ st->search_cache_ttl = atol(ttl) * 1000000;
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
+ "[%d] ldap cache: Setting cache TTL to %ld microseconds.",
+ getpid(), st->search_cache_ttl);
+
+ return NULL;
+}
+
+static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy, const char *size)
+{
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
+ &ldap_module);
+
+
+ st->search_cache_size = atol(size);
+ if (st->search_cache_size < 0) {
+ st->search_cache_size = 0;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
+ "[%d] ldap cache: Setting search cache size to %ld entries.",
+ getpid(), st->search_cache_size);
+
+ return NULL;
+}
+
+static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy, const char *ttl)
+{
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
+ &ldap_module);
+
+ st->compare_cache_ttl = atol(ttl) * 1000000;
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
+ "[%d] ldap cache: Setting operation cache TTL to %ld microseconds.",
+ getpid(), st->compare_cache_ttl);
+
+ return NULL;
+}
+
+static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy, const char *size)
+{
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
+ &ldap_module);
+
+ st->compare_cache_size = atol(size);
+ if (st->compare_cache_size < 0) {
+ st->compare_cache_size = 0;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
+ "[%d] ldap cache: Setting operation cache size to %ld entries.",
+ getpid(), st->compare_cache_size);
+
+ return NULL;
+}
+
+#ifdef APU_HAS_LDAPSSL_CLIENT_INIT
+static const char *util_ldap_set_certdbpath(cmd_parms *cmd, void *dummy, const char *path)
+{
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config,
+ &ldap_module);
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server,
+ "[%d] ldap cache: Setting LDAP SSL client certificate dbpath to %s.",
+ getpid(), path);
+
+ st->have_certdb = 1;
+ if (ldapssl_client_init(path, NULL) != 0) {
+ return "Could not initialize SSL client";
+ }
+ else {
+ return NULL;
+ }
+}
+#endif
+
+void *util_ldap_create_config(apr_pool_t *p, server_rec *s)
+{
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t));
+
+ st->pool = p;
+
+ st->cache_bytes = 100000;
+ st->search_cache_ttl = 600000000;
+ st->search_cache_size = 1024;
+ st->compare_cache_ttl = 600000000;
+ st->compare_cache_size = 1024;
+
+ st->connections = NULL;
+#ifdef APU_HAS_LDAP_NETSCAPE_SSL
+ st->have_certdb = 0;
+#endif
+
+ return st;
+}
+
+static void util_ldap_init_module(apr_pool_t *pool, server_rec *s)
+{
+ util_ldap_state_t *st =
+ (util_ldap_state_t *)ap_get_module_config(s->module_config,
+ &ldap_module);
+
+ apr_status_t result = util_ldap_cache_init(pool, st->cache_bytes);
+ char buf[MAX_STRING_LEN];
+
+ apr_strerror(result, buf, sizeof(buf));
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, result, s,
+ "[%d] ldap cache init: %s",
+ getpid(), buf);
+}
+
+
+command_rec util_ldap_cmds[] = {
+ AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes, NULL, RSRC_CONF,
+ "Sets the size of the shared memory cache in bytes. "
+ "Zero means disable the shared memory cache. Defaults to 100KB."),
+
+ AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries, NULL, RSRC_CONF,
+ "Sets the maximum number of entries that are possible in the LDAP "
+ "search cache. "
+ "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."),
+
+ AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl, NULL, RSRC_CONF,
+ "Sets the maximum time (in seconds) that an item can be cached in the LDAP "
+ "search cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."),
+
+ AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries, NULL, RSRC_CONF,
+ "Sets the maximum number of entries that are possible in the LDAP "
+ "compare cache. "
+ "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."),
+
+ AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl, NULL, RSRC_CONF,
+ "Sets the maximum time (in seconds) that an item is cached in the LDAP "
+ "operation cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."),
+
+#ifdef APU_HAS_LDAPSSL_CLIENT_INIT
+ AP_INIT_TAKE1("LDAPCertDBPath", util_ldap_set_certdbpath, NULL, RSRC_CONF,
+ "Specifies the file containing Certificate Authority certificates "
+ "for validating secure LDAP server certificates. This file must be the "
+ "cert7.db database used by Netscape Communicator"),
+#endif
+
+ {NULL}
+};
+
+static void util_ldap_register_hooks(apr_pool_t *p)
+{
+ ap_hook_child_init(util_ldap_init_module, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module ldap_module = {
+ STANDARD20_MODULE_STUFF,
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ util_ldap_create_config, /* server config */
+ NULL, /* merge server config */
+ util_ldap_cmds, /* command table */
+ util_ldap_register_hooks, /* set up request processing hooks */
+};
diff --git a/modules/experimental/util_ldap_cache.c b/modules/experimental/util_ldap_cache.c
new file mode 100644
index 0000000000..9d9844ddd6
--- /dev/null
+++ b/modules/experimental/util_ldap_cache.c
@@ -0,0 +1,309 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+/*
+ * util_ldap_cache.c: LDAP cache things
+ *
+ * Original code from auth_ldap module for Apache v1.3:
+ * Copyright 1998, 1999 Enbridge Pipelines Inc.
+ * Copyright 1999-2001 Dave Carrigan
+ */
+
+#include
+#include "util_ldap.h"
+#include "util_ldap_cache.h"
+
+#ifdef APU_HAS_LDAP
+
+
+
+
+/* ------------------------------------------------------------------ */
+
+unsigned long util_ldap_url_node_hash(void *n)
+{
+ util_url_node_t *node = (util_url_node_t *)n;
+ return util_ald_hash_string(1, node->url);
+}
+
+int util_ldap_url_node_compare(void *a, void *b)
+{
+ util_url_node_t *na = (util_url_node_t *)a;
+ util_url_node_t *nb = (util_url_node_t *)b;
+
+ return(strcmp(na->url, nb->url) == 0);
+}
+
+void *util_ldap_url_node_copy(void *c)
+{
+ util_url_node_t *n = (util_url_node_t *)c;
+ util_url_node_t *node = (util_url_node_t *)util_ald_alloc(sizeof(util_url_node_t));
+
+ if (node) {
+ if (!(node->url = util_ald_strdup(n->url))) {
+ util_ald_free(node->url);
+ return NULL;
+ }
+ node->search_cache = n->search_cache;
+ node->compare_cache = n->compare_cache;
+ node->dn_compare_cache = n->dn_compare_cache;
+ return node;
+ }
+ else {
+ return NULL;
+ }
+}
+
+void util_ldap_url_node_free(void *n)
+{
+ util_url_node_t *node = (util_url_node_t *)n;
+
+ util_ald_free(node->url);
+ util_ald_destroy_cache(node->search_cache);
+ util_ald_destroy_cache(node->compare_cache);
+ util_ald_destroy_cache(node->dn_compare_cache);
+ util_ald_free(node);
+}
+
+/* ------------------------------------------------------------------ */
+
+/* Cache functions for search nodes */
+unsigned long util_ldap_search_node_hash(void *n)
+{
+ util_search_node_t *node = (util_search_node_t *)n;
+ return util_ald_hash_string(1, ((util_search_node_t *)(node))->username);
+}
+
+int util_ldap_search_node_compare(void *a, void *b)
+{
+ return(strcmp(((util_search_node_t *)a)->username,
+ ((util_search_node_t *)b)->username) == 0);
+}
+
+void *util_ldap_search_node_copy(void *c)
+{
+ util_search_node_t *node = (util_search_node_t *)c;
+ util_search_node_t *newnode = util_ald_alloc(sizeof(util_search_node_t));
+
+ /* safety check */
+ if (newnode) {
+
+ /* copy vals */
+ if (node->vals) {
+ int k = 0;
+ int i = 0;
+ while (node->vals[k++]);
+ if (!(newnode->vals = util_ald_alloc(sizeof(char *) * (k+1)))) {
+ util_ldap_search_node_free(newnode);
+ return NULL;
+ }
+ while (node->vals[i]) {
+ if (!(newnode->vals[i] = util_ald_strdup(node->vals[i]))) {
+ util_ldap_search_node_free(newnode);
+ return NULL;
+ }
+ i++;
+ }
+ }
+ else {
+ newnode->vals = NULL;
+ }
+ if (!(newnode->username = util_ald_strdup(node->username)) ||
+ !(newnode->dn = util_ald_strdup(node->dn)) ||
+ !(newnode->bindpw = util_ald_strdup(node->bindpw)) ) {
+ util_ldap_search_node_free(newnode);
+ return NULL;
+ }
+ newnode->lastbind = node->lastbind;
+
+ }
+ return (void *)newnode;
+}
+
+void util_ldap_search_node_free(void *n)
+{
+ int i = 0;
+ util_search_node_t *node = (util_search_node_t *)n;
+ if (node->vals) {
+ while (node->vals[i]) {
+ util_ald_free(node->vals[i++]);
+ }
+ util_ald_free(node->vals);
+ }
+ util_ald_free(node->username);
+ util_ald_free(node->dn);
+ util_ald_free(node->bindpw);
+ util_ald_free(node);
+}
+
+/* ------------------------------------------------------------------ */
+
+unsigned long util_ldap_compare_node_hash(void *n)
+{
+ util_compare_node_t *node = (util_compare_node_t *)n;
+ return util_ald_hash_string(3, node->dn, node->attrib, node->value);
+}
+
+int util_ldap_compare_node_compare(void *a, void *b)
+{
+ util_compare_node_t *na = (util_compare_node_t *)a;
+ util_compare_node_t *nb = (util_compare_node_t *)b;
+ return (strcmp(na->dn, nb->dn) == 0 &&
+ strcmp(na->attrib, nb->attrib) == 0 &&
+ strcmp(na->value, nb->value) == 0);
+}
+
+void *util_ldap_compare_node_copy(void *c)
+{
+ util_compare_node_t *n = (util_compare_node_t *)c;
+ util_compare_node_t *node = (util_compare_node_t *)util_ald_alloc(sizeof(util_compare_node_t));
+
+ if (node) {
+ if (!(node->dn = util_ald_strdup(n->dn)) ||
+ !(node->attrib = util_ald_strdup(n->attrib)) ||
+ !(node->value = util_ald_strdup(n->value))) {
+ util_ldap_compare_node_free(node);
+ return NULL;
+ }
+ node->lastcompare = n->lastcompare;
+ node->result = n->result;
+ return node;
+ }
+ else {
+ return NULL;
+ }
+}
+
+void util_ldap_compare_node_free(void *n)
+{
+ util_compare_node_t *node = (util_compare_node_t *)n;
+ util_ald_free(node->dn);
+ util_ald_free(node->attrib);
+ util_ald_free(node->value);
+ util_ald_free(node);
+}
+
+/* ------------------------------------------------------------------ */
+
+unsigned long util_ldap_dn_compare_node_hash(void *n)
+{
+ return util_ald_hash_string(1, ((util_dn_compare_node_t *)n)->reqdn);
+}
+
+int util_ldap_dn_compare_node_compare(void *a, void *b)
+{
+ return (strcmp(((util_dn_compare_node_t *)a)->reqdn,
+ ((util_dn_compare_node_t *)b)->reqdn) == 0);
+}
+
+void *util_ldap_dn_compare_node_copy(void *c)
+{
+ util_dn_compare_node_t *n = (util_dn_compare_node_t *)c;
+ util_dn_compare_node_t *node = (util_dn_compare_node_t *)util_ald_alloc(sizeof(util_dn_compare_node_t));
+ if (node) {
+ if (!(node->reqdn = util_ald_strdup(n->reqdn)) ||
+ !(node->dn = util_ald_strdup(n->dn))) {
+ util_ldap_dn_compare_node_free(node);
+ return NULL;
+ }
+ return node;
+ }
+ else {
+ return NULL;
+ }
+}
+
+void util_ldap_dn_compare_node_free(void *n)
+{
+ util_dn_compare_node_t *node = (util_dn_compare_node_t *)n;
+ util_ald_free(node->reqdn);
+ util_ald_free(node->dn);
+ util_ald_free(node);
+}
+
+
+/* ------------------------------------------------------------------ */
+apr_status_t util_ldap_cache_child_kill(void *data);
+apr_status_t util_ldap_cache_module_kill(void *data);
+
+apr_status_t util_ldap_cache_module_kill(void *data)
+{
+#if APR_HAS_SHARED_MEMORY
+ if (util_ldap_shm != NULL) {
+ apr_status_t result = apr_shm_destroy(util_ldap_shm);
+ util_ldap_shm = NULL;
+ return result;
+ }
+#endif
+ return APR_SUCCESS;
+}
+
+apr_status_t util_ldap_cache_init(apr_pool_t *pool, apr_size_t reqsize)
+{
+ apr_status_t result = APR_SUCCESS;
+ apr_pool_cleanup_register(pool, NULL, util_ldap_cache_module_kill, apr_pool_cleanup_null);
+
+#if APR_HAS_SHARED_MEMORY
+ result = apr_shm_init(&util_ldap_shm, reqsize, "/tmp/ldap_cache", pool);
+#endif
+ util_ldap_cache = util_ald_create_cache(50,
+ util_ldap_url_node_hash,
+ util_ldap_url_node_compare,
+ util_ldap_url_node_copy,
+ util_ldap_url_node_free);
+ return result;
+}
+
+
+#endif /* APU_HAS_LDAP */
diff --git a/modules/experimental/util_ldap_cache.h b/modules/experimental/util_ldap_cache.h
new file mode 100644
index 0000000000..18790eb854
--- /dev/null
+++ b/modules/experimental/util_ldap_cache.h
@@ -0,0 +1,214 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+
+#ifndef APU_LDAP_CACHE_H
+#define APU_LDAP_CACHE_H
+
+/*
+ * This switches LDAP support on or off.
+ */
+
+/* this whole thing disappears if LDAP is not enabled */
+#ifdef APU_HAS_LDAP
+
+
+/*
+ * LDAP Cache Manager
+ */
+
+#include
+
+typedef struct util_cache_node_t {
+ void *payload; /* Pointer to the payload */
+ time_t add_time; /* Time node was added to cache */
+ struct util_cache_node_t *next;
+} util_cache_node_t;
+
+typedef struct util_ald_cache_t {
+ unsigned long size; /* Size of cache array */
+ unsigned long maxentries; /* Maximum number of cache entries */
+ unsigned long numentries; /* Current number of cache entries */
+ unsigned long fullmark; /* Used to keep track of when cache becomes 3/4 full */
+ time_t marktime; /* Time that the cache became 3/4 full */
+ unsigned long (*hash)(void *); /* Func to hash the payload */
+ int (*compare)(void *, void *); /* Func to compare two payloads */
+ void * (*copy)(void *); /* Func to alloc mem and copy payload to new mem */
+ void (*free)(void *); /* Func to free mem used by the payload */
+ util_cache_node_t **nodes;
+
+ unsigned long numpurges; /* No. of times the cache has been purged */
+ double avg_purgetime; /* Average time to purge the cache */
+ time_t last_purge; /* Time of the last purge */
+ unsigned long npurged; /* Number of elements purged in last purge. This is not
+ obvious: it won't be 3/4 the size of the cache if
+ there were a lot of expired entries. */
+
+ unsigned long fetches; /* Number of fetches */
+ unsigned long hits; /* Number of cache hits */
+ unsigned long inserts; /* Number of inserts */
+ unsigned long removes; /* Number of removes */
+} util_ald_cache_t;
+
+#if APR_HAS_SHARED_MEMORY
+apr_shmem_t *util_ldap_shm;
+#endif
+util_ald_cache_t *util_ldap_cache;
+apr_lock_t *util_ldap_cache_lock;
+
+#ifndef WIN32
+#define ALD_MM_FILE_MODE ( S_IRUSR|S_IWUSR )
+#else
+#define ALD_MM_FILE_MODE ( _S_IREAD|_S_IWRITE )
+#endif
+
+
+/*
+ * LDAP Cache
+ */
+
+/*
+ * Maintain a cache of LDAP URLs that the server handles. Each node in
+ * the cache contains the search cache for that URL, and a compare cache
+ * for the URL. The compare cash is populated when doing require group
+ * compares.
+ */
+typedef struct util_url_node_t {
+ const char *url;
+ util_ald_cache_t *search_cache;
+ util_ald_cache_t *compare_cache;
+ util_ald_cache_t *dn_compare_cache;
+} util_url_node_t;
+
+/*
+ * We cache every successful search and bind operation, using the username
+ * as the key. Each node in the cache contains the returned DN, plus the
+ * password used to bind.
+ */
+typedef struct util_search_node_t {
+ const char *username; /* Cache key */
+ const char *dn; /* DN returned from search */
+ const char *bindpw; /* The most recently used bind password;
+ NULL if the bind failed */
+ apr_time_t lastbind; /* Time of last successful bind */
+ const char **vals; /* Values of queried attributes */
+} util_search_node_t;
+
+/*
+ * We cache every successful compare operation, using the DN, attrib, and
+ * value as the key.
+ */
+typedef struct util_compare_node_t {
+ const char *dn; /* DN, attrib and value combine to be the key */
+ const char *attrib;
+ const char *value;
+ apr_time_t lastcompare;
+ int result;
+} util_compare_node_t;
+
+/*
+ * We cache every successful compare dn operation, using the dn in the require
+ * statement and the dn fetched based on the client-provided username.
+ */
+typedef struct util_dn_compare_node_t {
+ const char *reqdn; /* The DN in the require dn statement */
+ const char *dn; /* The DN found in the search */
+} util_dn_compare_node_t;
+
+
+/*
+ * Function prototypes for LDAP cache
+ */
+
+/* util_ldap_cache.c */
+unsigned long util_ldap_url_node_hash(void *n);
+int util_ldap_url_node_compare(void *a, void *b);
+void *util_ldap_url_node_copy(void *c);
+void util_ldap_url_node_free(void *n);
+unsigned long util_ldap_search_node_hash(void *n);
+int util_ldap_search_node_compare(void *a, void *b);
+void *util_ldap_search_node_copy(void *c);
+void util_ldap_search_node_free(void *n);
+unsigned long util_ldap_compare_node_hash(void *n);
+int util_ldap_compare_node_compare(void *a, void *b);
+void *util_ldap_compare_node_copy(void *c);
+void util_ldap_compare_node_free(void *n);
+unsigned long util_ldap_dn_compare_node_hash(void *n);
+int util_ldap_dn_compare_node_compare(void *a, void *b);
+void *util_ldap_dn_compare_node_copy(void *c);
+void util_ldap_dn_compare_node_free(void *n);
+
+
+/* util_ldap_cache_mgr.c */
+
+void util_ald_free(const void *ptr);
+void *util_ald_alloc(unsigned long size);
+const char *util_ald_strdup(const char *s);
+unsigned long util_ald_hash_string(int nstr, ...);
+void util_ald_cache_purge(util_ald_cache_t *cache);
+util_url_node_t *util_ald_create_caches(util_ldap_state_t *s, const char *url);
+util_ald_cache_t *util_ald_create_cache(unsigned long maxentries,
+ unsigned long (*hashfunc)(void *),
+ int (*comparefunc)(void *, void *),
+ void * (*copyfunc)(void *),
+ void (*freefunc)(void *));
+void util_ald_destroy_cache(util_ald_cache_t *cache);
+void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload);
+void util_ald_cache_insert(util_ald_cache_t *cache, void *payload);
+void util_ald_cache_remove(util_ald_cache_t *cache, void *payload);
+char *util_ald_cache_display_stats(apr_pool_t *p, util_ald_cache_t *cache,
+ char *name);
+
+#endif /* APU_HAS_LDAP */
+#endif /* APU_LDAP_CACHE_H */
diff --git a/modules/experimental/util_ldap_cache_mgr.c b/modules/experimental/util_ldap_cache_mgr.c
new file mode 100644
index 0000000000..49eece8958
--- /dev/null
+++ b/modules/experimental/util_ldap_cache_mgr.c
@@ -0,0 +1,537 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+/*
+ * util_ldap_cache_mgr.c: LDAP cache manager things
+ *
+ * Original code from auth_ldap module for Apache v1.3:
+ * Copyright 1998, 1999 Enbridge Pipelines Inc.
+ * Copyright 1999-2001 Dave Carrigan
+ */
+
+#include
+#include "util_ldap.h"
+#include "util_ldap_cache.h"
+#include
+
+#ifdef APU_HAS_LDAP
+
+/* only here until strdup is gone */
+#include
+
+/* here till malloc is gone */
+#include
+
+static const int primes[] =
+{
+ 11,
+ 19,
+ 37,
+ 73,
+ 109,
+ 163,
+ 251,
+ 367,
+ 557,
+ 823,
+ 1237,
+ 1861,
+ 2777,
+ 4177,
+ 6247,
+ 9371,
+ 14057,
+ 21089,
+ 31627,
+ 47431,
+ 71143,
+ 106721,
+ 160073,
+ 240101,
+ 360163,
+ 540217,
+ 810343,
+ 1215497,
+ 1823231,
+ 2734867,
+ 4102283,
+ 6153409,
+ 9230113,
+ 13845163,
+ 0
+};
+
+void util_ald_free(const void *ptr)
+{
+#if APR_HAS_SHARED_MEMORY
+ if (util_ldap_shm) {
+ if (ptr)
+ apr_shm_free(util_ldap_shm, (void *)ptr);
+ } else {
+ if (ptr)
+ free((void *)ptr);
+ }
+#else
+ if (ptr)
+ free((void *)ptr);
+#endif
+}
+
+void *util_ald_alloc(unsigned long size)
+{
+ if (0 == size)
+ return NULL;
+#if APR_HAS_SHARED_MEMORY
+ if (util_ldap_shm) {
+ return (void *)apr_shm_calloc(util_ldap_shm, size);
+ } else {
+ return (void *)calloc(sizeof(char), size);
+ }
+#else
+ return (void *)calloc(sizeof(char), size);
+#endif
+}
+
+const char *util_ald_strdup(const char *s)
+{
+#if APR_HAS_SHARED_MEMORY
+ if (util_ldap_shm) {
+ char *buf = apr_shm_malloc(util_ldap_shm, strlen(s)+1);
+ if (buf) {
+ strcpy(buf, s);
+ return buf;
+ }
+ else {
+ return NULL;
+ }
+ } else {
+ return strdup(s);
+ }
+#else
+ return strdup(s);
+#endif
+}
+
+
+/*
+ * Computes the hash on a set of strings. The first argument is the number
+ * of strings to hash, the rest of the args are strings.
+ * Algorithm taken from glibc.
+ */
+unsigned long util_ald_hash_string(int nstr, ...)
+{
+ int i;
+ va_list args;
+ unsigned long h=0, g;
+ char *str, *p;
+
+ va_start(args, nstr);
+ for (i=0; i < nstr; ++i) {
+ str = va_arg(args, char *);
+ for (p = str; *p; ++p) {
+ h = ( h << 4 ) + *p;
+ if ( ( g = h & 0xf0000000 ) ) {
+ h = h ^ (g >> 24);
+ h = h ^ g;
+ }
+ }
+ }
+ va_end(args);
+
+ return h;
+}
+
+
+/*
+ Purges a cache that has gotten full. We keep track of the time that we
+ added the entry that made the cache 3/4 full, then delete all entries
+ that were added before that time. It's pretty simplistic, but time to
+ purge is only O(n), which is more important.
+*/
+void util_ald_cache_purge(util_ald_cache_t *cache)
+{
+ int i;
+ util_cache_node_t *p, *q;
+ apr_time_t t;
+
+ if (!cache)
+ return;
+
+ cache->last_purge = apr_time_now();
+ cache->npurged = 0;
+ cache->numpurges++;
+
+ for (i=0; i < cache->size; ++i) {
+ p = cache->nodes[i];
+ while (p != NULL) {
+ if (p->add_time < cache->marktime) {
+ q = p->next;
+ (*cache->free)(p->payload);
+ util_ald_free(p);
+ cache->numentries--;
+ cache->npurged++;
+ p = q;
+ }
+ else {
+ p = p->next;
+ }
+ }
+ }
+
+ t = apr_time_now();
+ cache->avg_purgetime =
+ ((t - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) /
+ cache->numpurges;
+}
+
+
+/*
+ * create caches
+ */
+util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url)
+{
+ util_url_node_t *curl = NULL;
+ util_ald_cache_t *search_cache;
+ util_ald_cache_t *compare_cache;
+ util_ald_cache_t *dn_compare_cache;
+
+ /* create the three caches */
+ search_cache = util_ald_create_cache(st->search_cache_size,
+ util_ldap_search_node_hash,
+ util_ldap_search_node_compare,
+ util_ldap_search_node_copy,
+ util_ldap_search_node_free);
+ compare_cache = util_ald_create_cache(st->compare_cache_size,
+ util_ldap_compare_node_hash,
+ util_ldap_compare_node_compare,
+ util_ldap_compare_node_copy,
+ util_ldap_compare_node_free);
+ dn_compare_cache = util_ald_create_cache(st->compare_cache_size,
+ util_ldap_dn_compare_node_hash,
+ util_ldap_dn_compare_node_compare,
+ util_ldap_dn_compare_node_copy,
+ util_ldap_dn_compare_node_free);
+
+ /* check that all the caches initialised successfully */
+ if (search_cache && compare_cache && dn_compare_cache) {
+
+ curl = (util_url_node_t *)apr_pcalloc(st->pool, sizeof(util_url_node_t));
+ curl->url = url;
+ curl->search_cache = search_cache;
+ curl->compare_cache = compare_cache;
+ curl->dn_compare_cache = dn_compare_cache;
+
+ util_ald_cache_insert(util_ldap_cache, curl);
+
+ }
+
+ return curl;
+}
+
+
+util_ald_cache_t *util_ald_create_cache(unsigned long maxentries,
+ unsigned long (*hashfunc)(void *),
+ int (*comparefunc)(void *, void *),
+ void * (*copyfunc)(void *),
+ void (*freefunc)(void *))
+{
+ util_ald_cache_t *cache;
+ int i;
+
+ if (maxentries <= 0)
+ return NULL;
+
+ cache = (util_ald_cache_t *)util_ald_alloc(sizeof(util_ald_cache_t));
+ if (!cache)
+ return NULL;
+
+ cache->maxentries = maxentries;
+ cache->numentries = 0;
+ cache->size = maxentries / 3;
+ if (cache->size < 64) cache->size = 64;
+ for (i = 0; primes[i] && primes[i] < cache->size; ++i) ;
+ cache->size = primes[i]? primes[i] : primes[i-1];
+
+ cache->nodes = (util_cache_node_t **)util_ald_alloc(cache->size * sizeof(util_cache_node_t *));
+ if (!cache->nodes) {
+ util_ald_free(cache);
+ return NULL;
+ }
+
+ for (i=0; i < cache->size; ++i)
+ cache->nodes[i] = NULL;
+
+ cache->hash = hashfunc;
+ cache->compare = comparefunc;
+ cache->copy = copyfunc;
+ cache->free = freefunc;
+
+ cache->fullmark = cache->maxentries / 4 * 3;
+ cache->marktime = 0;
+ cache->avg_purgetime = 0.0;
+ cache->numpurges = 0;
+ cache->last_purge = 0;
+ cache->npurged = 0;
+
+ cache->fetches = 0;
+ cache->hits = 0;
+ cache->inserts = 0;
+ cache->removes = 0;
+
+ return cache;
+}
+
+void util_ald_destroy_cache(util_ald_cache_t *cache)
+{
+ int i;
+ util_cache_node_t *p, *q;
+
+ if (cache == NULL)
+ return;
+
+ for (i = 0; i < cache->size; ++i) {
+ p = cache->nodes[i];
+ q = NULL;
+ while (p != NULL) {
+ q = p->next;
+ (*cache->free)(p->payload);
+ util_ald_free(p);
+ p = q;
+ }
+ }
+ util_ald_free(cache->nodes);
+}
+
+void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload)
+{
+ int hashval;
+ util_cache_node_t *p;
+
+ if (cache == NULL)
+ return NULL;
+
+ cache->fetches++;
+
+ hashval = (*cache->hash)(payload) % cache->size;
+ for (p = cache->nodes[hashval];
+ p && !(*cache->compare)(p->payload, payload);
+ p = p->next) ;
+
+ if (p != NULL) {
+ cache->hits++;
+ return p->payload;
+ }
+ else {
+ return NULL;
+ }
+}
+
+/*
+ * Insert an item into the cache.
+ * *** Does not catch duplicates!!! ***
+ */
+void util_ald_cache_insert(util_ald_cache_t *cache, void *payload)
+{
+ int hashval;
+ util_cache_node_t *node;
+
+ if (cache == NULL || payload == NULL)
+ return;
+
+ cache->inserts++;
+ hashval = (*cache->hash)(payload) % cache->size;
+ node = (util_cache_node_t *)util_ald_alloc(sizeof(util_cache_node_t));
+ node->add_time = apr_time_now();
+ node->payload = (*cache->copy)(payload);
+ node->next = cache->nodes[hashval];
+ cache->nodes[hashval] = node;
+ if (++cache->numentries == cache->fullmark)
+ cache->marktime=apr_time_now();
+ if (cache->numentries >= cache->maxentries)
+ util_ald_cache_purge(cache);
+}
+
+void util_ald_cache_remove(util_ald_cache_t *cache, void *payload)
+{
+ int hashval;
+ util_cache_node_t *p, *q;
+
+ if (cache == NULL)
+ return;
+
+ cache->removes++;
+ hashval = (*cache->hash)(payload) % cache->size;
+ for (p = cache->nodes[hashval], q=NULL;
+ p && !(*cache->compare)(p->payload, payload);
+ p = p->next) {
+ q = p;
+ }
+
+ /* If p is null, it means that we couldn't find the node, so just return */
+ if (p == NULL)
+ return;
+
+ if (q == NULL) {
+ /* We found the node, and it's the first in the list */
+ cache->nodes[hashval] = p->next;
+ }
+ else {
+ /* We found the node and it's not the first in the list */
+ q->next = p->next;
+ }
+ (*cache->free)(p->payload);
+ util_ald_free(p);
+ cache->numentries--;
+}
+
+char *util_ald_cache_display_stats(apr_pool_t *p, util_ald_cache_t *cache, char *name)
+{
+ int i;
+ int totchainlen = 0;
+ int nchains = 0;
+ double chainlen;
+ util_cache_node_t *n;
+ char *buf;
+
+ if (cache == NULL) {
+ return "";
+ }
+
+ for (i=0; i < cache->size; ++i) {
+ if (cache->nodes[i] != NULL) {
+ nchains++;
+ for (n = cache->nodes[i]; n != NULL; n = n->next)
+ totchainlen++;
+ }
+ }
+ chainlen = nchains? (double)totchainlen / (double)nchains : 0;
+
+ buf = apr_psprintf(p,
+ ""
+ "%s | "
+ "%lu (%.0f%% full) | "
+ "%.1f | "
+ "%lu/%lu | "
+ "%.0f%% | "
+ "%lu/%lu | ",
+ name,
+ cache->numentries,
+ (double)cache->numentries / (double)cache->maxentries * 100.0,
+ chainlen,
+ cache->hits,
+ cache->fetches,
+ (cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0),
+ cache->inserts,
+ cache->removes);
+
+ if (cache->numpurges) {
+ char str_ctime[APR_CTIME_LEN];
+
+ apr_ctime(str_ctime, cache->last_purge);
+ buf = apr_psprintf(p,
+ "%s"
+ "%lu | \n"
+ "%s | \n",
+ buf,
+ cache->numpurges,
+ str_ctime);
+ }
+ else {
+ buf = apr_psprintf(p,
+ "%s(none) | \n",
+ buf);
+ }
+
+ buf = apr_psprintf(p, "%s%.2g | \n
", buf, cache->avg_purgetime);
+
+ return buf;
+}
+
+char *util_ald_cache_display(apr_pool_t *pool)
+{
+ int i;
+ char *buf, *t1, *t2, *t3;
+
+ if (!util_ldap_cache) {
+ return "Cache has not been enabled/initialised. |
";
+ }
+
+ buf = util_ald_cache_display_stats(pool, util_ldap_cache, "LDAP URL Cache");
+
+ for (i=0; i < util_ldap_cache->size; ++i) {
+ util_cache_node_t *p;
+ for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) {
+ util_url_node_t *n;
+
+ n = (util_url_node_t *)p->payload;
+
+ t1 = apr_psprintf(pool, "%s (Searches)", n->url);
+ t2 = apr_psprintf(pool, "%s (Compares)", n->url);
+ t3 = apr_psprintf(pool, "%s (DNCompares)", n->url);
+
+ buf = apr_psprintf(pool, "%s\n\n"
+ "%s\n\n"
+ "%s\n\n"
+ "%s\n\n",
+ buf,
+ util_ald_cache_display_stats(pool, n->search_cache, t1),
+ util_ald_cache_display_stats(pool, n->compare_cache, t2),
+ util_ald_cache_display_stats(pool, n->dn_compare_cache, t3)
+ );
+ }
+ }
+ return buf;
+}
+
+#endif /* APU_HAS_LDAP */
--
2.40.0