diff options
Diffstat (limited to '')
20 files changed, 804 insertions, 259 deletions
diff --git a/sca-cpp/trunk/modules/http/basic-auth-conf b/sca-cpp/trunk/modules/http/basic-auth-conf index 39dde90b50..8710d1fdf7 100755 --- a/sca-cpp/trunk/modules/http/basic-auth-conf +++ b/sca-cpp/trunk/modules/http/basic-auth-conf @@ -25,9 +25,16 @@ root=`echo "import os; print os.path.realpath('$1')" | python` conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` host=`echo $conf | awk '{ print $6 }'` -loc=$2 -if [ "$loc" = "" ]; then +if [ "$2" = "" ]; then + providers="file" +else + providers="$2 file" +fi + +if [ "$3" = "" ]; then loc="/" +else + loc="$3" fi sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` @@ -52,7 +59,9 @@ cat >>$root/conf/locauth$sslsuffix.conf <<EOF <Location $loc> AuthType Basic AuthName "$host" -AuthBasicProvider file +AuthBasicProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / Require valid-user </Location> diff --git a/sca-cpp/trunk/modules/http/cert-auth-conf b/sca-cpp/trunk/modules/http/cert-auth-conf index 514e46324f..a30fdfff8c 100755 --- a/sca-cpp/trunk/modules/http/cert-auth-conf +++ b/sca-cpp/trunk/modules/http/cert-auth-conf @@ -32,6 +32,12 @@ else sslsuffix="-ssl" fi +if [ "$2" = "" ]; then + providers="file" +else + providers="$2 file" +fi + # Disallow public access to server resources cat >$root/conf/noauth$sslsuffix.conf <<EOF # Generated by: cert-auth-conf $* @@ -49,7 +55,9 @@ SSLVerifyDepth 1 <Location /> AuthType Basic AuthName "$host" -AuthBasicProvider file +AuthBasicProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / Require valid-user </Location> diff --git a/sca-cpp/trunk/modules/http/form-auth-conf b/sca-cpp/trunk/modules/http/form-auth-conf index 08b97b9df8..fbe943f3d9 100755 --- a/sca-cpp/trunk/modules/http/form-auth-conf +++ b/sca-cpp/trunk/modules/http/form-auth-conf @@ -26,9 +26,15 @@ conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` host=`echo $conf | awk '{ print $6 }'` if [ "$2" = "" ]; then + providers="file" +else + providers="$2 file" +fi + +if [ "$3" = "" ]; then pw=`cat $root/cert/ca.key | head -2 | tail -1` else - pw="$2" + pw="$3" fi sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` @@ -53,8 +59,10 @@ cat >>$root/conf/locauth$sslsuffix.conf <<EOF <Location /> AuthType Form AuthName "$host" -AuthFormProvider file -AuthFormLoginRequiredLocation /login +AuthFormProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / +AuthFormLoginRequiredLocation /login/ AuthFormLogoutLocation / Session On SessionCookieName TuscanyFormAuth domain=.$host; path=/ diff --git a/sca-cpp/trunk/modules/http/httpd-ssl-conf b/sca-cpp/trunk/modules/http/httpd-ssl-conf index 3cb90d61e0..f99a10071c 100755 --- a/sca-cpp/trunk/modules/http/httpd-ssl-conf +++ b/sca-cpp/trunk/modules/http/httpd-ssl-conf @@ -43,6 +43,15 @@ htdocs=`echo $conf | awk '{ print $8 }'` mkdir -p $htdocs htdocs=`echo "import os; print os.path.realpath('$htdocs')" | python` +uname=`uname -s` +if [ $uname = "Darwin" ]; then + libsuffix=".dylib" +else + libsuffix=".so" +fi + +modules_prefix=`cat $here/httpd-modules.prefix` + # Extract organization name from our CA certificate org=`openssl x509 -noout -subject -nameopt multiline -in $root/cert/ca.crt | grep organizationName | awk -F "= " '{ print $2 }'` @@ -69,7 +78,7 @@ ServerName https://$host$sslpportsuffix <Location /> RewriteEngine on -RewriteCond %{HTTP_HOST} !^$host [NC] +Include conf/hostcond.conf RewriteCond %{HTTP:X-Forwarded-Server} ^$ [NC] RewriteCond %{REQUEST_URI} !^/server-status [NC] RewriteCond %{REQUEST_URI} !^/balancer-manager [NC] @@ -176,7 +185,7 @@ Include conf/log-ssl.conf # Enable HTTPS reverse proxy ProxyRequests Off -ProxyPreserveHost Off +ProxyPreserveHost On ProxyStatus On SSLProxyEngine on SSLProxyCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL @@ -222,11 +231,6 @@ CustomLog $root/logs/ssl_access_log sslcombined EOF # Configure virtual hosts -proxycert="server" -if [ "$proxyconf" != "" ]; then - proxycert="proxy" -fi - cat >$root/conf/svhost-ssl.conf <<EOF # Generated by: httpd-ssl-conf $* # Static virtual host configuration @@ -238,10 +242,6 @@ SSLCertificateChainFile "$root/cert/ca.crt" SSLCertificateFile "$root/cert/server.crt" SSLCertificateKeyFile "$root/cert/server.key" -# Declare proxy SSL client certificates -SSLProxyCACertificateFile "$root/cert/ca.crt" -SSLProxyMachineCertificateFile "$root/cert/$proxycert.pem" - EOF cat >$root/conf/dvhost-ssl.conf <<EOF @@ -255,9 +255,5 @@ SSLCertificateChainFile "$root/cert/ca.crt" SSLCertificateFile "$root/cert/vhost.crt" SSLCertificateKeyFile "$root/cert/vhost.key" -# Declare proxy SSL client certificates -SSLProxyCACertificateFile "$root/cert/ca.crt" -SSLProxyMachineCertificateFile "$root/cert/$proxycert.pem" - EOF diff --git a/sca-cpp/trunk/modules/http/httpd-tunnel-ssl-conf b/sca-cpp/trunk/modules/http/httpd-tunnel-ssl-conf new file mode 100755 index 0000000000..0028576364 --- /dev/null +++ b/sca-cpp/trunk/modules/http/httpd-tunnel-ssl-conf @@ -0,0 +1,38 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate a minimal HTTPD SSL Tunnel configuration +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + libsuffix=".dylib" +else + libsuffix=".so" +fi + +# Generate required modules list +cat >>$root/conf/modules.conf <<EOF +# Generated by: httpd-tunnel-ssl-conf $* +LoadModule mod_tuscany_ssltunnel $here/libmod_tuscany_ssltunnel$libsuffix + +EOF + diff --git a/sca-cpp/trunk/modules/http/mod-openauth.cpp b/sca-cpp/trunk/modules/http/mod-openauth.cpp index b1aabd73fe..2e308ecedb 100644 --- a/sca-cpp/trunk/modules/http/mod-openauth.cpp +++ b/sca-cpp/trunk/modules/http/mod-openauth.cpp @@ -44,6 +44,7 @@ #include "http.hpp" #include "openauth.hpp" + extern "C" { extern module AP_MODULE_DECLARE_DATA mod_tuscany_openauth; } @@ -64,6 +65,20 @@ public: }; /** + * Authentication provider configuration. + */ +class AuthnProviderConf { +public: + AuthnProviderConf() : name(), provider(NULL) { + } + AuthnProviderConf(const string name, const authn_provider* provider) : name(name), provider(provider) { + } + + string name; + const authn_provider* provider; +}; + +/** * Directory configuration. */ class DirConf { @@ -75,6 +90,7 @@ public: const char* dir; bool enabled; string login; + list<AuthnProviderConf> apcs; }; #ifdef WANT_MAINTAINER_LOG @@ -101,10 +117,39 @@ const bool debugSession(request_rec* r, session_rec* z) { #endif /** + * Run the authnz hooks to authenticate a request. + */ +const failable<int> checkAuthnzProviders(const string& user, const string& pw, request_rec* r, const list<AuthnProviderConf>& apcs) { + if (isNil(apcs)) + return mkfailure<int>("Authentication failure for: " + user); + const AuthnProviderConf apc = car<AuthnProviderConf>(apcs); + if (apc.provider == NULL || !apc.provider->check_password) + return checkAuthnzProviders(user, pw, r, cdr(apcs)); + + apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, c_str(apc.name)); + const authn_status auth_result = apc.provider->check_password(r, c_str(user), c_str(pw)); + apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE); + if (auth_result != AUTH_GRANTED) + return checkAuthnzProviders(user, pw, r, cdr(apcs)); + return OK; +} + +const failable<int> checkAuthnz(const string& user, const string& pw, request_rec* r, const DirConf& dc) { + if (substr(user, 0, 1) == "/" && pw == "password") + return mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED); + + if (isNil(dc.apcs)) { + const authn_provider* provider = (const authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, AUTHN_DEFAULT_PROVIDER, AUTHN_PROVIDER_VERSION); + return checkAuthnzProviders(user, pw, r, mklist<AuthnProviderConf>(AuthnProviderConf(AUTHN_DEFAULT_PROVIDER, provider))); + } + return checkAuthnzProviders(user, pw, r, dc.apcs); +} + +/** * Return the user info from a form auth encrypted session cookie. */ static int (*ap_session_load_fn) (request_rec * r, session_rec ** z) = NULL; -static void (*ap_session_get_fn) (request_rec * r, session_rec * z, const char *key, const char **value) = NULL; +static int (*ap_session_get_fn) (request_rec * r, session_rec * z, const char *key, const char **value) = NULL; const failable<value> userInfoFromSession(const string& realm, request_rec* r) { debug("modopenauth::userInfoFromSession"); @@ -186,36 +231,23 @@ const failable<int> authenticated(const list<list<value> >& info, request_rec* r } /** - * Run the authnz hooks to try to authenticate a request. - */ -const failable<int> checkAuthnz(const string& user, const string& pw, request_rec* r) { - const authn_provider* provider = (const authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, AUTHN_DEFAULT_PROVIDER, AUTHN_PROVIDER_VERSION); - if (!provider || !provider->check_password) - return mkfailure<int>("No Authn provider configured"); - apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, AUTHN_DEFAULT_PROVIDER); - const authn_status auth_result = provider->check_password(r, c_str(user), c_str(pw)); - apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE); - if (auth_result != AUTH_GRANTED) - return mkfailure<int>("Authentication failure for: " + user); - return OK; -} - -/** * Check user authentication. */ static int checkAuthn(request_rec *r) { + gc_scoped_pool pool(r->pool); + // Decline if we're not enabled or AuthType is not set to Open const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth); if (!dc.enabled) return DECLINED; const char* atype = ap_auth_type(r); - debug(atype, "modopenauth::checkAuthn::auth_type"); if (atype == NULL || strcasecmp(atype, "Open")) return DECLINED; - - // Create a scoped memory pool - gc_scoped_pool pool(r->pool); debug_httpdRequest(r, "modopenauth::checkAuthn::input"); + debug(atype, "modopenauth::checkAuthn::auth_type"); + + // Get the request args + const list<list<value> > args = httpd::queryArgs(r); // Get session id from the request const maybe<string> sid = sessionID(r, "TuscanyOpenAuth"); @@ -231,17 +263,17 @@ static int checkAuthn(request_rec *r) { return httpd::reportStatus(mkfailure<int>("Missing AuthName")); // Extract user info from the session id - const failable<value> info = userInfoFromCookie(content(sid), aname, r); - if (hasContent(info)) { + const failable<value> userinfo = userInfoFromCookie(content(sid), aname, r); + if (hasContent(userinfo)) { // Try to authenticate the request - const value uinfo = content(info); - const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", uinfo)), cadr(assoc<value>("password", uinfo)), r); + const value uinfo = content(userinfo); + const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", uinfo)), cadr(assoc<value>("password", uinfo)), r, dc); if (!hasContent(authz)) { // Authentication failed, redirect to login page r->ap_auth_type = const_cast<char*>(atype); - return httpd::reportStatus(login(dc.login, r)); + return httpd::reportStatus(login(dc.login, value(), 1, r)); } // Successfully authenticated, store the user info in the request @@ -265,12 +297,12 @@ static int checkAuthn(request_rec *r) { // Try to authenticate the request const value uinfo = content(info); - const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", uinfo)), cadr(assoc<value>("password", uinfo)), r); + const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", uinfo)), cadr(assoc<value>("password", uinfo)), r, dc); if (!hasContent(authz)) { // Authentication failed, redirect to login page r->ap_auth_type = const_cast<char*>(atype); - return httpd::reportStatus(login(dc.login, r)); + return httpd::reportStatus(login(dc.login, value(), 1, r)); } // Successfully authenticated, store the user info in the request @@ -279,9 +311,6 @@ static int checkAuthn(request_rec *r) { } } - // Get the request args - const list<list<value> > args = httpd::queryArgs(r); - // Decline if the request is for another authentication provider if (!isNil(assoc<value>("openid_identifier", args))) return DECLINED; @@ -291,57 +320,59 @@ static int checkAuthn(request_rec *r) { hasContent(sessionID(r, "TuscanyOAuth1")) || hasContent(sessionID(r, "TuscanyOAuth2"))) return DECLINED; + r->ap_auth_type = const_cast<char*>(atype); - return httpd::reportStatus(login(dc.login, r)); + return httpd::reportStatus(login(dc.login, value(), value(), r)); } /** - * Fixup cache control. + * Save the auth session cookie in the response. */ -bool filterCacheControl(const string& tok) { - return tok != "no-cache"; +static int sessionCookieSave(request_rec* r, session_rec* z) { + gc_scoped_pool pool(r->pool); + + const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth); + if (!dc.enabled) + return DECLINED; + + debug(c_str(cookie("TuscanyOpenAuth", z->encoded, httpd::hostName(r))), "modopenauth::setcookie"); + apr_table_set(r->err_headers_out, "Set-Cookie", c_str(cookie("TuscanyOpenAuth", z->encoded, httpd::hostName(r)))); + return OK; } -static apr_status_t outputFilter(ap_filter_t * f, apr_bucket_brigade * in) { - request_rec *r = f->r->main; - if (!r) - r = f->r; - for (; r != NULL; r = r->next) { - if (r->status != HTTP_OK && r->status != HTTP_NOT_MODIFIED) { - - // Don't cache errors and redirects - debug("no-cache", "modopenauth::outputFilter::nokCacheControl"); - apr_table_set(r->headers_out, "Cache-Control", "no-cache"); - continue; - } +/** + * Load the auth session cookie from the request. Similar + */ +static int sessionCookieLoad(request_rec* r, session_rec** z) { + gc_scoped_pool pool(r->pool); - // Cache OK content - const char* cc = apr_table_get(r->headers_out, "Cache-Control"); - if (cc == NULL) { - debug("modopenauth::outputFilter::noCacheControl"); - continue; - } - debug(cc, "modopenauth::outputFilter::cacheControl"); - const string ncc = join(", ", filter<string>(filterCacheControl, tokenize(", ", cc))); - if (length(ncc) == 0) { - debug("modopenauth::outputFilter::noCacheControl"); - apr_table_unset(r->headers_out, "Cache-Control"); - continue; - } + const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_openauth); + if (!dc.enabled) + return DECLINED; - debug(ncc, "modopenauth::outputFilter::okCacheControl"); - apr_table_set(r->headers_out, "Cache-Control", c_str(ncc)); + // First look in the notes + const char* note = apr_pstrcat(r->pool, "mod_openauth", "TuscanyOpenAuth", NULL); + session_rec* zz = (session_rec*)(void*)apr_table_get(r->notes, note); + if (zz != NULL) { + *z = zz; + return OK; } - ap_remove_output_filter(f); - return ap_pass_brigade(f->next, in); -} + // Parse the cookie + const maybe<string> sid = openauth::sessionID(r, "TuscanyOpenAuth"); -/** - * Insert our cache control output filter. - */ -static void insertOutputFilter(request_rec * r) { - ap_add_output_filter("mod_openauth", NULL, r, r->connection); + // Create a new session + zz = (session_rec*)apr_pcalloc(r->pool, sizeof(session_rec)); + zz->pool = r->pool; + zz->entries = apr_table_make(r->pool, 10); + zz->encoded = hasContent(sid)? c_str(content(sid)) : NULL; + zz->uuid = (apr_uuid_t *) apr_pcalloc(r->pool, sizeof(apr_uuid_t)); + *z = zz; + + // Store it in the notes + apr_table_setn(r->notes, note, (char*)zz); + + return OK; } /** @@ -357,6 +388,7 @@ int postConfigMerge(ServerConf& mainsc, server_rec* s) { int postConfig(apr_pool_t* p, unused apr_pool_t* plog, unused apr_pool_t* ptemp, server_rec* s) { gc_scoped_pool pool(p); + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_openauth); debug(httpd::serverName(s), "modopenauth::postConfig::serverName"); @@ -369,6 +401,7 @@ int postConfig(apr_pool_t* p, unused apr_pool_t* plog, unused apr_pool_t* ptemp, */ void childInit(apr_pool_t* p, server_rec* s) { gc_scoped_pool pool(p); + ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_openauth); if(psc == NULL) { cfailure << "[Tuscany] Due to one or more errors mod_tuscany_openauth loading failed. Causing apache to stop loading." << endl; @@ -395,11 +428,25 @@ const char* confLogin(cmd_parms *cmd, void *c, const char* arg) { dc.login = arg; return NULL; } +const char* confAuthnProvider(cmd_parms *cmd, void *c, const char* arg) { + gc_scoped_pool pool(cmd->pool); + DirConf& dc = httpd::dirConf<DirConf>(c); + + // Lookup and cache the Authn provider + const authn_provider* provider = (authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, arg, AUTHN_PROVIDER_VERSION); + if (provider == NULL) + return apr_psprintf(cmd->pool, "Unknown Authn provider: %s", arg); + if (!provider->check_password) + return apr_psprintf(cmd->pool, "The '%s' Authn provider doesn't support password authentication", arg); + dc.apcs = append<AuthnProviderConf>(dc.apcs, mklist<AuthnProviderConf>(AuthnProviderConf(arg, provider))); + return NULL; +} /** * HTTP server module declaration. */ const command_rec commands[] = { + AP_INIT_ITERATE("AuthOpenAuthProvider", (const char*(*)())confAuthnProvider, NULL, OR_AUTHCFG, "Auth providers for a directory or location"), AP_INIT_FLAG("AuthOpenAuth", (const char*(*)())confEnabled, NULL, OR_AUTHCFG, "Tuscany Open Auth authentication On | Off"), AP_INIT_TAKE1("AuthOpenAuthLoginPage", (const char*(*)())confLogin, NULL, OR_AUTHCFG, "Tuscany Open Auth login page"), {NULL, NULL, NULL, 0, NO_ARGS, NULL} @@ -409,8 +456,8 @@ void registerHooks(unused apr_pool_t *p) { ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_check_authn(checkAuthn, NULL, NULL, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF); - ap_register_output_filter("mod_openauth", outputFilter, NULL, AP_FTYPE_CONTENT_SET); - ap_hook_insert_filter(insertOutputFilter, NULL, NULL, APR_HOOK_LAST); + ap_hook_session_load(sessionCookieLoad, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_session_save(sessionCookieSave, NULL, NULL, APR_HOOK_MIDDLE); } } diff --git a/sca-cpp/trunk/modules/http/mod-security-conf b/sca-cpp/trunk/modules/http/mod-security-conf index 4d978a01cb..5d03fc5cfb 100755 --- a/sca-cpp/trunk/modules/http/mod-security-conf +++ b/sca-cpp/trunk/modules/http/mod-security-conf @@ -51,8 +51,8 @@ SecDefaultAction "phase:2,pass,nolog,auditlog" #SecDebugLog $root/logs//modsec_debug_log #SecDebugLogLevel 3 -# Allow mod-security to access request bodies -SecRequestBodyAccess On +# Process request bodies +SecRequestBodyAccess Off SecRule REQUEST_HEADERS:Content-Type "text/xml" "phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" SecRule REQUEST_HEADERS:Content-Type "application/xml" "phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" SecRequestBodyLimit 13107200 @@ -90,8 +90,8 @@ SecRule RESPONSE_STATUS "@streq 408" "phase:5,t:none,nolog,pass, setvar:ip.slow_ SecRule IP:SLOW_DOS_COUNTER "@gt 5" "phase:1,t:none,log,drop, msg:'Client Connection Dropped due to high # of slow DoS alerts'" SecWriteStateLimit 50 -# Allow mod-security to access response bodies -SecResponseBodyAccess On +# Process response bodies +SecResponseBodyAccess Off SecResponseBodyMimeType text/plain text/html text/xml application/xml SecResponseBodyLimit 524288 SecResponseBodyLimitAction ProcessPartial @@ -157,28 +157,28 @@ SecRule &TX:REAL_IP "@eq 0" "phase:1,id:'981218',t:none,pass,nolog,initcol:glob # Include all base mod-security CRS rules Include ${modsecurity_prefix}/base_rules/modsecurity_crs_20_protocol_violations.conf -Include ${modsecurity_prefix}/base_rules/modsecurity_crs_41_xss_attacks.conf -Include ${modsecurity_prefix}/base_rules/modsecurity_crs_50_outbound.conf +#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_41_xss_attacks.conf +#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_50_outbound.conf Include ${modsecurity_prefix}/base_rules/modsecurity_crs_35_bad_robots.conf Include ${modsecurity_prefix}/base_rules/modsecurity_crs_47_common_exceptions.conf Include ${modsecurity_prefix}/base_rules/modsecurity_crs_60_correlation.conf -Include ${modsecurity_prefix}/base_rules/modsecurity_crs_40_generic_attacks.conf +#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_40_generic_attacks.conf Include ${modsecurity_prefix}/base_rules/modsecurity_crs_21_protocol_anomalies.conf Include ${modsecurity_prefix}/base_rules/modsecurity_crs_30_http_policy.conf Include ${modsecurity_prefix}/base_rules/modsecurity_crs_49_inbound_blocking.conf Include ${modsecurity_prefix}/base_rules/modsecurity_crs_41_sql_injection_attacks.conf -Include ${modsecurity_prefix}/base_rules/modsecurity_crs_45_trojans.conf +#Include ${modsecurity_prefix}/base_rules/modsecurity_crs_45_trojans.conf Include ${modsecurity_prefix}/base_rules/modsecurity_crs_59_outbound_blocking.conf Include ${modsecurity_prefix}/base_rules/modsecurity_crs_23_request_limits.conf Include ${modsecurity_prefix}/base_rules/modsecurity_crs_42_tight_security.conf # Include some optional mod-security CRS rules -Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_10_ignore_static.conf -Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_13_xml_enabler.conf -Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_25_cc_known.conf -Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_42_comment_spam.conf -Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_47_skip_outbound_checks.conf -Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_55_application_defects.conf +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_10_ignore_static.conf +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_13_xml_enabler.conf +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_25_cc_known.conf +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_42_comment_spam.conf +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_47_skip_outbound_checks.conf +#Include ${modsecurity_prefix}/optional_rules/modsecurity_crs_55_application_defects.conf EOF diff --git a/sca-cpp/trunk/modules/http/open-auth-conf b/sca-cpp/trunk/modules/http/open-auth-conf index 5226622058..f4715b3a1c 100755 --- a/sca-cpp/trunk/modules/http/open-auth-conf +++ b/sca-cpp/trunk/modules/http/open-auth-conf @@ -40,14 +40,20 @@ else fi if [ "$2" = "" ]; then + providers="file" +else + providers="$2 file" +fi + +if [ "$3" = "" ]; then pw=`cat $root/cert/ca.key | head -2 | tail -1` else - pw="$2" + pw="$3" fi # Configure HTTPD mod_tuscany_openauth module cat >>$root/conf/modules.conf <<EOF -# Generated by: openauth-conf $* +# Generated by: open-auth-conf $* # Load support for Open authentication LoadModule mod_tuscany_openauth $here/libmod_tuscany_openauth$libsuffix @@ -67,11 +73,13 @@ cat >>$root/conf/locauth$sslsuffix.conf <<EOF <Location /> AuthType Open AuthName "$host" +AuthOpenAuthProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / Session On -SessionCookieName TuscanyOpenAuth domain=.$host; path=/ SessionCryptoPassphrase $pw AuthOpenAuth On -AuthOpenAuthLoginPage /login +AuthOpenAuthLoginPage /login/ Require valid-user </Location> @@ -79,8 +87,10 @@ Require valid-user <Location /login/dologin> AuthType Form AuthName "$host" -AuthFormProvider file -AuthFormLoginRequiredLocation /login +AuthFormProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / +AuthFormLoginRequiredLocation /login/?openauth_attempt=1 AuthFormLogoutLocation / Require valid-user SetHandler form-login-handler diff --git a/sca-cpp/trunk/modules/http/openauth.hpp b/sca-cpp/trunk/modules/http/openauth.hpp index 5d887885aa..3ffa88d362 100644 --- a/sca-cpp/trunk/modules/http/openauth.hpp +++ b/sca-cpp/trunk/modules/http/openauth.hpp @@ -77,7 +77,7 @@ const string cookie(const string& key, const string& sid, const string& domain) const time_t t = time(NULL) + 86400; char exp[32]; strftime(exp, 32, "%a, %d-%b-%Y %H:%M:%S GMT", gmtime(&t)); - const string c = key + string("=") + sid + "; expires=" + string(exp) + "; domain=." + domain + "; path=/"; + const string c = key + string("=") + sid + "; expires=" + string(exp) + "; domain=." + httpd::realm(domain) + "; path=/"; debug(c, "openauth::cookie"); return c; } @@ -85,9 +85,11 @@ const string cookie(const string& key, const string& sid, const string& domain) /** * Redirect to the configured login page. */ -const failable<int> login(const string& page, request_rec* r) { - const list<list<value> > largs = mklist<list<value> >(mklist<value>("openauth_referrer", httpd::escape(httpd::url(r->uri, r)))); - const string loc = httpd::url(page, r) + string("?") + http::queryString(largs); +const failable<int> login(const string& page, const value& ref, const value& attempt, request_rec* r) { + const list<list<value> > rarg = ref == string("/")? list<list<value> >() : mklist<list<value> >(mklist<value>("openauth_referrer", httpd::escape(httpd::url(isNil(ref)? r->uri : ref, r)))); + const list<list<value> > aarg = isNil(attempt)? list<list<value> >() : mklist<list<value> >(mklist<value>("openauth_attempt", attempt)); + const list<list<value> > largs = append<list<value> >(rarg, aarg); + const string loc = isNil(largs)? httpd::url(page, r) : httpd::url(page, r) + string("?") + http::queryString(largs); debug(loc, "openauth::login::uri"); return httpd::externalRedirect(loc, r); } diff --git a/sca-cpp/trunk/modules/http/passwd-auth-conf b/sca-cpp/trunk/modules/http/passwd-auth-conf index 119b0fbb3b..718b96de0a 100755 --- a/sca-cpp/trunk/modules/http/passwd-auth-conf +++ b/sca-cpp/trunk/modules/http/passwd-auth-conf @@ -27,5 +27,5 @@ httpd_prefix=`cat $here/httpd.prefix` # Create password file touch $root/conf/httpd.passwd -$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd $user $pass 2>/dev/null +$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd "$user" "$pass" 2>/dev/null diff --git a/sca-cpp/trunk/modules/oauth/Makefile.am b/sca-cpp/trunk/modules/oauth/Makefile.am index a5a5916a84..9e8eb5a0dd 100644 --- a/sca-cpp/trunk/modules/oauth/Makefile.am +++ b/sca-cpp/trunk/modules/oauth/Makefile.am @@ -19,7 +19,7 @@ if WANT_OAUTH INCLUDES = -I${HTTPD_INCLUDE} -I${LIBOAUTH_INCLUDE} -dist_mod_SCRIPTS = oauth-conf oauth-memcached-conf oauth1-appkey-conf oauth2-appkey-conf +dist_mod_SCRIPTS = oauth-conf oauth12-conf oauth-memcached-conf oauth1-appkey-conf oauth2-appkey-conf moddir=$(prefix)/modules/oauth mod_LTLIBRARIES = libmod_tuscany_oauth1.la libmod_tuscany_oauth2.la diff --git a/sca-cpp/trunk/modules/oauth/mod-oauth1.cpp b/sca-cpp/trunk/modules/oauth/mod-oauth1.cpp index eb23443491..d2c7cf7ddd 100644 --- a/sca-cpp/trunk/modules/oauth/mod-oauth1.cpp +++ b/sca-cpp/trunk/modules/oauth/mod-oauth1.cpp @@ -71,6 +71,20 @@ public: }; /** + * Authentication provider configuration. + */ +class AuthnProviderConf { +public: + AuthnProviderConf() : name(), provider(NULL) { + } + AuthnProviderConf(const string name, const authn_provider* provider) : name(name), provider(provider) { + } + + string name; + const authn_provider* provider; +}; + +/** * Directory configuration. */ class DirConf { @@ -83,9 +97,39 @@ public: bool enabled; string login; list<list<value> > scopeattrs; + list<AuthnProviderConf> apcs; }; /** + * Run the authnz hooks to authenticate a request. + */ +const failable<int> checkAuthnzProviders(const string& user, request_rec* r, const list<AuthnProviderConf>& apcs) { + if (isNil(apcs)) + return mkfailure<int>("Authentication failure for: " + user, HTTP_UNAUTHORIZED); + const AuthnProviderConf apc = car<AuthnProviderConf>(apcs); + if (apc.provider == NULL || !apc.provider->check_password) + return checkAuthnzProviders(user, r, cdr(apcs)); + + apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, c_str(apc.name)); + const authn_status auth_result = apc.provider->check_password(r, c_str(string("/oauth1/") + user), "password"); + apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE); + if (auth_result != AUTH_GRANTED) + return checkAuthnzProviders(user, r, cdr(apcs)); + return OK; +} + +const failable<int> checkAuthnz(const string& user, request_rec* r, const list<AuthnProviderConf>& apcs) { + if (substr(user, 0, 1) == "/") + return mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED); + + if (isNil(apcs)) { + const authn_provider* provider = (const authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, AUTHN_DEFAULT_PROVIDER, AUTHN_PROVIDER_VERSION); + return checkAuthnzProviders(user, r, mklist<AuthnProviderConf>(AuthnProviderConf(AUTHN_DEFAULT_PROVIDER, provider))); + } + return checkAuthnzProviders(user, r, apcs); +} + +/** * Return the user info for a session. */ const failable<value> userInfo(const value& sid, const memcache::MemCached& mc) { @@ -95,27 +139,31 @@ const failable<value> userInfo(const value& sid, const memcache::MemCached& mc) /** * Handle an authenticated request. */ -const failable<int> authenticated(const list<list<value> >& attrs, const list<list<value> >& info, request_rec* r) { - debug(info, "modoauth1::authenticated::info"); +const failable<int> authenticated(const list<list<value> >& userinfo, const bool check, request_rec* r, const list<list<value> >& scopeattrs, const list<AuthnProviderConf>& apcs) { + debug(userinfo, "modoauth2::authenticated::userinfo"); - if (isNil(attrs)) { + if (isNil(scopeattrs)) { // Store user id in an environment variable - const list<value> id = assoc<value>("id", info); + const list<value> id = assoc<value>("id", userinfo); if (isNil(id) || isNil(cdr(id))) return mkfailure<int>("Couldn't retrieve user id"); - apr_table_set(r->subprocess_env, "OAUTH1_ID", apr_pstrdup(r->pool, c_str(cadr(id)))); + apr_table_set(r->subprocess_env, "OAUTH2_ID", apr_pstrdup(r->pool, c_str(cadr(id)))); // If the request user field has not been mapped to another attribute, map the // OAuth id attribute to it if (r->user == NULL || r->user[0] == '\0') r->user = apr_pstrdup(r->pool, c_str(cadr(id))); + + // Run the authnz hooks to check the authenticated user + if (check) + return checkAuthnz(r->user == NULL? "" : r->user, r, apcs); return OK; } - // Store each configure OAuth scope attribute in an environment variable - const list<value> a = car(attrs); - const list<value> v = assoc<value>(cadr(a), info); + // Store each configured OAuth scope attribute in an environment variable + const list<value> a = car(scopeattrs); + const list<value> v = assoc<value>(cadr(a), userinfo); if (!isNil(v) && !isNil(cdr(v))) { // Map the REMOTE_USER attribute to the request user field @@ -124,7 +172,7 @@ const failable<int> authenticated(const list<list<value> >& attrs, const list<li else apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, c_str(car(a))), apr_pstrdup(r->pool, c_str(cadr(v)))); } - return authenticated(cdr(attrs), info, r); + return authenticated(userinfo, check, r, cdr(scopeattrs), apcs); } /** @@ -297,7 +345,8 @@ const failable<list<value> > profileUserInfo(const value& cid, const string& inf /** * Handle an access_token request. */ -const failable<int> accessToken(const list<list<value> >& args, request_rec* r, const list<list<value> >& appkeys, const memcache::MemCached& mc) { +const failable<int> accessToken(const list<list<value> >& args, request_rec* r, const list<list<value> >& appkeys, const list<list<value> >& scopeattrs, const list<AuthnProviderConf>& apcs, const memcache::MemCached& mc) { + // Extract access_token URI, client ID and verification code const list<value> ref = assoc<value>("openauth_referrer", args); if (isNil(ref) || isNil(cdr(ref))) @@ -372,13 +421,18 @@ const failable<int> accessToken(const list<list<value> >& args, request_rec* r, debug(profres, "modoauth1::access_token::profres"); // Retrieve the user info from the profile - const failable<list<value> > iv = profileUserInfo(cadr(cid), profres); - if (!hasContent(iv)) - return mkfailure<int>(iv); + const failable<list<value> > userinfo = profileUserInfo(cadr(cid), profres); + if (!hasContent(userinfo)) + return mkfailure<int>(userinfo); + + // Validate the authenticated user + const failable<int> authrc = authenticated(content(userinfo), true, r, scopeattrs, apcs); + if (!hasContent(authrc)) + return authrc; // Store user info in memcached keyed by session ID const value sid = string("OAuth1_") + mkrand(); - const failable<bool> prc = memcache::put(mklist<value>("tuscanyOAuth1", sid), content(iv), mc); + const failable<bool> prc = memcache::put(mklist<value>("tuscanyOAuth1", sid), content(userinfo), mc); if (!hasContent(prc)) return mkfailure<int>(prc); @@ -392,20 +446,19 @@ const failable<int> accessToken(const list<list<value> >& args, request_rec* r, * Check user authentication. */ static int checkAuthn(request_rec *r) { + gc_scoped_pool pool(r->pool); + // Decline if we're not enabled or AuthType is not set to Open const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_oauth1); if (!dc.enabled) return DECLINED; const char* atype = ap_auth_type(r); - debug(atype, "modopenauth::checkAuthn::auth_type"); if (atype == NULL || strcasecmp(atype, "Open")) return DECLINED; - - // Create a scoped memory pool - gc_scoped_pool pool(r->pool); + debug_httpdRequest(r, "modoauth1::checkAuthn::input"); + debug(atype, "modopenauth::checkAuthn::auth_type"); // Get the server configuration - debug_httpdRequest(r, "modoauth1::checkAuthn::input"); const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_oauth1); // Get session id from the request @@ -415,24 +468,33 @@ static int checkAuthn(request_rec *r) { if (substr(content(sid), 0, 7) != "OAuth1_") return DECLINED; - // If we're authenticated store the user info in the request - const failable<value> info = userInfo(content(sid), sc.mc); - if (hasContent(info)) { - r->ap_auth_type = const_cast<char*>(atype); - return httpd::reportStatus(authenticated(dc.scopeattrs, content(info), r)); - } + // Extract the user info from the auth session + const failable<value> userinfo = userInfo(content(sid), sc.mc); + if (!hasContent(userinfo)) + return httpd::reportStatus(mkfailure<int>(userinfo)); + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(authenticated(content(userinfo), false, r, dc.scopeattrs, dc.apcs)); } + // Get the request args + const list<list<value> > args = httpd::queryArgs(r); + // Handle OAuth authorize request step if (string(r->uri) == "/oauth1/authorize/") { r->ap_auth_type = const_cast<char*>(atype); - return httpd::reportStatus(authorize(httpd::queryArgs(r), r, sc.appkeys, sc.mc)); + return httpd::reportStatus(authorize(args, r, sc.appkeys, sc.mc)); } // Handle OAuth access_token request step if (string(r->uri) == "/oauth1/access_token/") { r->ap_auth_type = const_cast<char*>(atype); - return httpd::reportStatus(accessToken(httpd::queryArgs(r), r, sc.appkeys, sc.mc)); + const failable<int> authrc = accessToken(args, r, sc.appkeys, dc.scopeattrs, dc.apcs, sc.mc); + + // Redirect to the login page if user is not authorized + if (!hasContent(authrc) && rcode(authrc) == HTTP_UNAUTHORIZED) + return httpd::reportStatus(openauth::login(dc.login, string("/"), 1, r)); + + return httpd::reportStatus(authrc); } // Redirect to the login page, unless we have a session id or an authorization @@ -443,10 +505,11 @@ static int checkAuthn(request_rec *r) { hasContent(openauth::sessionID(r, "TuscanyOpenAuth")) || hasContent(openauth::sessionID(r, "TuscanyOAuth2"))) return DECLINED; - if ((substr(string(r->uri), 0, 8) == "/oauth2/") || !isNil(assoc<value>("openid_identifier", httpd::queryArgs(r)))) + if ((substr(string(r->uri), 0, 8) == "/oauth2/") || !isNil(assoc<value>("openid_identifier", args))) return DECLINED; + r->ap_auth_type = const_cast<char*>(atype); - return httpd::reportStatus(openauth::login(dc.login, r)); + return httpd::reportStatus(openauth::login(dc.login, value(), value(), r)); } /** @@ -471,6 +534,7 @@ int postConfigMerge(ServerConf& mainsc, server_rec* s) { int postConfig(apr_pool_t* p, unused apr_pool_t* plog, unused apr_pool_t* ptemp, server_rec* s) { gc_scoped_pool pool(p); + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_oauth1); debug(httpd::serverName(s), "modoauth1::postConfig::serverName"); @@ -487,7 +551,7 @@ public: } const gc_ptr<http::CURLSession> operator()() const { - return new (gc_new<http::CURLSession>()) http::CURLSession(ca, cert, key, ""); + return new (gc_new<http::CURLSession>()) http::CURLSession(ca, cert, key, "", 0); } private: @@ -501,6 +565,7 @@ private: */ void childInit(apr_pool_t* p, server_rec* s) { gc_scoped_pool pool(p); + ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_oauth1); if(psc == NULL) { cfailure << "[Tuscany] Due to one or more errors mod_tuscany_oauth1 loading failed. Causing apache to stop loading." << endl; @@ -572,11 +637,25 @@ const char* confScopeAttr(cmd_parms *cmd, void* c, const char* arg1, const char* dc.scopeattrs = cons<list<value> >(mklist<value>(arg1, arg2), dc.scopeattrs); return NULL; } +const char* confAuthnProvider(cmd_parms *cmd, void *c, const char* arg) { + gc_scoped_pool pool(cmd->pool); + DirConf& dc = httpd::dirConf<DirConf>(c); + + // Lookup and cache the Authn provider + const authn_provider* provider = (authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, arg, AUTHN_PROVIDER_VERSION); + if (provider == NULL) + return apr_psprintf(cmd->pool, "Unknown Authn provider: %s", arg); + if (!provider->check_password) + return apr_psprintf(cmd->pool, "The '%s' Authn provider doesn't support password authentication", arg); + dc.apcs = append<AuthnProviderConf>(dc.apcs, mklist<AuthnProviderConf>(AuthnProviderConf(arg, provider))); + return NULL; +} /** * HTTP server module declaration. */ const command_rec commands[] = { + AP_INIT_ITERATE("AuthOAuthProvider", (const char*(*)())confAuthnProvider, NULL, OR_AUTHCFG, "Auth providers for a directory or location"), AP_INIT_TAKE3("AddAuthOAuth1AppKey", (const char*(*)())confAppKey, NULL, RSRC_CONF, "OAuth 1.0 name app-id app-key"), AP_INIT_ITERATE("AddAuthOAuthMemcached", (const char*(*)())confMemcached, NULL, RSRC_CONF, "Memcached server host:port"), AP_INIT_FLAG("AuthOAuth", (const char*(*)())confEnabled, NULL, OR_AUTHCFG, "OAuth 1.0 authentication On | Off"), diff --git a/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp b/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp index cbece191aa..e384a0e742 100644 --- a/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp +++ b/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp @@ -65,6 +65,20 @@ public: }; /** + * Authentication provider configuration. + */ +class AuthnProviderConf { +public: + AuthnProviderConf() : name(), provider(NULL) { + } + AuthnProviderConf(const string name, const authn_provider* provider) : name(name), provider(provider) { + } + + string name; + const authn_provider* provider; +}; + +/** * Directory configuration. */ class DirConf { @@ -77,9 +91,39 @@ public: bool enabled; string login; list<list<value> > scopeattrs; + list<AuthnProviderConf> apcs; }; /** + * Run the authnz hooks to authenticate a request. + */ +const failable<int> checkAuthnzProviders(const string& user, request_rec* r, const list<AuthnProviderConf>& apcs) { + if (isNil(apcs)) + return mkfailure<int>("Authentication failure for: " + user, HTTP_UNAUTHORIZED); + const AuthnProviderConf apc = car<AuthnProviderConf>(apcs); + if (apc.provider == NULL || !apc.provider->check_password) + return checkAuthnzProviders(user, r, cdr(apcs)); + + apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, c_str(apc.name)); + const authn_status auth_result = apc.provider->check_password(r, c_str(string("/oauth2/") + user), "password"); + apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE); + if (auth_result != AUTH_GRANTED) + return checkAuthnzProviders(user, r, cdr(apcs)); + return OK; +} + +const failable<int> checkAuthnz(const string& user, request_rec* r, const list<AuthnProviderConf>& apcs) { + if (substr(user, 0, 1) == "/") + return mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED); + + if (isNil(apcs)) { + const authn_provider* provider = (const authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, AUTHN_DEFAULT_PROVIDER, AUTHN_PROVIDER_VERSION); + return checkAuthnzProviders(user, r, mklist<AuthnProviderConf>(AuthnProviderConf(AUTHN_DEFAULT_PROVIDER, provider))); + } + return checkAuthnzProviders(user, r, apcs); +} + +/** * Return the user info for a session. */ const failable<value> userInfo(const value& sid, const memcache::MemCached& mc) { @@ -89,13 +133,13 @@ const failable<value> userInfo(const value& sid, const memcache::MemCached& mc) /** * Handle an authenticated request. */ -const failable<int> authenticated(const list<list<value> >& attrs, const list<list<value> >& info, request_rec* r) { - debug(info, "modoauth2::authenticated::info"); +const failable<int> authenticated(const list<list<value> >& userinfo, const bool check, request_rec* r, const list<list<value> >& scopeattrs, const list<AuthnProviderConf>& apcs) { + debug(userinfo, "modoauth2::authenticated::userinfo"); - if (isNil(attrs)) { + if (isNil(scopeattrs)) { // Store user id in an environment variable - const list<value> id = assoc<value>("id", info); + const list<value> id = assoc<value>("id", userinfo); if (isNil(id) || isNil(cdr(id))) return mkfailure<int>("Couldn't retrieve user id"); apr_table_set(r->subprocess_env, "OAUTH2_ID", apr_pstrdup(r->pool, c_str(cadr(id)))); @@ -104,12 +148,16 @@ const failable<int> authenticated(const list<list<value> >& attrs, const list<li // OAuth id attribute to it if (r->user == NULL || r->user[0] == '\0') r->user = apr_pstrdup(r->pool, c_str(cadr(id))); + + // Run the authnz hooks to check the authenticated user + if (check) + return checkAuthnz(r->user == NULL? "" : r->user, r, apcs); return OK; } - // Store each configure OAuth scope attribute in an environment variable - const list<value> a = car(attrs); - const list<value> v = assoc<value>(cadr(a), info); + // Store each configured OAuth scope attribute in an environment variable + const list<value> a = car(scopeattrs); + const list<value> v = assoc<value>(cadr(a), userinfo); if (!isNil(v) && !isNil(cdr(v))) { // Map the REMOTE_USER attribute to the request user field @@ -118,7 +166,7 @@ const failable<int> authenticated(const list<list<value> >& attrs, const list<li else apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, c_str(car(a))), apr_pstrdup(r->pool, c_str(cadr(v)))); } - return authenticated(cdr(attrs), info, r); + return authenticated(userinfo, check, r, cdr(scopeattrs), apcs); } /** @@ -181,7 +229,8 @@ const failable<list<value> > profileUserInfo(const value& cid, const list<value> /** * Handle an access_token request. */ -const failable<int> accessToken(const list<list<value> >& args, request_rec* r, const list<list<value> >& appkeys, const perthread_ptr<http::CURLSession>& cs, const memcache::MemCached& mc) { +const failable<int> accessToken(const list<list<value> >& args, request_rec* r, const list<list<value> >& appkeys, const perthread_ptr<http::CURLSession>& cs, const list<list<value> >& scopeattrs, const list<AuthnProviderConf>& apcs, const memcache::MemCached& mc) { + // Extract access_token URI, client ID and authorization code parameters const list<value> state = assoc<value>("state", args); if (isNil(state) || isNil(cdr(state))) @@ -245,17 +294,22 @@ const failable<int> accessToken(const list<list<value> >& args, request_rec* r, debug(content(profres), "modoauth2::access_token::info"); // Retrieve the user info from the profile - const failable<list<value> > iv = profileUserInfo(cadr(cid), content(profres)); - if (!hasContent(iv)) - return mkfailure<int>(iv); + const failable<list<value> > userinfo = profileUserInfo(cadr(cid), content(profres)); + if (!hasContent(userinfo)) + return mkfailure<int>(userinfo); - // Store user info in memcached keyed by session ID + // Validate the authenticated user + const failable<int> authrc = authenticated(content(userinfo), true, r, scopeattrs, apcs); + if (!hasContent(authrc)) + return authrc; + + // Store user info in memcached keyed by a session ID const value sid = string("OAuth2_") + mkrand(); - const failable<bool> prc = memcache::put(mklist<value>("tuscanyOAuth2", sid), content(iv), mc); + const failable<bool> prc = memcache::put(mklist<value>("tuscanyOAuth2", sid), content(userinfo), mc); if (!hasContent(prc)) return mkfailure<int>(prc); - // Send session ID to the client in a cookie + // Send the session ID to the client in a cookie debug(c_str(openauth::cookie("TuscanyOAuth2", sid, httpd::hostName(r))), "modoauth2::access_token::setcookie"); apr_table_set(r->err_headers_out, "Set-Cookie", c_str(openauth::cookie("TuscanyOAuth2", sid, httpd::hostName(r)))); return httpd::externalRedirect(httpd::url(httpd::unescape(cadr(ref)), r), r); @@ -265,20 +319,19 @@ const failable<int> accessToken(const list<list<value> >& args, request_rec* r, * Check user authentication. */ static int checkAuthn(request_rec *r) { + gc_scoped_pool pool(r->pool); + // Decline if we're not enabled or AuthType is not set to Open const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_oauth2); if (!dc.enabled) return DECLINED; const char* atype = ap_auth_type(r); - debug(atype, "modopenauth::checkAuthn::auth_type"); if (atype == NULL || strcasecmp(atype, "Open")) return DECLINED; - - // Create a scoped memory pool - gc_scoped_pool pool(r->pool); + debug_httpdRequest(r, "modoauth2::checkAuthn::input"); + debug(atype, "modopenauth::checkAuthn::auth_type"); // Get the server configuration - debug_httpdRequest(r, "modoauth2::checkAuthn::input"); const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_oauth2); // Get session id from the request @@ -288,24 +341,33 @@ static int checkAuthn(request_rec *r) { if (substr(content(sid), 0, 7) != "OAuth2_") return DECLINED; - // If we're authenticated store the user info in the request - const failable<value> info = userInfo(content(sid), sc.mc); - if (hasContent(info)) { - r->ap_auth_type = const_cast<char*>(atype); - return httpd::reportStatus(authenticated(dc.scopeattrs, content(info), r)); - } + // Extract the user info from the auth session + const failable<value> userinfo = userInfo(content(sid), sc.mc); + if (!hasContent(userinfo)) + return httpd::reportStatus(mkfailure<int>(userinfo)); + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(authenticated(content(userinfo), false, r, dc.scopeattrs, dc.apcs)); } + // Get the request args + const list<list<value> > args = httpd::queryArgs(r); + // Handle OAuth authorize request step if (string(r->uri) == "/oauth2/authorize/") { r->ap_auth_type = const_cast<char*>(atype); - return httpd::reportStatus(authorize(httpd::queryArgs(r), r, sc.appkeys)); + return httpd::reportStatus(authorize(args, r, sc.appkeys)); } // Handle OAuth access_token request step if (string(r->uri) == "/oauth2/access_token/") { r->ap_auth_type = const_cast<char*>(atype); - return httpd::reportStatus(accessToken(httpd::queryArgs(r), r, sc.appkeys, sc.cs, sc.mc)); + const failable<int> authrc = accessToken(args, r, sc.appkeys, sc.cs, dc.scopeattrs, dc.apcs, sc.mc); + + // Redirect to the login page if user is not authorized + if (!hasContent(authrc) && rcode(authrc) == HTTP_UNAUTHORIZED) + return httpd::reportStatus(openauth::login(dc.login, string("/"), 1, r)); + + return httpd::reportStatus(authrc); } // Redirect to the login page, unless we have a session id or an authorization @@ -316,10 +378,11 @@ static int checkAuthn(request_rec *r) { hasContent(openauth::sessionID(r, "TuscanyOpenAuth")) || hasContent(openauth::sessionID(r, "TuscanyOAuth1"))) return DECLINED; - if ((substr(string(r->uri), 0, 8) == "/oauth1/") || !isNil(assoc<value>("openid_identifier", httpd::queryArgs(r)))) + if ((substr(string(r->uri), 0, 8) == "/oauth1/") || !isNil(assoc<value>("openid_identifier", args))) return DECLINED; + r->ap_auth_type = const_cast<char*>(atype); - return httpd::reportStatus(openauth::login(dc.login, r)); + return httpd::reportStatus(openauth::login(dc.login, value(), value(), r)); } /** @@ -344,6 +407,7 @@ int postConfigMerge(ServerConf& mainsc, server_rec* s) { int postConfig(apr_pool_t* p, unused apr_pool_t* plog, unused apr_pool_t* ptemp, server_rec* s) { gc_scoped_pool pool(p); + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_oauth2); debug(httpd::serverName(s), "modoauth2::postConfig::serverName"); @@ -360,7 +424,7 @@ public: } const gc_ptr<http::CURLSession> operator()() const { - return new (gc_new<http::CURLSession>()) http::CURLSession(ca, cert, key, ""); + return new (gc_new<http::CURLSession>()) http::CURLSession(ca, cert, key, "", 0); } private: @@ -374,6 +438,7 @@ private: */ void childInit(apr_pool_t* p, server_rec* s) { gc_scoped_pool pool(p); + ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_oauth2); if(psc == NULL) { cfailure << "[Tuscany] Due to one or more errors mod_tuscany_oauth2 loading failed. Causing apache to stop loading." << endl; @@ -445,11 +510,25 @@ const char* confScopeAttr(cmd_parms *cmd, void* c, const char* arg1, const char* dc.scopeattrs = cons<list<value> >(mklist<value>(arg1, arg2), dc.scopeattrs); return NULL; } +const char* confAuthnProvider(cmd_parms *cmd, void *c, const char* arg) { + gc_scoped_pool pool(cmd->pool); + DirConf& dc = httpd::dirConf<DirConf>(c); + + // Lookup and cache the Authn provider + const authn_provider* provider = (authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, arg, AUTHN_PROVIDER_VERSION); + if (provider == NULL) + return apr_psprintf(cmd->pool, "Unknown Authn provider: %s", arg); + if (!provider->check_password) + return apr_psprintf(cmd->pool, "The '%s' Authn provider doesn't support password authentication", arg); + dc.apcs = append<AuthnProviderConf>(dc.apcs, mklist<AuthnProviderConf>(AuthnProviderConf(arg, provider))); + return NULL; +} /** * HTTP server module declaration. */ const command_rec commands[] = { + AP_INIT_ITERATE("AuthOAuthProvider", (const char*(*)())confAuthnProvider, NULL, OR_AUTHCFG, "Auth providers for a directory or location"), AP_INIT_TAKE3("AddAuthOAuth2AppKey", (const char*(*)())confAppKey, NULL, RSRC_CONF, "OAuth 2.0 name app-id app-key"), AP_INIT_ITERATE("AddAuthOAuthMemcached", (const char*(*)())confMemcached, NULL, RSRC_CONF, "Memcached server host:port"), AP_INIT_FLAG("AuthOAuth", (const char*(*)())confEnabled, NULL, OR_AUTHCFG, "OAuth 2.0 authentication On | Off"), diff --git a/sca-cpp/trunk/modules/oauth/oauth-conf b/sca-cpp/trunk/modules/oauth/oauth-conf index 0b8f14b636..26e654036b 100755 --- a/sca-cpp/trunk/modules/oauth/oauth-conf +++ b/sca-cpp/trunk/modules/oauth/oauth-conf @@ -39,18 +39,23 @@ else sslsuffix="-ssl" fi +if [ "$2" = "" ]; then + providers="file" +else + providers="$2 file" +fi + # Configure HTTPD mod_tuscany_oauth module cat >>$root/conf/modules.conf <<EOF # Generated by: oauth-conf $* # Load support for OAuth authentication -LoadModule mod_tuscany_oauth1 $here/libmod_tuscany_oauth1$libsuffix LoadModule mod_tuscany_oauth2 $here/libmod_tuscany_oauth2$libsuffix EOF # Disallow public access to server resources cat >$root/conf/noauth$sslsuffix.conf <<EOF -# Generated by: oauth-auth-conf $* +# Generated by: oauth-conf $* # Disallow public access to server resources EOF @@ -62,9 +67,12 @@ cat >>$root/conf/locauth$sslsuffix.conf <<EOF <Location /> AuthType Open AuthName "$host" +AuthOAuthProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / Require valid-user AuthOAuth On -AuthOAuthLoginPage /login +AuthOAuthLoginPage /login/ AddAuthOAuth2ScopeAttr REALM realm AddAuthOAuth2ScopeAttr REMOTE_USER email AddAuthOAuth2ScopeAttr EMAIL email @@ -72,13 +80,7 @@ AddAuthOAuth2ScopeAttr NICKNAME name AddAuthOAuth2ScopeAttr FULLNAME name AddAuthOAuth2ScopeAttr FIRSTNAME first_name AddAuthOAuth2ScopeAttr LASTNAME last_name -AddAuthOAuth1ScopeAttr REALM realm -AddAuthOAuth1ScopeAttr REMOTE_USER email -AddAuthOAuth1ScopeAttr EMAIL email -AddAuthOAuth1ScopeAttr NICKNAME screen_name AddAuthOAuth2ScopeAttr FULLNAME name -AddAuthOAuth1ScopeAttr FIRSTNAME first-name -AddAuthOAuth1ScopeAttr LASTNAME last-name </Location> EOF diff --git a/sca-cpp/trunk/modules/oauth/oauth12-conf b/sca-cpp/trunk/modules/oauth/oauth12-conf new file mode 100755 index 0000000000..43c879faee --- /dev/null +++ b/sca-cpp/trunk/modules/oauth/oauth12-conf @@ -0,0 +1,115 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Generate an OAuth server conf +here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here` +mkdir -p $1 +root=`echo "import os; print os.path.realpath('$1')" | python` + +uname=`uname -s` +if [ $uname = "Darwin" ]; then + libsuffix=".dylib" +else + libsuffix=".so" +fi + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` + +sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"` +if [ "$sslconf" = "" ]; then + sslsuffix="" +else + sslsuffix="-ssl" +fi + +if [ "$2" = "" ]; then + providers="file" +else + providers="$2 file" +fi + +# Configure HTTPD mod_tuscany_oauth module +cat >>$root/conf/modules.conf <<EOF +# Generated by: oauth12-conf $* +# Load support for OAuth authentication +LoadModule mod_tuscany_oauth1 $here/libmod_tuscany_oauth1$libsuffix +LoadModule mod_tuscany_oauth2 $here/libmod_tuscany_oauth2$libsuffix + +EOF + +# Disallow public access to server resources +cat >$root/conf/noauth$sslsuffix.conf <<EOF +# Generated by: oauth12-conf $* +# Disallow public access to server resources + +EOF + +# Configure OAuth authentication +cat >>$root/conf/locauth$sslsuffix.conf <<EOF +# Generated by: oauth12-conf $* +# Enable OAuth authentication +<Location /> +AuthType Open +AuthName "$host" +AuthOAuthProvider socache $providers +AuthnCacheProvideFor $providers +AuthnCacheContext / +Require valid-user +AuthOAuth On +AuthOAuthLoginPage /login/ +AddAuthOAuth2ScopeAttr REALM realm +AddAuthOAuth2ScopeAttr REMOTE_USER email +AddAuthOAuth2ScopeAttr EMAIL email +AddAuthOAuth2ScopeAttr NICKNAME name +AddAuthOAuth2ScopeAttr FULLNAME name +AddAuthOAuth2ScopeAttr FIRSTNAME first_name +AddAuthOAuth2ScopeAttr LASTNAME last_name +AddAuthOAuth1ScopeAttr REALM realm +AddAuthOAuth1ScopeAttr REMOTE_USER email +AddAuthOAuth1ScopeAttr EMAIL email +AddAuthOAuth1ScopeAttr NICKNAME screen_name +AddAuthOAuth2ScopeAttr FULLNAME name +AddAuthOAuth1ScopeAttr FIRSTNAME first-name +AddAuthOAuth1ScopeAttr LASTNAME last-name +</Location> + +EOF + +cat >>$root/conf/auth.conf <<EOF +# Configure OAuth App keys +Include $root/cert/oauth-keys.conf + +EOF + +mkdir -p $root/cert +cat >$root/cert/oauth-keys.conf <<EOF +# Generated by: oauth12-conf $* +# OAuth App keys + +EOF + +if [ -d "$HOME/.oauth" ]; then + cat >>$root/conf/auth.conf <<EOF +# Configure OAuth App keys +Include $HOME/.oauth/*-key.conf + +EOF +fi + diff --git a/sca-cpp/trunk/modules/oauth/start-mixed-test b/sca-cpp/trunk/modules/oauth/start-mixed-test index e5a8837354..478f2cc777 100755 --- a/sca-cpp/trunk/modules/oauth/start-mixed-test +++ b/sca-cpp/trunk/modules/oauth/start-mixed-test @@ -38,7 +38,7 @@ rm -rf tmp ../openid/openid-memcached-conf tmp www.example.com 11212 ../openid/openid-memcached-conf tmp www.example.com 11213 -./oauth-conf tmp +./oauth12-conf tmp ./oauth-memcached-conf tmp www.example.com 11212 ./oauth-memcached-conf tmp www.example.com 11213 @@ -48,13 +48,16 @@ rm -rf tmp ./oauth2-appkey-conf tmp facebook.com app1234 secret6789 ./oauth2-appkey-conf tmp github.com app5678 secret8901 -../http/open-auth-conf tmp -../http/passwd-auth-conf tmp foo foo +../../modules/http/open-auth-conf tmp -# For this test to work you need to add your form, oauth and open id ids -# to the authorized user group +# For this test to work you need to add your oauth and openid user ids to +# the password file with a dummy 'password' password, and to the group +# of authorized users +../../modules/http/passwd-auth-conf tmp foo foo ../../modules/http/group-auth-conf tmp foo +../../modules/http/passwd-auth-conf tmp /openid/123456 password ../../modules/http/group-auth-conf tmp 123456 +../../modules/http/passwd-auth-conf tmp /oauth2/jane@example.com password ../../modules/http/group-auth-conf tmp jane@example.com ../../modules/server/server-conf tmp diff --git a/sca-cpp/trunk/modules/oauth/start-test b/sca-cpp/trunk/modules/oauth/start-test index 8fc41cdb9a..cadbed2bac 100755 --- a/sca-cpp/trunk/modules/oauth/start-test +++ b/sca-cpp/trunk/modules/oauth/start-test @@ -33,7 +33,7 @@ rm -rf tmp ../../modules/http/httpd-conf tmp www.example.com 8090 htdocs ../../modules/http/httpd-ssl-conf tmp 8453 -./oauth-conf tmp +./oauth12-conf tmp ./oauth-memcached-conf tmp www.example.com 11212 ./oauth-memcached-conf tmp www.example.com 11213 @@ -44,7 +44,9 @@ rm -rf tmp ./oauth2-appkey-conf tmp github.com app5678 secret8901 # For this test to work you need to add your oauth user id to the -# authorized user group +# password file with a dummy 'password' password, and to the group +# of authorized users +../../modules/http/passwd-auth-conf tmp /oauth2/jane@example.com password ../../modules/http/group-auth-conf tmp jane@example.com ../../modules/server/server-conf tmp diff --git a/sca-cpp/trunk/modules/openid/openid-conf b/sca-cpp/trunk/modules/openid/openid-conf index d07e4b7cad..7f986fc8d7 100755 --- a/sca-cpp/trunk/modules/openid/openid-conf +++ b/sca-cpp/trunk/modules/openid/openid-conf @@ -33,6 +33,12 @@ else sslsuffix="-ssl" fi +if [ "$2" = "" ]; then + providers="file" +else + providers="$2" +fi + # Configure HTTPD mod_auth_openid module cat >>$root/conf/modules.conf <<EOF # Generated by: openid-conf $* @@ -55,6 +61,9 @@ cat >>$root/conf/locauth$sslsuffix.conf <<EOF <Location /> AuthType Open AuthName "$host" +#AuthOpenIDProvider socache $providers +#AuthnCacheProvideFor $providers +#AuthnCacheContext / Require valid-user AuthOpenIDEnabled On AuthOpenIDCookiePath / diff --git a/sca-cpp/trunk/modules/openid/start-test b/sca-cpp/trunk/modules/openid/start-test index f0d27d16be..56ebbc3736 100755 --- a/sca-cpp/trunk/modules/openid/start-test +++ b/sca-cpp/trunk/modules/openid/start-test @@ -32,8 +32,10 @@ rm -rf tmp ./openid-memcached-conf tmp localhost 11213 ./openid-step2-conf tmp -# For this test to work you need to add your openid (or email address if -# available from your openid attributes to the the authorized user group +# For this test to work you need to add your openid user id to the +# password file with a dummy 'password' password, and to the group +# of authorized users +../../modules/http/passwd-auth-conf tmp /openid/jane@example.com password ../../modules/http/group-auth-conf tmp jane@example.com ../../modules/server/server-conf tmp diff --git a/sca-cpp/trunk/modules/server/mod-eval.hpp b/sca-cpp/trunk/modules/server/mod-eval.hpp index ac5bf55698..ee99baa039 100644 --- a/sca-cpp/trunk/modules/server/mod-eval.hpp +++ b/sca-cpp/trunk/modules/server/mod-eval.hpp @@ -43,6 +43,10 @@ #include "../http/http.hpp" #include "../http/httpd.hpp" +#include "apr_md5.h" +#include "ap_provider.h" +#include "mod_auth.h" + extern "C" { extern module AP_MODULE_DECLARE_DATA mod_tuscany_eval; } @@ -75,8 +79,10 @@ public: string domain; string contribPath; string composName; - string providerName; - value provider; + string contributorName; + value contributor; + string authenticatorName; + value authenticator; }; /** @@ -115,7 +121,7 @@ public: ServerConf() { } - ServerConf(apr_pool_t* p, const server_rec* s) : p(p), server(s) { + ServerConf(apr_pool_t* p, const server_rec* s) : p(p), server(s), timeout(0) { } const gc_pool p; @@ -123,6 +129,7 @@ public: lambda<value(const list<value>&)> lifecycle; ContribConf contribc; SSLConf sslc; + int timeout; VhostConf vhostc; Composite compos; }; @@ -145,7 +152,12 @@ public: }; /** - * Convert a result represented as a content + failure pair to a + * Authentication cache store function. + */ +static APR_OPTIONAL_FN_TYPE(ap_authn_cache_store) *authnCacheStore = NULL; + +/** + * Convert a result represented as a (content reason? code?) tuple to a * failable monad. */ const failable<value> failableResult(const list<value>& v) { @@ -177,9 +189,9 @@ public: /** * Make an HTTP proxy lambda to an absolute URI */ -const value mkhttpProxy(const string& uri, const gc_pool& p) { +const value mkhttpProxy(const string& uri, const int timeout, const gc_pool& p) { debug(uri, "modeval::mkhttpProxy::uri"); - return lambda<value(const list<value>&)>(http::proxy(uri, "", "", "", "", p)); + return lambda<value(const list<value>&)>(http::proxy(uri, "", "", "", "", timeout, p)); } /** @@ -187,7 +199,7 @@ const value mkhttpProxy(const string& uri, const gc_pool& p) { */ class implProxy { public: - implProxy(const value& name, const list<value>& impls, const SSLConf& sslc) : name(name), impls(impls), sslc(sslc) { + implProxy(const value& name, const list<value>& impls, const SSLConf& sslc, const int timeout) : name(name), impls(impls), sslc(sslc), timeout(timeout) { } const value callImpl(const value& cname, const list<value>& aparams) const { @@ -229,7 +241,7 @@ public: ostringstream appuri; appuri << httpd::scheme(currentRequest) << "://" << substr(uri, 6) << "." << http::topDomain(httpd::hostName(currentRequest)) << ":" << httpd::port(currentRequest) << "/"; debug(str(appuri), "modeval::implProxy::httpproxy::appuri"); - const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(str(appuri), sslc.ca, sslc.cert, sslc.key, httpd::cookie(currentRequest), p)); + const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(str(appuri), sslc.ca, sslc.cert, sslc.key, httpd::cookie(currentRequest), timeout, p)); return px(aparams); } @@ -237,13 +249,13 @@ public: // only if the target is in the same top level domain if (http::topDomain(http::hostName(uri, p)) == http::topDomain(httpd::hostName(currentRequest))) { debug(uri, "modeval::implProxy::httpproxy::samedomain"); - const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(uri, sslc.ca, sslc.cert, sslc.key, httpd::cookie(currentRequest), p)); + const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(uri, sslc.ca, sslc.cert, sslc.key, httpd::cookie(currentRequest), timeout, p)); return px(aparams); } // No SSL certificate or cookie on a cross domain call debug(uri, "modeval::implProxy::httpproxy::crossdomain"); - const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(uri, "", "", "", "", p)); + const lambda<value(const list<value>&)> px = lambda<value(const list<value>&)>(http::proxy(uri, "", "", "", "", timeout, p)); return px(aparams); } @@ -259,11 +271,12 @@ private: const value name; const list<value>& impls; const SSLConf& sslc; + const int timeout; }; -const value mkimplProxy(const value& name, const list<value>& impls, const SSLConf& sslc) { +const value mkimplProxy(const value& name, const list<value>& impls, const SSLConf& sslc, const int timeout) { debug(name, "modeval::implProxy::impl"); - return lambda<value(const list<value>&)>(implProxy(name, impls, sslc)); + return lambda<value(const list<value>&)>(implProxy(name, impls, sslc, timeout)); } /** @@ -303,7 +316,7 @@ const value mkunwiredProxy(const string& ref) { /** * Convert a list of component references to a list of proxy lambdas. */ -const value mkrefProxy(const value& ref, const list<value>& impls, const SSLConf& sslc, const gc_pool& p) { +const value mkrefProxy(const value& ref, const list<value>& impls, const SSLConf& sslc, const int timeout, const gc_pool& p) { const value target = scdl::target(ref); const bool wbyimpl = scdl::wiredByImpl(ref); debug(ref, "modeval::mkrefProxy::ref"); @@ -312,18 +325,18 @@ const value mkrefProxy(const value& ref, const list<value>& impls, const SSLConf // Use an HTTP proxy or an internal proxy to the component implementation if (wbyimpl) - return mkimplProxy(value(), impls, sslc); + return mkimplProxy(value(), impls, sslc, timeout); if (isNil(target)) return mkunwiredProxy(scdl::name(ref)); if (http::isAbsolute(target)) - return mkhttpProxy(target, p); - return mkimplProxy(car(pathValues(target)), impls, sslc); + return mkhttpProxy(target, timeout, p); + return mkimplProxy(car(pathValues(target)), impls, sslc, timeout); } -const list<value> refProxies(const list<value>& refs, const list<value>& impls, const SSLConf& sslc, const gc_pool& p) { +const list<value> refProxies(const list<value>& refs, const list<value>& impls, const SSLConf& sslc, const int timeout, const gc_pool& p) { if (isNil(refs)) return refs; - return cons(mkrefProxy(car(refs), impls, sslc, p), refProxies(cdr(refs), impls, sslc, p)); + return cons(mkrefProxy(car(refs), impls, sslc, timeout, p), refProxies(cdr(refs), impls, sslc, timeout, p)); } /** @@ -422,15 +435,29 @@ struct realmPropProxy { const char* env = apr_table_get(currentRequest->subprocess_env, "REALM"); if (env == NULL) return v; - const char* realm = strncmp(env, "www.", 4) == 0? env + 4 : env; - if (*realm == '\0') + const string realm = httpd::realm(string(env)); + if (length(realm) == 0) return v; - const value r = string(realm); + const value r = realm; debug(r, "modeval::realmPropProxy::value"); return r; } }; +struct timeoutPropProxy { + const value v; + timeoutPropProxy(const value& v) : v(atoi(c_str((string)v))) { + } + const value operator()(unused const list<value>& params) const { + if (currentRequest == NULL) + return v; + const ServerConf& sc = httpd::serverConf<ServerConf>(currentRequest, &mod_tuscany_eval); + const value r = sc.timeout; + debug(r, "modeval::timeoutPropProxy::value"); + return r; + } +}; + struct userPropProxy { const value v; userPropProxy(const value& v) : v(v) { @@ -461,6 +488,8 @@ const value mkpropProxy(const value& prop) { return lambda<value(const list<value>&)>(userPropProxy(v)); if (n == "realm") return lambda<value(const list<value>&)>(realmPropProxy(v)); + if (n == "timeout") + return lambda<value(const list<value>&)>(timeoutPropProxy(v)); if (n == "email") return lambda<value(const list<value>&)>(envPropProxy("EMAIL", v)); if (n == "nickname") @@ -499,7 +528,7 @@ struct implementationFailure { } }; -const value evalComponent(const string& contribPath, const value& comp, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const gc_pool& p) { +const value evalComponent(const string& contribPath, const value& comp, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const int timeout, const gc_pool& p) { extern const failable<lambda<value(const list<value>&)> > evalImplementation(const string& cpath, const value& impl, const list<value>& px, const lambda<value(const list<value>&)>& lifecycle); const value impl = scdl::implementation(comp); @@ -507,7 +536,7 @@ const value evalComponent(const string& contribPath, const value& comp, const li debug(impl, "modeval::evalComponent::impl"); // Convert component references to configured proxy lambdas - const list<value> rpx(refProxies(scdl::references(comp), impls, sslc, p)); + const list<value> rpx(refProxies(scdl::references(comp), impls, sslc, timeout, p)); // Convert component properties to configured proxy lambdas const list<value> ppx(propProxies(scdl::properties(comp))); @@ -522,12 +551,12 @@ const value evalComponent(const string& contribPath, const value& comp, const li /** * Return a list of component-name + configured-implementation pairs. */ -const list<value> componentToImplementationAssoc(const list<value>& c, const string& contribPath, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const gc_pool& p) { +const list<value> componentToImplementationAssoc(const list<value>& c, const string& contribPath, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const int timeout, const gc_pool& p) { if (isNil(c)) return c; return cons<value>(mklist<value>(scdl::name(car(c)), - evalComponent(contribPath, car(c), impls, lifecycle, sslc, p)), - componentToImplementationAssoc(cdr(c), contribPath, impls, lifecycle, sslc, p)); + evalComponent(contribPath, car(c), impls, lifecycle, sslc, timeout, p)), + componentToImplementationAssoc(cdr(c), contribPath, impls, lifecycle, sslc, timeout, p)); } /** @@ -541,16 +570,17 @@ const failable<list<value> > readComponents(const string& path) { } /** - * Get the components returned by a provider. + * Get the components returned by a contributor. */ -const failable<list<value> > getComponents(const lambda<value(const list<value>&)>& provider, const string& name) { - const failable<value> val = failableResult(provider(cons<value>("get", mklist<value>(mklist<value>(name))))); +const failable<list<value> > getComponents(const lambda<value(const list<value>&)>& contributor, const string& name) { + const failable<value> val = failableResult(contributor(cons<value>("get", mklist<value>(mklist<value>(name))))); if (!hasContent(val)) return mkfailure<list<value> >(val); - const list<value> c = assoc<value>(value("content"), (list<list<value> >)cdr<value>(content(val))); + const list<value> c = assoc<value>(value("composite"), assoc<value>(value("content"), (list<list<value> >)cdr<value>(car<value>(content(val))))); + debug(c, "modeval::getComponents::comp"); if (isNil(c)) return mkfailure<list<value> >(string("Could not get composite: ") + name); - const failable<list<string> > x = writeXML(car<value>(valuesToElements(mklist<value>(mklist<value>(cadr(c)))))); + const failable<list<string> > x = writeXML(car<value>(valuesToElements(mklist<value>(mklist<value>(c))))); if (!hasContent(x)) return mkfailure<list<value> >(x); return scdl::components(readXML(content(x))); @@ -631,16 +661,16 @@ const list<value> uriToComponentAssoc(const list<value>& c) { /** * Configure the components declared in the deployed composite. */ -const failable<Composite> confComponents(const string& contribPath, const string& composName, const value& provider, const string& vhost, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const gc_pool& p) { +const failable<Composite> confComponents(const string& contribPath, const string& composName, const value& contributor, const string& vhost, const list<value>& impls, const lambda<value(const list<value>&)> lifecycle, const SSLConf& sslc, const int timeout, const gc_pool& p) { debug(contribPath, "modeval::confComponents::contribPath"); debug(composName, "modeval::confComponents::composName"); - debug(provider, "modeval::confComponents::provider"); + debug(contributor, "modeval::confComponents::contributor"); debug(vhost, "modeval::confComponents::vhost"); debug(impls, "modeval::confComponents::impls"); - const failable<list<value> > fcomps = isNil(provider)? + const failable<list<value> > fcomps = isNil(contributor)? readComponents(scdl::resourcePath(length(vhost) != 0? contribPath + vhost + "/" : contribPath, composName)) : - getComponents(provider, vhost); + getComponents(contributor, vhost); if (!hasContent(fcomps)) return mkfailure<Composite>(fcomps); @@ -654,8 +684,8 @@ const failable<Composite> confComponents(const string& contribPath, const string debug(flatten(svcs), "modeval::confComponents::svcs"); const list<value> cimpls = mkbtree(sort(componentToImplementationAssoc(comps, - isNil(provider)? length(vhost) != 0? contribPath + vhost + "/" : contribPath : contribPath, - impls, lifecycle, sslc, p))); + isNil(contributor)? length(vhost) != 0? contribPath + vhost + "/" : contribPath : contribPath, + impls, lifecycle, sslc, timeout, p))); debug(flatten(cimpls), "modeval::confComponents::impls"); return Composite(refs, svcs, cimpls); @@ -720,6 +750,10 @@ const failable<int> get(const list<value>& rpath, request_rec* r, const lambda<v const value c = content(val); debug(c, "modeval::get::content"); + // Return a nil value as a not found status + if (!isList(c) && isNil(c)) + return HTTP_NOT_FOUND; + // Check if the client requested a specific format const list<value> fmt = assoc<value>("format", args); @@ -735,6 +769,7 @@ const failable<int> get(const list<value>& rpath, request_rec* r, const lambda<v debug(lc, "modeval::get::symbol"); return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r); } + const list<value> lc = mklist<value>(mklist<value>("value", c)); debug(lc, "modeval::get::value"); return httpd::writeResult(json::writeJSON(valuesToElements(lc), cx), "application/json; charset=utf-8", r); @@ -1041,9 +1076,9 @@ const int translateRequest(request_rec* r, const list<value>& rpath, const list< // Attempt to map a request targeting the main host to an actual file if (isNil(vpath)) { - const failable<request_rec*, int> fnr = httpd::internalSubRequest(r->uri, r); + const failable<request_rec*> fnr = httpd::internalSubRequest(r->uri, r); if (!hasContent(fnr)) - return HTTP_INTERNAL_SERVER_ERROR; + return rcode(fnr); request_rec* nr = content(fnr); nr->uri = r->filename; const int tr = ap_core_translate(nr); @@ -1082,7 +1117,6 @@ int translate(request_rec *r) { if(r->method_number != M_GET && r->method_number != M_POST && r->method_number != M_PUT && r->method_number != M_DELETE) return DECLINED; - // Create a scoped memory pool gc_scoped_pool pool(r->pool); debug_httpdRequest(r, "modeval::translate::input"); @@ -1103,9 +1137,9 @@ int translate(request_rec *r) { // If the request is targeting a virtual host, configure the components // in that virtual host - if (length(sc.vhostc.domain) != 0 && (length(sc.vhostc.contribPath) != 0 || !isNil(sc.vhostc.provider)) && httpd::isVhostRequest(sc.server, sc.vhostc.domain, r)) { + if (length(sc.vhostc.domain) != 0 && (length(sc.vhostc.contribPath) != 0 || !isNil(sc.vhostc.contributor)) && httpd::isVhostRequest(sc.server, sc.vhostc.domain, r)) { const string vname = http::subDomain(httpd::hostName(r)); - const failable<Composite> fvcompos = confComponents(sc.vhostc.contribPath, sc.vhostc.composName, sc.vhostc.provider, vname, reqc.impls, sc.lifecycle, sc.sslc, sc.p); + const failable<Composite> fvcompos = confComponents(sc.vhostc.contribPath, sc.vhostc.composName, sc.vhostc.contributor, vname, reqc.impls, sc.lifecycle, sc.sslc, sc.timeout, sc.p); if (!hasContent(fvcompos)) return DECLINED; const Composite vcompos = content(fvcompos); @@ -1124,9 +1158,9 @@ int translate(request_rec *r) { return rc; // Attempt to map the first segment of the request path to a virtual host - if (length(prefix) != 0 && (length(sc.vhostc.contribPath) != 0 || !isNil(sc.vhostc.provider))) { + if (length(prefix) != 0 && (length(sc.vhostc.contribPath) != 0 || !isNil(sc.vhostc.contributor))) { const string vname = prefix; - const failable<Composite> fvcompos = confComponents(sc.vhostc.contribPath, sc.vhostc.composName, sc.vhostc.provider, vname, reqc.impls, sc.lifecycle, sc.sslc, sc.p); + const failable<Composite> fvcompos = confComponents(sc.vhostc.contribPath, sc.vhostc.composName, sc.vhostc.contributor, vname, reqc.impls, sc.lifecycle, sc.sslc, sc.timeout, sc.p); if (!hasContent(fvcompos)) return DECLINED; const Composite vcompos = content(fvcompos); @@ -1150,9 +1184,9 @@ const int handleRequest(const list<value>& rpath, request_rec *r, const list<val mkfailure<int>(string("Couldn't find component implementation: ") + cadr(rpath)); return HTTP_NOT_FOUND; } + const lambda<value(const list<value>&)> l(cadr<value>(impl)); // Handle HTTP method - const lambda<value(const list<value>&)> l(cadr<value>(impl)); if (r->header_only) return OK; if(r->method_number == M_GET) @@ -1182,7 +1216,7 @@ int handler(request_rec *r) { if(strcmp(r->handler, "mod_tuscany_eval")) return DECLINED; - // Create a scoped memory pool and a scope for the current request + // Create a scope for the current request gc_scoped_pool pool(r->pool); ScopedRequest sr(r); @@ -1228,6 +1262,61 @@ int handler(request_rec *r) { } /** + * Call an authenticator component to check a user's password. + */ +authn_status checkPassword(request_rec* r, const char* u, const char* p) { + gc_scoped_pool pool(r->pool); + + // Prevent FakeBasicAuth spoofing + const string user = u; + const string password = p; + debug(user, "modeval::checkPassword::user"); + if (substr(user, 0, 1) != "/" && find(user, "/") != length(user) && password == "password") { + mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED); + return AUTH_DENIED; + } + + // Get the server configuration + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_eval); + if (isNil(sc.vhostc.authenticator)) { + mkfailure<int>("SCA authenticator not configured"); + return AUTH_GENERAL_ERROR; + } + + // Retrieve the user's password hash + const list<value> uid = pathValues(user); + const failable<value> val = failableResult(sc.vhostc.authenticator(cons<value>("get", mklist<value>(uid)))); + if (!hasContent(val)) { + mkfailure<int>(string("SCA authentication check user failed, user not found: ") + user); + return AUTH_USER_NOT_FOUND; + } + const value hval = content(val); + const list<value> hcontent = isList(hval) && !isNil(hval) && isList(car<value>(hval)) && !isNil(car<value>(hval))? assoc<value>(value("content"), cdr<value>(car<value>(hval))) : list<value>(); + const list<value> hassoc = isNil(hcontent)? list<value>() : assoc<value>(value("hash"), cdr<value>(hcontent)); + if (isNil(hassoc)) { + mkfailure<int>(string("SCA authentication check user failed, hash not found: ") + user); + return AUTH_USER_NOT_FOUND; + } + const string hash = cadr<value>(hassoc); + if (length(hash) == 0) { + mkfailure<int>(string("SCA authentication check user failed: ") + user); + return AUTH_USER_NOT_FOUND; + } + + // Cache the hash in the auth cache provider, if available + if (authnCacheStore != NULL) + authnCacheStore(r, "component", u, NULL, c_str(hash)); + + // Validate the presented password against the hash + const apr_status_t rv = apr_password_validate(p, c_str(hash)); + if (rv != APR_SUCCESS) { + mkfailure<int>(string("SCA authentication user password check failed: ") + user); + return AUTH_DENIED; + } + return AUTH_GRANTED; +} + +/** * Cleanup callback, called when the server is stopped or restarted. */ apr_status_t serverCleanup(void* v) { @@ -1262,6 +1351,7 @@ const int postConfigMerge(const ServerConf& mainsc, server_rec* s) { if (sc.sslc.ca == "") sc.sslc.ca = mainsc.sslc.ca; if (sc.sslc.cert == "") sc.sslc.cert = mainsc.sslc.cert; if (sc.sslc.key == "") sc.sslc.key = mainsc.sslc.key; + sc.timeout = mainsc.timeout; sc.compos = mainsc.compos; return postConfigMerge(mainsc, s->next); } @@ -1311,7 +1401,7 @@ int postConfig(apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, } // Configure the deployed components - const failable<Composite> compos = confComponents(sc.contribc.contribPath, sc.contribc.composName, value(), "", sc.compos.impls, sc.lifecycle, sc.sslc, sc.p); + const failable<Composite> compos = confComponents(sc.contribc.contribPath, sc.contribc.composName, value(), "", sc.compos.impls, sc.lifecycle, sc.sslc, sc.timeout, sc.p); if (!hasContent(compos)) { cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; return -1; @@ -1326,28 +1416,49 @@ int postConfig(apr_pool_t *p, unused apr_pool_t *plog, unused apr_pool_t *ptemp, } /** + * Exit after a failure. + */ +void failureExitChild() { + cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; + exit(APEXIT_CHILDFATAL); +} + +/** * Child process initialization. */ void childInit(apr_pool_t* p, server_rec* s) { gc_scoped_pool pool(p); + ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_eval); - if(psc == NULL) { - cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; - exit(APEXIT_CHILDFATAL); - } + if(psc == NULL) + failureExitChild(); ServerConf& sc = *psc; // Start the components in the child process const failable<list<value> > fsimpls = startComponents(sc.compos.impls); - if (!hasContent(fsimpls)) { - cfailure << "[Tuscany] Due to one or more errors mod_tuscany_eval loading failed. Causing apache to stop loading." << endl; - exit(APEXIT_CHILDFATAL); - } + if (!hasContent(fsimpls)) + failureExitChild(); sc.compos.impls = content(fsimpls); - // Create a proxy for the vhost provider if needed - if (length(sc.vhostc.providerName) != 0) - sc.vhostc.provider = mkimplProxy(sc.vhostc.providerName, sc.compos.impls, sc.sslc); + // Get the vhost contributor component implementation lambda + if (length(sc.vhostc.contributorName) != 0) { + const list<value> impl(assoctree<value>(sc.vhostc.contributorName, sc.compos.impls)); + if (isNil(impl)) { + mkfailure<int>(string("Couldn't find contributor component implementation: ") + sc.vhostc.contributorName); + failureExitChild(); + } + sc.vhostc.contributor = cadr<value>(impl); + } + + // Get the vhost authenticator component implementation lambda + if (length(sc.vhostc.authenticatorName) != 0) { + const list<value> impl(assoctree<value>(sc.vhostc.authenticatorName, sc.compos.impls)); + if (isNil(impl)) { + mkfailure<int>(string("Couldn't find authenticator component implementation: ") + sc.vhostc.authenticatorName); + failureExitChild(); + } + sc.vhostc.authenticator = cadr<value>(impl); + } // Merge the updated configuration into the virtual hosts postConfigMerge(sc, s->next); @@ -1383,10 +1494,10 @@ const char* confVirtualContribution(cmd_parms *cmd, unused void *c, const char * sc.vhostc.contribPath = arg; return NULL; } -const char* confVirtualprovider(cmd_parms *cmd, unused void *c, const char *arg) { +const char* confVirtualContributor(cmd_parms *cmd, unused void *c, const char *arg) { gc_scoped_pool pool(cmd->pool); ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); - sc.vhostc.providerName = arg; + sc.vhostc.contributorName = arg; return NULL; } const char* confVirtualComposite(cmd_parms *cmd, unused void *c, const char *arg) { @@ -1395,6 +1506,12 @@ const char* confVirtualComposite(cmd_parms *cmd, unused void *c, const char *arg sc.vhostc.composName = arg; return NULL; } +const char* confAuthenticator(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.vhostc.authenticatorName = arg; + return NULL; +} const char* confCAFile(cmd_parms *cmd, unused void *c, const char *arg) { gc_scoped_pool pool(cmd->pool); ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); @@ -1413,6 +1530,12 @@ const char* confCertKeyFile(cmd_parms *cmd, unused void *c, const char *arg) { sc.sslc.key = arg; return NULL; } +const char* confTimeout(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_eval); + sc.timeout = atoi(arg); + return NULL; +} const char* confEnv(unused cmd_parms *cmd, unused void *c, const char *name, const char *value) { gc_scoped_pool pool(cmd->pool); setenv(name, value != NULL? value : "", 1); @@ -1427,20 +1550,33 @@ const command_rec commands[] = { AP_INIT_TAKE1("SCAComposite", (const char*(*)())confComposite, NULL, RSRC_CONF, "SCA composite location"), AP_INIT_TAKE1("SCAVirtualDomain", (const char*(*)())confVirtualDomain, NULL, RSRC_CONF, "SCA virtual host domain"), AP_INIT_TAKE1("SCAVirtualContribution", (const char*(*)())confVirtualContribution, NULL, RSRC_CONF, "SCA virtual host contribution path"), - AP_INIT_TAKE1("SCAVirtualContributor", (const char*(*)())confVirtualprovider, NULL, RSRC_CONF, "SCA virtual host provider component"), + AP_INIT_TAKE1("SCAVirtualContributor", (const char*(*)())confVirtualContributor, NULL, RSRC_CONF, "SCA virtual host contributor component"), AP_INIT_TAKE1("SCAVirtualComposite", (const char*(*)())confVirtualComposite, NULL, RSRC_CONF, "SCA virtual composite location"), + AP_INIT_TAKE1("SCAAuthenticator", (const char*(*)())confAuthenticator, NULL, RSRC_CONF, "SCA authenticator component"), AP_INIT_TAKE12("SCASetEnv", (const char*(*)())confEnv, NULL, OR_FILEINFO, "Environment variable name and optional value"), AP_INIT_TAKE1("SCAWiringSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "SCA wiring SSL CA certificate file"), AP_INIT_TAKE1("SCAWiringSSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "SCA wiring SSL certificate file"), AP_INIT_TAKE1("SCAWiringSSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "SCA wiring SSL certificate key file"), + AP_INIT_TAKE1("SCAWiringTimeout", (const char*(*)())confTimeout, NULL, RSRC_CONF, "SCA wiring timeout"), {NULL, NULL, NULL, 0, NO_ARGS, NULL} }; +const authn_provider AuthnProvider = { + &checkPassword, + NULL +}; + +void retrieveAuthnCacheStore() { + authnCacheStore = APR_RETRIEVE_OPTIONAL_FN(ap_authn_cache_store); +} + void registerHooks(unused apr_pool_t *p) { ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_register_auth_provider(p, AUTHN_PROVIDER_GROUP, "component", AUTHN_PROVIDER_VERSION, &AuthnProvider, AP_AUTH_INTERNAL_PER_CONF); + ap_hook_optional_fn_retrieve(retrieveAuthnCacheStore, NULL, NULL, APR_HOOK_MIDDLE); } } |