Port to HTTPD 2.3.8. Add an auth module to make OpenID, OAuth 1/2 and HTTPD 2.3 Form auth modules play nice together.

git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1034693 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
jsdelfino 2010-11-13 07:53:10 +00:00
commit 3ac22b097d
57 changed files with 889 additions and 278 deletions

View file

@ -20,7 +20,7 @@ INCLUDES = -I${HTTPD_INCLUDE}
incl_HEADERS = *.hpp
incldir = $(prefix)/include/modules/http
dist_mod_SCRIPTS = httpd-conf httpd-addr httpd-start httpd-stop httpd-restart ssl-ca-conf ssl-cert-conf ssl-cert-find httpd-ssl-conf httpd-auth-conf proxy-conf proxy-ssl-conf proxy-member-conf proxy-ssl-member-conf vhost-conf vhost-ssl-conf tunnel-ssl-conf
dist_mod_SCRIPTS = httpd-conf httpd-addr httpd-start httpd-stop httpd-restart ssl-ca-conf ssl-cert-conf ssl-cert-find httpd-ssl-conf basic-auth-conf form-auth-conf open-auth-conf proxy-conf proxy-ssl-conf proxy-member-conf proxy-ssl-member-conf vhost-conf vhost-ssl-conf tunnel-ssl-conf
moddir=$(prefix)/modules/http
curl_test_SOURCES = curl-test.cpp
@ -32,18 +32,23 @@ curl_get_LDFLAGS = -lxml2 -lcurl -lmozjs
curl_connect_SOURCES = curl-connect.cpp
curl_connect_LDFLAGS = -lxml2 -lcurl -lmozjs
mod_LTLIBRARIES = libmod_tuscany_ssltunnel.la
noinst_DATA = libmod_tuscany_ssltunnel.so
mod_LTLIBRARIES = libmod_tuscany_ssltunnel.la libmod_tuscany_openauth.la
noinst_DATA = libmod_tuscany_ssltunnel.so libmod_tuscany_openauth.so
libmod_tuscany_ssltunnel_la_SOURCES = mod-ssltunnel.cpp
libmod_tuscany_ssltunnel_la_LDFLAGS = -lxml2 -lcurl -lmozjs
libmod_tuscany_ssltunnel.so:
ln -s .libs/libmod_tuscany_ssltunnel.so
libmod_tuscany_openauth_la_SOURCES = mod-openauth.cpp
libmod_tuscany_openauth_la_LDFLAGS = -lxml2 -lcurl -lmozjs
libmod_tuscany_openauth.so:
ln -s .libs/libmod_tuscany_openauth.so
mod_DATA = httpd.prefix httpd-apachectl.prefix httpd-modules.prefix curl.prefix
nobase_dist_mod_DATA = conf/*
EXTRA_DIST = htdocs/index.html
EXTRA_DIST = htdocs/index.html htdocs/login/index.html htdocs/logout/index.html
httpd.prefix: $(top_builddir)/config.status
echo ${HTTPD_PREFIX} >httpd.prefix

View file

@ -17,7 +17,7 @@
# specific language governing permissions and limitations
# under the License.
# Generate a minimal HTTPD SSL configuration
# Generate a minimal HTTPD basic authentication configuration
here=`readlink -f $0`; here=`dirname $here`
mkdir -p $1
root=`readlink -f $1`
@ -28,13 +28,14 @@ host=`echo $conf | awk '{ print $6 }'`
httpd_prefix=`cat $here/httpd.prefix`
# Generate basic authentication configuration
cat >>$root/conf/vhost-ssl.conf <<EOF
# Generated by: httpd-auth-conf $*
cat >>$root/conf/auth.conf <<EOF
# Generated by: basic-auth-conf $*
# Require clients to present a userid + password for HTTP
# basic authentication
<Location />
AuthType Basic
AuthName "$host"
AuthBasicProvider file
AuthUserFile "$root/conf/httpd.passwd"
Require valid-user
</Location>
@ -42,7 +43,8 @@ Require valid-user
EOF
# Create test users
$httpd_prefix/bin/htpasswd -bc $root/conf/httpd.passwd test test 2>/dev/null
touch $root/conf/httpd.passwd
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd test test 2>/dev/null
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd admin admin 2>/dev/null
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd foo foo 2>/dev/null
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd bar bar 2>/dev/null

View file

@ -0,0 +1,64 @@
#!/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 form authentication configuration
here=`readlink -f $0`; here=`dirname $here`
mkdir -p $1
root=`readlink -f $1`
conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
host=`echo $conf | awk '{ print $6 }'`
httpd_prefix=`cat $here/httpd.prefix`
# Generate form authentication configuration
cat >>$root/conf/auth.conf <<EOF
# Generated by: form-auth-conf $*
# Require clients to present a userid + password through form-based
# authentication
<Location />
AuthType Form
AuthName "$host"
AuthFormProvider file
AuthUserFile "$root/conf/httpd.passwd"
AuthFormLoginRequiredLocation /login
AuthFormLogoutLocation /
Session On
SessionCookieName TuscanyFormAuth path=/;secure=TRUE
#SessionCryptoPassphrase secret
Require valid-user
</Location>
<Location /login/dologin>
SetHandler form-login-handler
</Location>
<Location /logout/dologout>
SetHandler form-logout-handler
</Location>
EOF
# Create test users
touch $root/conf/httpd.passwd
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd test test 2>/dev/null
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd admin admin 2>/dev/null
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd foo foo 2>/dev/null
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd bar bar 2>/dev/null

View file

@ -0,0 +1,39 @@
<!--
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.
-->
<html><body><h1>Sign in</h1>
<script type="text/javascript">
function submitFormSignin() {
document.formSignin.httpd_location.value = '/';
document.formSignin.submit();
}
</script>
<form name="formSignin" method="POST" action="/login/dologin">
<table border="0">
<tr><td>Username:</td><td><input type="text" name="httpd_username" value=""/></td></tr>
<tr><td>Password:</td><td><input type="password" name="httpd_password" value=""/></td></tr>
<tr><td><input type="button" onclick="submitFormSignin()" value="Sign in"/></td><td></td></tr>
</table>
<input type="hidden" name="httpd_location" value="/"/>
</form>
</body>
</html>

View file

@ -0,0 +1,33 @@
<!--
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.
-->
<html><body>
<h1>Sign out</h1>
<form name="signout" action="/login" method="GET">
<script type="text/javascript">
function submitSignout() {
document.cookie = 'TuscanyFormAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';path=/;secure=TRUE';
document.signout.submit();
return true;
}
</script>
<input type="button" onclick="submitSignout()" value="Sign out"/>
</form>
</body></html>

View file

@ -159,19 +159,19 @@ const failable<CURL*> setup(const string& url, const CURLSession& cs) {
// Setup SSL options
if (cs.ca != "") {
debug(cs.ca, "http::apply::ca");
debug(cs.ca, "http::setup::ca");
curl_easy_setopt(ch, CURLOPT_CAINFO, c_str(cs.ca));
curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, true);
curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, 2);
} else
curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, false);
if (cs.cert != "") {
debug(cs.cert, "http::apply::cert");
debug(cs.cert, "http::setup::cert");
curl_easy_setopt(ch, CURLOPT_SSLCERT, c_str(cs.cert));
curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, "PEM");
}
if (cs.key != "") {
debug(cs.key, "http::apply::key");
debug(cs.key, "http::setup::key");
curl_easy_setopt(ch, CURLOPT_SSLKEY, c_str(cs.key));
curl_easy_setopt(ch, CURLOPT_SSLKEYTYPE, "PEM");
}
@ -238,6 +238,8 @@ curl_slist* headers(curl_slist* cl, const list<string>& h) {
}
template<typename R> const failable<list<R> > apply(const list<list<string> >& hdr, const lambda<R(const string&, const R)>& reduce, const R& initial, const string& url, const string& verb, const CURLSession& cs) {
debug(url, "http::apply::url");
debug(verb, "http::apply::verb");
// Setup the CURL session
const failable<CURL*> fch = setup(url, cs);
@ -523,6 +525,7 @@ apr_pollfd_t* pollfd(apr_socket_t* s, const int e, const gc_pool& p) {
* Connect to a URL.
*/
const failable<bool> connect(const string& url, CURLSession& cs) {
debug(url, "http::connect::url");
// Setup the CURL session
const failable<CURL*> fch = setup(url, cs);

View file

@ -52,19 +52,25 @@ PidFile $root/logs/httpd.pid
# after mod_rewrite's hooks)
LoadModule alias_module ${modules_prefix}/modules/mod_alias.so
LoadModule authn_file_module ${modules_prefix}/modules/mod_authn_file.so
LoadModule authn_default_module ${modules_prefix}/modules/mod_authn_default.so
LoadModule authn_core_module ${modules_prefix}/modules/mod_authn_core.so
LoadModule authz_host_module ${modules_prefix}/modules/mod_authz_host.so
LoadModule authz_groupfile_module ${modules_prefix}/modules/mod_authz_groupfile.so
LoadModule authz_user_module ${modules_prefix}/modules/mod_authz_user.so
LoadModule authz_default_module ${modules_prefix}/modules/mod_authz_default.so
LoadModule authz_core_module ${modules_prefix}/modules/mod_authz_core.so
LoadModule auth_basic_module ${modules_prefix}/modules/mod_auth_basic.so
LoadModule auth_digest_module ${modules_prefix}/modules/mod_auth_digest.so
LoadModule auth_form_module ${modules_prefix}/modules/mod_auth_form.so
LoadModule request_module ${modules_prefix}/modules/mod_request.so
LoadModule deflate_module ${modules_prefix}/modules/mod_deflate.so
LoadModule filter_module ${modules_prefix}/modules/mod_filter.so
LoadModule proxy_module ${modules_prefix}/modules/mod_proxy.so
LoadModule proxy_connect_module ${modules_prefix}/modules/mod_proxy_connect.so
LoadModule proxy_http_module ${modules_prefix}/modules/mod_proxy_http.so
LoadModule proxy_balancer_module ${modules_prefix}/modules/mod_proxy_balancer.so
LoadModule lbmethod_byrequests_module ${modules_prefix}/modules/mod_lbmethod_byrequests.so
LoadModule headers_module ${modules_prefix}/modules/mod_headers.so
LoadModule ssl_module ${modules_prefix}/modules/mod_ssl.so
LoadModule socache_shmcb_module ${modules_prefix}/modules/mod_socache_shmcb.so
LoadModule rewrite_module ${modules_prefix}/modules/mod_rewrite.so
LoadModule mime_module ${modules_prefix}/modules/mod_mime.so
LoadModule status_module ${modules_prefix}/modules/mod_status.so
@ -79,8 +85,16 @@ LoadModule logio_module ${modules_prefix}/modules/mod_logio.so
LoadModule usertrack_module ${modules_prefix}/modules/mod_usertrack.so
LoadModule vhost_alias_module ${modules_prefix}/modules/mod_vhost_alias.so
LoadModule cgi_module ${modules_prefix}/modules/mod_cgi.so
LoadModule unixd_module ${modules_prefix}/modules/mod_unixd.so
LoadModule session_module ${modules_prefix}/modules/mod_session.so
#LoadModule session_crypto_module ${modules_prefix}/modules/mod_session_crypto.so
LoadModule session_cookie_module ${modules_prefix}/modules/mod_session_cookie.so
LoadModule slotmem_shm_module ${modules_prefix}/modules/mod_slotmem_shm.so
LoadModule ratelimit_module ${modules_prefix}/modules/mod_ratelimit.so
LoadModule reqtimeout_module ${modules_prefix}/modules/mod_reqtimeout.so
LoadModule mod_tuscany_ssltunnel $here/libmod_tuscany_ssltunnel.so
LoadModule mod_tuscany_openauth $here/libmod_tuscany_openauth.so
# Basic security precautions
User $user
@ -103,7 +117,6 @@ CookieTracking on
CookieName TuscanyVisitorId
# Configure Mime types
DefaultType text/plain
TypesConfig $here/conf/mime.types
# Set default document root
@ -114,26 +127,44 @@ DirectoryIndex index.html
<Directory />
Options None
AllowOverride None
Order deny,allow
Deny from all
</Directory>
<FilesMatch "^\.ht">
Order deny,allow
Deny from all
Satisfy Any
</FilesMatch>
# Allow access to document root
<Directory "$htdocs">
Options FollowSymLinks
Allow from all
Require all denied
</Directory>
# Allow access to root location
<Location />
Options FollowSymLinks
Order deny,allow
Allow from all
# Configure authentication
Include conf/auth.conf
# Allow access to public locations
<Location /login>
AuthType None
Require all granted
</Location>
<Location /logout>
AuthType None
Require all granted
</Location>
<Location /public>
AuthType None
Require all granted
</Location>
<Location /openid>
AuthType None
Require all granted
</Location>
<Location /ui>
AuthType None
Require all granted
</Location>
<Location /wiring>
AuthType None
Require all granted
</Location>
<Location /.well-known/host-meta>
AuthType None
Require all granted
</Location>
<Location /favicon.ico>
AuthType None
Require all granted
</Location>
# Listen on HTTP port
@ -152,6 +183,25 @@ Include conf/svhost.conf
EOF
# Generate auth configuration
cat >$root/conf/auth.conf <<EOF
# Generated by: httpd-conf $*
# Authentication configuration
# Allow access to document root
<Directory "$htdocs">
Options FollowSymLinks
Require all granted
</Directory>
# Allow access to root location
<Location />
Options FollowSymLinks
Require all granted
</Location>
EOF
# Generate vhost configuration
cat >$root/conf/vhost.conf <<EOF
# Generated by: httpd-conf $*

View file

@ -51,7 +51,7 @@ AddType application/x-pkcs7-crl .crl
SSLPassPhraseDialog builtin
SSLSessionCache "shmcb:$root/logs/ssl_scache(512000)"
SSLSessionCacheTimeout 300
SSLMutex "file:$root/logs/ssl_mutex"
Mutex "file:$root/logs" ssl-cache
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
@ -68,7 +68,6 @@ Include conf/svhost-ssl.conf
<Location /server-status>
SetHandler server-status
HostnameLookups on
Allow from all
Require user admin
</Location>
@ -114,10 +113,10 @@ UseCanonicalName Off
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
BrowserMatch ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
SSLOptions -StrictRequire +OptRenegotiate
SSLOptions +StrictRequire +OptRenegotiate +FakeBasicAuth
# Verify client certificates
SSLVerifyClient none
SSLVerifyClient optional
SSLVerifyDepth 1
# Enable SSL proxy engine
@ -143,19 +142,13 @@ cat >>$root/conf/vhost-ssl.conf <<EOF
<Location />
# Require clients to use SSL and authenticate
SSLRequireSSL
# Also accept other forms of authentication (e.g. HTTP basic
# authentication, or OpenID authentication)
Satisfy Any
SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128
EOF
proxyconf=`cat $root/conf/vhost.conf | grep "# Generated by: proxy-conf"`
if [ "$proxyconf" != "" ]; then
cat >>$root/conf/vhost-ssl.conf <<EOF
# In an proxy, only require a 128+ cipher key
SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128
# Forward received SSL client certificate info in proxied requests
RewriteEngine on
RewriteRule .* - [E=SSL_PROTOCOL:%{SSL:SSL_PROTOCOL}]
@ -184,18 +177,6 @@ RequestHeader set X-Forwarded-SSL-Client-DN-OU %{SSL_S_DN_OU}e env=SSL_S_DN_OU
EOF
else
cat >>$root/conf/vhost-ssl.conf <<EOF
# In a server, require a 128+ cipher key and one of the following
# - another server's certificate issued by our certificate authority
# - a proxy certificate + forwarded info on the client request certificate,
# both signed by our certificate authority
# - OpenID authentication (set by mod_auth_openid in the auth_type)
# - another valid form of authentication as per the Satisfy directive
SSLRequire %{SSL_CIPHER_USEKEYSIZE} >= 128 and ( \
( %{SSL_CLIENT_I_DN_O} == "$org" and %{SSL_CLIENT_S_DN_OU} == "server" ) or \
( %{SSL_CLIENT_I_DN_O} == "$org" and %{SSL_CLIENT_S_DN_OU} == "tunnel" ) or \
( %{SSL_CLIENT_I_DN_O} == "$org" and %{SSL_CLIENT_S_DN_OU} == "proxy" and \
%{HTTP:X-Forwarded-SSL-Issuer-DN-O} == "$org" and %{HTTP:X-Forwarded-SSL-Client-DN-OU} == "server" ) or \
%{REQUEST_URI} =~ m/^.(login|logout|openid|public|ui).*$/ )
# Record received SSL client certificate info in environment vars
RewriteEngine on
@ -270,3 +251,8 @@ SSLProxyMachineCertificateFile "$root/cert/$proxycert.pem"
EOF
# Configure user for HTTP fake basic auth
cat >$root/conf/httpd.passwd <<EOF
/C=US/ST=CA/L=San Francisco/O=$host/OU=server/CN=$host:\$1\$OXLyS...\$Owx8s2/m9/gfkcRVXzgoE/
EOF

View file

@ -31,14 +31,23 @@
#include <apr_lib.h>
#define APR_WANT_STRFUNC
#include <apr_want.h>
#include <apr_base64.h>
#include <httpd.h>
// Hack to workaround compile error with HTTPD 2.3.8
#define new new_
#include <http_config.h>
#undef new
#include <http_core.h>
#include <http_connection.h>
#include <http_request.h>
#include <http_protocol.h>
// Hack to workaround compile error with HTTPD 2.3.8
#define aplog_module_index aplog_module_index = 0
#include <http_log.h>
#undef aplog_module_index
#undef APLOG_MODULE_INDEX
#define APLOG_MODULE_INDEX (aplog_module_index ? *aplog_module_index : APLOG_NO_MODULE)
#include <http_main.h>
#include <util_script.h>
#include <util_md5.h>
@ -46,6 +55,8 @@
#include <http_log.h>
#include <ap_mpm.h>
#include <mod_core.h>
#include <ap_provider.h>
#include <mod_auth.h>
#include "string.hpp"
#include "stream.hpp"
@ -358,6 +369,7 @@ const failable<int> writeResult(const failable<list<string> >& ls, const string&
* Report a request execution status.
*/
const int reportStatus(const failable<int>& rc) {
debug(rc, "httpd::reportStatus::rc");
if (!hasContent(rc))
return HTTP_INTERNAL_SERVER_ERROR;
return content(rc);
@ -575,10 +587,11 @@ const failable<request_rec*, int> internalSubRequest(const string& nr_uri, reque
* Return an HTTP external redirect request.
*/
const int externalRedirect(const string& uri, request_rec* r) {
debug(uri, "httpd::externalRedirect");
r->status = HTTP_MOVED_TEMPORARILY;
apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(uri)));
r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:/") + uri));
return OK;
return HTTP_MOVED_TEMPORARILY;
}
/**
@ -642,6 +655,12 @@ int debugNote(unused void* r, const char* key, const char* value) {
*/
const bool debugRequest(request_rec* r, const string& msg) {
cdebug << msg << ":" << endl;
cdebug << " unparsed uri: " << debugOptional(r->unparsed_uri) << endl;
cdebug << " uri: " << debugOptional(r->uri) << endl;
cdebug << " path info: " << debugOptional(r->path_info) << endl;
cdebug << " filename: " << debugOptional(r->filename) << endl;
cdebug << " uri tokens: " << pathTokens(r->uri) << endl;
cdebug << " args: " << debugOptional(r->args) << endl;
cdebug << " server: " << debugOptional(r->server->server_hostname) << endl;
cdebug << " protocol: " << debugOptional(r->protocol) << endl;
cdebug << " method: " << debugOptional(r->method) << endl;
@ -649,16 +668,10 @@ const bool debugRequest(request_rec* r, const string& msg) {
cdebug << " content type: " << contentType(r) << endl;
cdebug << " content encoding: " << debugOptional(r->content_encoding) << endl;
apr_table_do(debugHeader, r, r->headers_in, NULL);
cdebug << " unparsed uri: " << debugOptional(r->unparsed_uri) << endl;
cdebug << " uri: " << debugOptional(r->uri) << endl;
cdebug << " path info: " << debugOptional(r->path_info) << endl;
cdebug << " filename: " << debugOptional(r->filename) << endl;
cdebug << " uri tokens: " << pathTokens(r->uri) << endl;
cdebug << " args: " << debugOptional(r->args) << endl;
cdebug << " user: " << debugOptional(r->user) << endl;
cdebug << " auth type: " << debugOptional(r->ap_auth_type) << endl;
apr_table_do(debugEnv, r, r->subprocess_env, NULL);
apr_table_do(debugEnv, r, r->notes, NULL);
apr_table_do(debugNote, r, r->notes, NULL);
return true;
}

View file

@ -0,0 +1,325 @@
/*
* 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.
*/
/* $Rev$ $Date$ */
/**
* HTTPD module for Tuscany Open authentication.
*
* This module allows multiple authentication mechanisms to co-exist in a
* single Web site:
* - OAuth1 using Tuscany's mod-tuscany-oauth1
* - OAuth2 using Tuscany's mod-tuscany-oauth2
* - OpenID using mod_auth_openid
* - Form-based using HTTPD's mod_auth_form
* - SSL certificate using SSLFakeBasicAuth and mod_auth_basic
*/
#include <sys/stat.h>
#include "string.hpp"
#include "stream.hpp"
#include "list.hpp"
#include "tree.hpp"
#include "value.hpp"
#include "monad.hpp"
#include "httpd.hpp"
#include "http.hpp"
#include "openauth.hpp"
extern "C" {
extern module AP_MODULE_DECLARE_DATA mod_tuscany_openauth;
}
namespace tuscany {
namespace openauth {
/**
* Server configuration.
*/
class ServerConf {
public:
ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s) {
}
const gc_pool p;
server_rec* server;
};
/**
* Directory configuration.
*/
class DirConf {
public:
DirConf(apr_pool_t* p, char* d) : p(p), dir(d), enabled(false), login("") {
}
const gc_pool p;
const char* dir;
bool enabled;
string login;
};
/**
* Return the user info from a form auth session cookie.
*/
const failable<value> userInfo(const value& sid, const string& realm) {
const list<list<value>> info = httpd::queryArgs(sid);
debug(info, "modopenauth::userInfo::info");
const list<value> user = assoc<value>(realm + "-user", info);
if (isNil(user))
return mkfailure<value>("Couldn't retrieve user id");
const list<value> pw = assoc<value>(realm + "-pw", info);
if (isNil(pw))
return mkfailure<value>("Couldn't retrieve password");
return value(mklist<value>(mklist<value>("realm", realm), mklist<value>("id", cadr(user)), mklist<value>("password", cadr(pw))));
}
/**
* Return the user info from a basic auth header.
*/
const failable<value> userInfo(const char* header, const string& realm, request_rec* r) {
debug(header, "modopenauth::userInfo::header");
if (strcasecmp(ap_getword(r->pool, &header, ' '), "Basic"))
return mkfailure<value>("Wrong authentication scheme");
while (apr_isspace(*header))
header++;
char *decoded_line = (char*)apr_palloc(r->pool, apr_base64_decode_len(header) + 1);
int length = apr_base64_decode(decoded_line, header);
decoded_line[length] = '\0';
const string user(ap_getword_nulls(r->pool, const_cast<const char**>(&decoded_line), ':'));
const string pw(decoded_line);
return value(mklist<value>(mklist<value>("realm", realm), mklist<value>("id", user), mklist<value>("password", pw)));
}
/**
* Handle an authenticated request.
*/
const failable<int> authenticated(const list<list<value> >& info, request_rec* r) {
debug(info, "modopenauth::authenticated::info");
// Store user info in the request
const list<value> realm = assoc<value>("realm", info);
if (isNil(realm) || isNil(cdr(realm)))
return mkfailure<int>("Couldn't retrieve realm");
apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "REALM"), apr_pstrdup(r->pool, c_str(cadr(realm))));
const list<value> id = assoc<value>("id", info);
if (isNil(id) || isNil(cdr(id)))
return mkfailure<int>("Couldn't retrieve user id");
r->user = apr_pstrdup(r->pool, c_str(cadr(id)));
apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "NICKNAME"), apr_pstrdup(r->pool, c_str(cadr(id))));
return OK;
}
/**
* 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) {
// 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);
if (atype == NULL || strcasecmp(atype, "Open"))
return DECLINED;
gc_scoped_pool pool(r->pool);
httpdDebugRequest(r, "modopenauth::checkAuthn::input");
// Get session id from the request
const maybe<string> sid = sessionID(r);
if (hasContent(sid)) {
// Decline if the session id was not created by this module
const string stype = substr(content(sid), 0, 7);
if (stype == "OAuth2_" || stype == "OAuth1_" || stype == "OpenID_")
return DECLINED;
// Retrieve the auth realm
const char* aname = ap_auth_name(r);
if (aname == NULL)
return httpd::reportStatus(mkfailure<int>("Missing AuthName"));
// Extract user info from the session id
const failable<value> info = userInfo(content(sid), aname);
if (hasContent(info)) {
// Try to authenticate the request
const value cinfo = content(info);
const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", cinfo)), cadr(assoc<value>("password", cinfo)), r);
if (!hasContent(authz)) {
// Authentication failed, redirect to login page
r->ap_auth_type = const_cast<char*>(atype);
return httpd::reportStatus(login(dc.login, r));
}
// Successfully authenticated, store the user info in the request
r->ap_auth_type = const_cast<char*>(atype);
return httpd::reportStatus(authenticated(cinfo, r));
}
}
// Get basic auth header from the request
const char* header = apr_table_get(r->headers_in, (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authorization" : "Authorization");
if (header != NULL) {
// Retrieve the auth realm
const char* aname = ap_auth_name(r);
if (aname == NULL)
return httpd::reportStatus(mkfailure<int>("Missing AuthName"));
// Extract user info from the session id
const failable<value> info = userInfo(header, aname, r);
if (hasContent(info)) {
// Try to authenticate the request
const value cinfo = content(info);
const failable<int> authz = checkAuthnz(cadr(assoc<value>("id", cinfo)), cadr(assoc<value>("password", cinfo)), r);
if (!hasContent(authz)) {
// Authentication failed, redirect to login page
r->ap_auth_type = const_cast<char*>(atype);
return httpd::reportStatus(login(dc.login, r));
}
// Successfully authenticated, store the user info in the request
r->ap_auth_type = const_cast<char*>(atype);
return httpd::reportStatus(authenticated(cinfo, 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;
if (!isNil(assoc<value>("mod_oauth1_step", args)))
return DECLINED;
if (!isNil(assoc<value>("mod_oauth2_step", args)))
return DECLINED;
// Redirect to the login page
r->ap_auth_type = const_cast<char*>(atype);
return httpd::reportStatus(login(dc.login, r));
}
/**
* Process the module configuration.
*/
int postConfigMerge(ServerConf& mainsc, server_rec* s) {
if (s == NULL)
return OK;
debug(httpd::serverName(s), "modopenauth::postConfigMerge::serverName");
return postConfigMerge(mainsc, s->next);
}
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");
// Merge server configurations
return postConfigMerge(sc, s);
}
/**
* 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_openauth);
if(psc == NULL) {
cfailure << "[Tuscany] Due to one or more errors mod_tuscany_openauth loading failed. Causing apache to stop loading." << endl;
exit(APEXIT_CHILDFATAL);
}
ServerConf& sc = *psc;
// Merge the updated configuration into the virtual hosts
postConfigMerge(sc, s->next);
}
/**
* Configuration commands.
*/
const char* confEnabled(cmd_parms *cmd, void *c, const int arg) {
gc_scoped_pool pool(cmd->pool);
DirConf& dc = httpd::dirConf<DirConf>(c);
dc.enabled = (bool)arg;
return NULL;
}
const char* confLogin(cmd_parms *cmd, void *c, const char* arg) {
gc_scoped_pool pool(cmd->pool);
DirConf& dc = httpd::dirConf<DirConf>(c);
dc.login = arg;
return NULL;
}
/**
* HTTP server module declaration.
*/
const command_rec commands[] = {
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}
};
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);
}
}
}
extern "C" {
module AP_MODULE_DECLARE_DATA mod_tuscany_openauth = {
STANDARD20_MODULE_STUFF,
// dir config and merger
tuscany::httpd::makeDirConf<tuscany::openauth::DirConf>, NULL,
// server config and merger
tuscany::httpd::makeServerConf<tuscany::openauth::ServerConf>, NULL,
// commands and hooks
tuscany::openauth::commands, tuscany::openauth::registerHooks
};
}

View file

@ -0,0 +1,66 @@
#!/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 form authentication configuration
here=`readlink -f $0`; here=`dirname $here`
mkdir -p $1
root=`readlink -f $1`
conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
host=`echo $conf | awk '{ print $6 }'`
httpd_prefix=`cat $here/httpd.prefix`
# Generate form authentication configuration
cat >>$root/conf/auth.conf <<EOF
# Generated by: open-auth-conf $*
# Enable Tuscany open authentication
<Location />
AuthType Open
AuthName "$host"
AuthOpenAuth On
AuthOpenAuthLoginPage /login
AuthUserFile "$root/conf/httpd.passwd"
Require valid-user
</Location>
# Use HTTPD form-based authentication
<Location /login/dologin>
AuthType Form
AuthName "$host"
AuthFormProvider file
AuthUserFile "$root/conf/httpd.passwd"
AuthFormLoginRequiredLocation /login
AuthFormLogoutLocation /
Session On
SessionCookieName TuscanyOpenAuth path=/;secure=TRUE
#SessionCryptoPassphrase secret
Require valid-user
SetHandler form-login-handler
</Location>
EOF
# Create test users
touch $root/conf/httpd.passwd
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd test test 2>/dev/null
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd admin admin 2>/dev/null
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd foo foo 2>/dev/null
$httpd_prefix/bin/htpasswd -b $root/conf/httpd.passwd bar bar 2>/dev/null

View file

@ -0,0 +1,98 @@
/*
* 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.
*/
/* $Rev$ $Date$ */
#ifndef tuscany_openauth_hpp
#define tuscany_openauth_hpp
/**
* Tuscany Open auth support utility functions.
*/
#include "string.hpp"
#include "stream.hpp"
#include "list.hpp"
#include "tree.hpp"
#include "value.hpp"
#include "monad.hpp"
#include "../json/json.hpp"
#include "../http/httpd.hpp"
#include "../http/http.hpp"
namespace tuscany {
namespace openauth {
/**
* Return the session id from a request.
*/
const char* cookieName(const char* cs) {
if (*cs != ' ')
return cs;
return cookieName(cs + 1);
}
const maybe<string> sessionID(const list<string> c) {
if (isNil(c))
return maybe<string>();
const string cn = cookieName(c_str(car(c)));
const int i = find(cn, "=");
if (i < length(cn)) {
const list<string> kv = mklist<string>(substr(cn, 0, i), substr(cn, i+1));
if (!isNil(kv) && !isNil(cdr(kv))) {
if (car(kv) == "TuscanyOpenAuth")
return cadr(kv);
}
}
return sessionID(cdr(c));
}
const maybe<string> sessionID(const request_rec* r) {
const char* c = apr_table_get(r->headers_in, "Cookie");
debug(c, "openauth::sessionid::cookies");
if (c == NULL)
return maybe<string>();
return sessionID(tokenize(";", c));
}
/**
* Convert a session id to a cookie string.
*/
const string cookie(const string& sid) {
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 = string("TuscanyOpenAuth=") + sid + string(";path=/;expires=" + string(exp)) + ";secure=TRUE";
debug(c, "openauth::cookie");
return c;
}
/**
* 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("?") + httpd::queryString(largs);
debug(loc, "openauth::login::uri");
return httpd::externalRedirect(loc, r);
}
}
}
#endif /* tuscany_openauth_hpp */

View file

@ -33,8 +33,7 @@ ProxyStatus On
ProxyPass / balancer://cluster/
<Proxy balancer://cluster>
Order deny,allow
Allow from all
Require all granted
ProxySet lbmethod=byrequests
</Proxy>

View file

@ -34,8 +34,7 @@ ProxyPass /balancer-manager !
ProxyPass / balancer://sslcluster/
<Proxy balancer://sslcluster>
Order deny,allow
Allow from all
Require all granted
ProxySet lbmethod=byrequests
</Proxy>
@ -43,8 +42,6 @@ ProxySet lbmethod=byrequests
<Location /balancer-manager>
SetHandler balancer-manager
HostnameLookups on
Deny from all
Allow from all
Require user admin
</Location>