/* ============================================================ * Copyright (c) 2003-2004, Andreas Brenk * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the * following conditions are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * 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. * * Neither the names of the copyright holders nor the * names of its contributors may be used to endorse or * promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS 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 COPYRIGHT OWNER OR * 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. * ============================================================ */ /* * mod_cfg_ldap.c --- read vhost config from an LDAP directory */ #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_request.h" #include "apr_strings.h" #include "ldap.h" #define CFG_LDAP_NO_SUCH_VHOST "cfg_ldap_no_such_vhost" #define HOST_NOT_FOUND 0 #define HOST_FOUND 1 #define MULTIPLE_HOST_FOUND 2 module AP_MODULE_DECLARE_DATA cfg_ldap_module; typedef struct cfg_ldap_cfg { int enabled; apr_time_t cachettl; const char *hostname; int port; int usetls; const char *cert_auth_file; const char *username; const char *password; const char *basedn; const char *filter; } cfg_ldap_cfg; typedef struct cfg_ldap_vhost { const char *name; const char *admin; const char *docroot; apr_time_t timestamp; } cfg_ldap_vhost; typedef struct cfg_ldap_novhost { apr_time_t timestamp; } cfg_ldap_novhost; static apr_pool_t *pool = NULL; static apr_hash_t *cache = NULL; static LDAP *ld = NULL; static const char *attrs[] = { "apacheServerName", "apacheServerAdmin", "apacheDocumentRoot", 0 }; static void cfg_ldap_init_ldap (apr_pool_t * p, server_rec * s) { int failures = 0; cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (s->module_config, &cfg_ldap_module); int rc; do { if (cfg->usetls) { ld = ldap_init (cfg->hostname, LDAPS_PORT); int version = LDAP_VERSION3; if ((rc = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version)) != LDAP_OPT_SUCCESS) { ap_log_error (APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, NULL, "mod_cfg_ldap: Setting LDAP version option failed: %s", ldap_err2string (rc)); ldap_unbind (ld); ld = NULL; return; } int SSLmode = LDAP_OPT_X_TLS_HARD; if ((rc = ldap_set_option (ld, LDAP_OPT_X_TLS, &SSLmode)) != LDAP_SUCCESS) { ldap_unbind_s (ld); ap_log_error (APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, NULL, "mod_cfg_ldap: ldap_set_option - LDAP_OPT_X_TLS_HARD failed"); ld = NULL; return; } if ((rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, cfg->cert_auth_file)) != LDAP_SUCCESS) { ldap_unbind_s (ld); ap_log_error (APLOG_MARK, APLOG_CRIT, 0, s, "mod_cfg_ldap: Invalid LDAPTrustedCA directive (%s): %s", cfg->cert_auth_file, ldap_err2string (rc)); ld = NULL; return; } } else { ld = ldap_init (cfg->hostname, LDAP_PORT); } rc = ldap_simple_bind_s (ld, cfg->username, cfg->password); // TODO extract "5" to MAX_FAILURES if (rc == LDAP_SERVER_DOWN && failures++ <= 5) { ap_log_error (APLOG_MARK, APLOG_WARNING, 0, s, "cfg_ldap: unknown ldap error %d", rc); break; } } while (!rc == LDAP_SUCCESS); } static void * cfg_ldap_create_server_config (apr_pool_t * p, server_rec * s) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) apr_pcalloc (p, sizeof (cfg_ldap_cfg)); cfg->enabled = 0; return (void *) cfg; } static apr_status_t cfg_ldap_child_exit (void *data) { ldap_unbind (ld); return APR_SUCCESS; } static void cfg_ldap_child_init (apr_pool_t * p, server_rec * s) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (s->module_config, &cfg_ldap_module); if (!cfg->enabled) { return; } if (pool == NULL) { apr_pool_create (&pool, NULL); } if (cache == NULL) { cache = apr_hash_make (p); } if (ld == NULL) { cfg_ldap_init_ldap (p, s); } apr_pool_cleanup_register (p, NULL, cfg_ldap_child_exit, cfg_ldap_child_exit); } static cfg_ldap_vhost * cfg_ldap_read_vhost_from_ldap (apr_pool_t * p, server_rec * s, char *hostname) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (s->module_config, &cfg_ldap_module); cfg_ldap_vhost *vhost; vhost = (cfg_ldap_vhost *) apr_pcalloc (p, sizeof (cfg_ldap_vhost)); vhost->timestamp = apr_time_now (); vhost->name = CFG_LDAP_NO_SUCH_VHOST; char *filter; filter = apr_pstrcat (p, "(&(|(apacheServerName=", hostname, ")(apacheServerAlias=", hostname, "))", cfg->filter, ")", NULL); int rc; LDAPMessage *res; rc = ldap_search_s (ld, cfg->basedn, LDAP_SCOPE_SUBTREE, filter, (char **) &attrs, 0, &res); if (!rc == LDAP_SUCCESS) { if (rc == LDAP_SERVER_DOWN) { cfg_ldap_init_ldap (p, s); return cfg_ldap_read_vhost_from_ldap (p, s, hostname); } ap_log_error (APLOG_MARK, APLOG_WARNING, 0, s, "cfg_ldap: unknown ldap error %d", rc); return vhost; } LDAPMessage *entry; entry = ldap_first_entry (ld, res); // count vhosts entries int count = ldap_count_entries (ld, res); if (count == 0) switch (count) { case HOST_NOT_FOUND: return vhost; break; case HOST_FOUND: break; case MULTIPLE_HOST_FOUND: default: ap_log_error (APLOG_MARK, APLOG_WARNING, 0, s, "cfg_ldap: more than one entry for %s", hostname); return vhost; break; } char *attr; BerElement *ber; char **val = NULL; char *vhost_name = NULL; char *vhost_admin = NULL; char *vhost_docroot = NULL; for (attr = ldap_first_attribute (ld, entry, &ber); attr != NULL; attr = ldap_next_attribute (ld, entry, ber)) { val = ldap_get_values (ld, entry, attr); if (strcasecmp (attr, "apacheServerName") == 0) { vhost_name = apr_pstrdup (p, val[0]); } if (strcasecmp (attr, "apacheServerAdmin") == 0) { vhost_admin = apr_pstrdup (p, val[0]); } if (strcasecmp (attr, "apacheDocumentRoot") == 0) { vhost_docroot = apr_pstrdup (p, val[0]); } } ldap_value_free (val); ldap_memfree (attr); if (ber != NULL) { ber_free (ber, 0); } vhost->name = apr_pstrdup (p, vhost_name); if (vhost_admin != NULL) { vhost->admin = apr_pstrdup (p, vhost_admin); } else { vhost->admin = apr_pstrdup (p, s->server_admin); } vhost->docroot = apr_pstrdup (p, vhost_docroot); return vhost; } static int cfg_ldap_translate_name (request_rec * r) { apr_table_t *e; cfg_ldap_cfg *cfg; cfg_ldap_vhost *vhost; cfg = (cfg_ldap_cfg *) ap_get_module_config (r->server->module_config, &cfg_ldap_module); // mod_cfg_ldap is disabled if (!cfg->enabled) { return DECLINED; } vhost = apr_hash_get (cache, r->hostname, APR_HASH_KEY_STRING); if (vhost == NULL) { vhost = cfg_ldap_read_vhost_from_ldap (pool, r->server, apr_pstrdup (r->pool, r->hostname)); apr_hash_set (cache, r->hostname, APR_HASH_KEY_STRING, vhost); } else { if ((vhost->timestamp + cfg->cachettl) < apr_time_now ()) { apr_hash_set (cache, r->hostname, APR_HASH_KEY_STRING, NULL); return cfg_ldap_translate_name (r); } } // host not found if (strcasecmp (vhost->name, CFG_LDAP_NO_SUCH_VHOST) == 0) { return DECLINED; } r->filename = apr_pstrcat (r->pool, vhost->docroot, r->parsed_uri.path, NULL); r->server->server_hostname = apr_pstrdup (r->pool, vhost->name); r->server->server_admin = apr_pstrdup (r->pool, vhost->admin); // set environment variables e = r->subprocess_env; apr_table_addn (e, "SERVER_ROOT", vhost->docroot); return OK; } static const char * set_cfg_ldap_enabled (cmd_parms * cmd, void *mconfig, const int enabled) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (cmd->server->module_config, &cfg_ldap_module); cfg->enabled = enabled; return NULL; } static const char * set_cfg_ldap_hostname (cmd_parms * cmd, void *mconfig, const char *hostname) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (cmd->server->module_config, &cfg_ldap_module); cfg->hostname = hostname; return NULL; } static const char * set_cfg_ldap_usetls (cmd_parms * cmd, void *mconfig, const int usetls) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (cmd->server->module_config, &cfg_ldap_module); cfg->usetls = usetls; return NULL; } static const char * set_cfg_ldap_cert_auth (cmd_parms * cmd, void *dummy, const char *file) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (cmd->server->module_config, &cfg_ldap_module); const char *err = ap_check_cmd_context (cmd, GLOBAL_ONLY); if (err != NULL) { return err; } ap_log_error (APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, cmd->server, "LDAP: SSL trusted certificate authority file - %s", file); cfg->cert_auth_file = ap_server_root_relative (cmd->pool, file); return NULL; } static const char * set_cfg_ldap_username (cmd_parms * cmd, void *mconfig, const char *username) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (cmd->server->module_config, &cfg_ldap_module); cfg->username = username; return NULL; } static const char * set_cfg_ldap_password (cmd_parms * cmd, void *mconfig, const char *password) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (cmd->server->module_config, &cfg_ldap_module); cfg->password = password; return NULL; } static const char * set_cfg_ldap_basedn (cmd_parms * cmd, void *mconfig, const char *basedn) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (cmd->server->module_config, &cfg_ldap_module); cfg->basedn = basedn; return NULL; } static const char * set_cfg_ldap_filter (cmd_parms * cmd, void *mconfig, const char *filter) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (cmd->server->module_config, &cfg_ldap_module); cfg->filter = filter; return NULL; } static const char * set_cfg_ldap_cachettl (cmd_parms * cmd, void *mconfig, const char *cachettl) { cfg_ldap_cfg *cfg; cfg = (cfg_ldap_cfg *) ap_get_module_config (cmd->server->module_config, &cfg_ldap_module); cfg->cachettl = 1000 * 1000 * atol (cachettl); return NULL; } static const command_rec cfg_ldap_cmds[] = { AP_INIT_FLAG ("EnableCfgLdap", set_cfg_ldap_enabled, NULL, RSRC_CONF, "enable mod_cfg_ldap"), AP_INIT_TAKE1 ("CfgLdapServer", set_cfg_ldap_hostname, NULL, RSRC_CONF, "the LDAP server to connect to"), AP_INIT_FLAG ("CfgLdapUseTLS", set_cfg_ldap_usetls, NULL, RSRC_CONF, "whether to use an encrypted connection to the LDAP server"), AP_INIT_TAKE1 ("CfgLdapTrustedCA", set_cfg_ldap_cert_auth, NULL, RSRC_CONF, "Sets the file containing the trusted Certificate Authority certificate. " "Used to validate the LDAP server certificate for SSL connections."), AP_INIT_TAKE1 ("CfgLdapBindDN", set_cfg_ldap_username, NULL, RSRC_CONF, "the DN to bind with"), AP_INIT_TAKE1 ("CfgLdapCredentials", set_cfg_ldap_password, NULL, RSRC_CONF, "the credentials to bind with"), AP_INIT_TAKE1 ("CfgLdapBaseDN", set_cfg_ldap_basedn, NULL, RSRC_CONF, "the base DN to use in searches"), AP_INIT_TAKE1 ("CfgLdapFilter", set_cfg_ldap_filter, NULL, RSRC_CONF, "an additional filter to use in searches"), AP_INIT_TAKE1 ("CfgLdapCacheTTL", set_cfg_ldap_cachettl, NULL, RSRC_CONF, "number of seconds a config is cached"), {NULL} }; static void cfg_ldap_register_hooks (apr_pool_t * p) { ap_hook_child_init (cfg_ldap_child_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_translate_name (cfg_ldap_translate_name, NULL, NULL, APR_HOOK_MIDDLE); } module AP_MODULE_DECLARE_DATA cfg_ldap_module = { STANDARD20_MODULE_STUFF, NULL, NULL, cfg_ldap_create_server_config, NULL, cfg_ldap_cmds, cfg_ldap_register_hooks, };