modmellon/mod_auth_mellon.c

251 lines
8.5 KiB
C

/*
*
* mod_auth_mellon.c: an authentication apache module
* Copyright © 2003-2007 UNINETT (http://www.uninett.no/)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "auth_mellon.h"
#include <curl/curl.h>
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(auth_mellon);
#endif
/* This function is called after the configuration of the server is parsed
* (it's a post-config hook).
*
* It initializes the shared memory and the mutex which is used to protect
* the shared memory.
*
* Parameters:
* apr_pool_t *conf The configuration pool. Valid as long as this
* configuration is valid.
* apr_pool_t *log A pool for memory which is cleared after each read
* through the config files.
* apr_pool_t *tmp A pool for memory which will be destroyed after
* all the post_config hooks are run.
* server_rec *s The current server record.
*
* Returns:
* OK on successful initialization, or !OK on failure.
*/
static int am_global_init(apr_pool_t *conf, apr_pool_t *log,
apr_pool_t *tmp, server_rec *s)
{
apr_size_t mem_size;
am_mod_cfg_rec *mod;
int rv;
const char userdata_key[] = "auth_mellon_init";
char buffer[512];
void *data;
/* Apache tests loadable modules by loading them (as is the only way).
* This has the effect that all modules are loaded and initialised twice,
* and we just want to initialise shared memory and mutexes when the
* module loads for real!
*
* To accomplish this, we store a piece of data as userdata in the
* process pool the first time the function is run. This data can be
* detected on all subsequent runs, and then we know that this isn't the
* first time this function runs.
*/
apr_pool_userdata_get(&data, userdata_key, s->process->pool);
if (!data) {
/* This is the first time this function is run. */
apr_pool_userdata_set((const void *)1, userdata_key,
apr_pool_cleanup_null, s->process->pool);
return OK;
}
mod = am_get_mod_cfg(s);
/* If the session store is initialized then we can't change it. */
if(mod->cache != NULL) {
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
"auth_mellon session store already initialized -"
" reinitialization skipped.");
return OK;
}
/* Copy from the variables set by the configuration file into variables
* which will be set only once. We do this to avoid confusion if the user
* tries to change the parameters of the session store after it is
* initialized.
*/
mod->init_cache_size = mod->cache_size;
mod->init_lock_file = apr_pstrdup(conf, mod->lock_file);
mod->init_entry_size = mod->entry_size;
if (mod->init_entry_size < AM_CACHE_MIN_ENTRY_SIZE) {
mod->init_entry_size = AM_CACHE_MIN_ENTRY_SIZE;
}
/* find out the memory size of the cache */
mem_size = mod->init_entry_size * mod->init_cache_size;
/* Create the shared memory, exit if it fails. */
rv = apr_shm_create(&(mod->cache), mem_size, NULL, conf);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"shm_create: Error [%d] \"%s\"", rv,
apr_strerror(rv, buffer, sizeof(buffer)));
return !OK;
}
/* Initialize the session table. */
am_cache_init(mod);
/* Now create the mutex that we need for locking the shared memory, then
* test for success. we really need this, so we exit on failure. */
rv = apr_global_mutex_create(&(mod->lock),
mod->init_lock_file,
APR_LOCK_DEFAULT,
conf);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"mutex_create: Error [%d] \"%s\"", rv,
apr_strerror(rv, buffer, sizeof(buffer)));
return !OK;
}
#ifdef AP_NEED_SET_MUTEX_PERMS
/* On some platforms the mutex is implemented as a file. To allow child
* processes running as a different user to open it, it is necessary to
* change the permissions on it. */
rv = ap_unixd_set_global_mutex_perms(mod->lock);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"Failed to set permissions on session table lock;"
" check User and Group directives");
return rv;
}
#endif
return OK;
}
/* This function is run when each child process of apache starts.
* apr_global_mutex_child_init must be run on the session data mutex for
* every child process of apache.
*
* Parameters:
* apr_pool_t *p This pool is for data associated with this
* child process.
* server_rec *s The server record for the current server.
*
* Returns:
* Nothing.
*/
static void am_child_init(apr_pool_t *p, server_rec *s)
{
am_mod_cfg_rec *m = am_get_mod_cfg(s);
apr_status_t rv;
CURLcode curl_res;
/* Reinitialize the mutex for the child process. */
rv = apr_global_mutex_child_init(&(m->lock), m->init_lock_file, p);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"Child process could not connect to mutex");
}
/* lasso_init() must be run before any other lasso-functions. */
lasso_init();
/* curl_global_init() should be called before any other curl
* function. Relying on curl_easy_init() to call curl_global_init()
* isn't thread safe.
*/
curl_res = curl_global_init(CURL_GLOBAL_SSL);
if(curl_res != CURLE_OK) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"Failed to initialize curl library: %u", curl_res);
}
return;
}
static int am_create_request(request_rec *r)
{
am_req_cfg_rec *req_cfg;
req_cfg = apr_pcalloc(r->pool, sizeof(am_req_cfg_rec));
req_cfg->cookie_value = NULL;
#ifdef HAVE_ECP
req_cfg->ecp_authn_req = false;
#endif /* HAVE_ECP */
#ifdef ENABLE_DIAGNOSTICS
req_cfg->diag_emitted = false;
#endif
ap_set_module_config(r->request_config, &auth_mellon_module, req_cfg);
return OK;
}
static void register_hooks(apr_pool_t *p)
{
/* Our handler needs to run before mod_proxy so that it can properly
* return ECP AuthnRequest messages when running as a reverse proxy.
* See: https://github.com/Uninett/mod_auth_mellon/pull/196
*/
static const char * const run_handler_before[]={ "mod_proxy.c", NULL };
ap_hook_access_checker(am_auth_mellon_user, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_check_user_id(am_check_uid, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_config(am_global_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_child_init(am_child_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_create_request(am_create_request, NULL, NULL, APR_HOOK_MIDDLE);
/* Add the hook to handle requests to the mod_auth_mellon endpoint.
*
* This is APR_HOOK_FIRST because we do not expect nor require users
* to add a SetHandler option for the endpoint. Instead, simply
* setting MellonEndpointPath should be enough.
*
* Therefore this hook must run before any handler that may check
* r->handler and decide that it is the only handler for this URL.
*/
ap_hook_handler(am_handler, NULL, run_handler_before, APR_HOOK_FIRST);
#ifdef ENABLE_DIAGNOSTICS
ap_hook_open_logs(am_diag_log_init,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_log_transaction(am_diag_finalize_request,NULL,NULL,APR_HOOK_REALLY_LAST);
#endif
}
module AP_MODULE_DECLARE_DATA auth_mellon_module =
{
STANDARD20_MODULE_STUFF,
auth_mellon_dir_config,
auth_mellon_dir_merge,
auth_mellon_server_config,
auth_mellon_srv_merge,
auth_mellon_commands,
register_hooks
};