summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2009-06-17 00:13:58 +0000
committerStef Walter <stef@memberwebs.com>2009-06-17 00:13:58 +0000
commita7557acb5423ea8e4c6bcce27472918c638d56f8 (patch)
tree3e3c0e5b811441d1231670ba034be6c4973fdb1d
parent2e1c4b9fc834c8af9f1a31789b1dc614c13fcad5 (diff)
Some coding work. Banged out generally how things will work. Nothing compiles yet.
-rw-r--r--.gitignore4
-rwxr-xr-xautogen.sh1
-rw-r--r--configure.in36
-rw-r--r--ideas.txt19
-rw-r--r--module/Makefile.am38
-rw-r--r--module/consumer.cc243
-rw-r--r--module/consumer.h14
-rw-r--r--module/mod_auth_singleid.c1621
-rw-r--r--module/storage.c200
-rw-r--r--module/storage.h32
10 files changed, 2180 insertions, 28 deletions
diff --git a/.gitignore b/.gitignore
index 7753ee0..66a335c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,7 @@
+*.o
+depcomp
+libtool
+ltmain.sh
aclocal.m4
autom4te.cache
config.*
diff --git a/autogen.sh b/autogen.sh
index 3b52ffd..2995946 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -4,6 +4,7 @@ set -ex
aclocal
autoheader
+libtoolize
automake -a
autoconf
./configure --enable-maintainer-mode "$@"
diff --git a/configure.in b/configure.in
index de38bd9..a18d5de 100644
--- a/configure.in
+++ b/configure.in
@@ -39,17 +39,38 @@ dnl Process this file with autoconf to produce a configure script.
AC_INIT(mod_auth_singleid, 0.1, stef@memberwebs.com)
AM_INIT_AUTOMAKE(mod_auth_singleid, 0.1)
-AC_CONFIG_SRCDIR([module/mod_auth_singleid.c])
+AC_CONFIG_SRCDIR([module/mod_auth_singleid.cc])
AM_CONFIG_HEADER([config.h])
# Checks for programs.
-AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_CXXCPP
+AC_LANG_CPLUSPLUS
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
+AM_PROG_LIBTOOL
# APACHE Build stuff
+AC_ARG_WITH(apr_config, AC_HELP_STRING([[--with-apr-config=FILE]], [Path to apr-config program]),
+ [ apr_config="$withval" ],
+ [AC_PATH_PROGS(apr_config,
+ [apr-config apr-0-config apr-1-config],
+ [no],
+ [$PATH:/usr/sbin/:/usr/local/apache2/bin]
+ )]
+)
+
+if test "$apr_config" = "no"; then
+ AC_MSG_ERROR(Could not find the apr-config program. You can specify a location with the --with-apr-config=FILE option. It may be named apr-0-config or apr-1-config and can be found in your apache2 bin directory.)
+fi
+
+$apr_config --cppflags &> /dev/null
+if test "$?" != "0"; then
+ AC_MSG_ERROR($apr_config is not a valid apr-config program)
+fi
+
AC_PATH_PROG(APXS, apxs, "failed")
if test "$APXS" = "failed"; then
AC_PATH_PROG(APXS2, apxs2, "failed")
@@ -75,6 +96,17 @@ fi
AC_SUBST(APACHECTL)
+APACHE_CFLAGS="-I`${APXS} -q INCLUDEDIR` -I`${apr_config} --includedir --cppflags`"
+AC_SUBST(APACHE_CFLAGS)
+
+# ------------------------------------------------------------------------------------
+# OPKELE
+
+PKG_CHECK_MODULES([OPKELE], [libopkele >= 2.0], , [
+ AC_MSG_ERROR([no libopkele library found (version 2.0 or higher). get one from http://kin.klever.net/libopkele/])
+])
+
+
AC_DEFINE_UNQUOTED(CONF_PREFIX, "`eval echo ${sysconfdir}`", [Installation Prefix] )
# Debug mode
diff --git a/ideas.txt b/ideas.txt
new file mode 100644
index 0000000..f10cbb5
--- /dev/null
+++ b/ideas.txt
@@ -0,0 +1,19 @@
+
+AuthSingleIdIdentifier https://id.familymembers.com/
+AuthSingleIdCookie openid
+AuthSingleIdUserPrefix https://id.familymembers.com/
+AuthSingleIdUserSuffix
+AuthSingleIdAttribute url alias count
+
+AX_TYPE_FNAME=http://example.com/schema/fullname
+AX_VALUE_FNAME=John Smith
+AX_TYPE_GENDER=http://example.com/schema/gender
+AX_COUNT_GENDER=0
+AX_TYPE_FAV_DOG=http://example.com/schema/favourite_dog
+AX_VALUE_FAV_DOG=Spot
+AX_TYPE_FAV_MOVIE=http://example.com/schema/favourite_movie
+AX_COUNT_FAV_MOVIE=2
+AX_VALUE_FAV_MOVIE_1=Movie1
+AX_VALUE_FAV_MOVIE_2=Movie2
+AX_UPDATE_URL=http://idconsumer.com/update?transaction_id=a6b5c41
+
diff --git a/module/Makefile.am b/module/Makefile.am
index b303c5e..7b05ad4 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -1,30 +1,16 @@
+noinst_LTLIBRARIES = libmodauthsingleid.la
+noinst_DATA = mod_auth_singleid.la
-EXTRA_DIST = mod_auth_singleid.c
+INCLUDES = -I../ -g -O0 ${APACHE_CFLAGS} ${OPKELE_CFLAGS}
+AM_LDFLAGS = ${OPKELE_LIBS}
-DEF =
-INC = -I../
+libmodauthsingleid_la_SOURCES = \
+ mod_auth_singleid.c \
+ consumer.cc consumer.h \
+ storage.c storage.h
-all: mod_auth_singleid.so
-
-mod_auth_singleid.so: mod_auth_singleid.c
- @APXS@ -c -Wc,-g -Wc,-O0 -Wc,-Wall $(DEF) $(INC) $(LIB) mod_auth_singleid.c
-
-# Install the DSO file into the Apache installation and activate it in config
-install: all
- @APXS@ -i -a -c -Wc,-g -Wc,-O0 $(DEF) $(INC) $(LIB) mod_auth_singleid.c
-
-# Cleanup
-clean:
- -rm -f mod_auth_singleid.o mod_auth_singleid.so
-
-# Reload the module by installing and restarting Apache
-reload: install restart
-
-# The general Apache start/restart/stop procedures
-start:
- @APACHECTL@ start
-restart:
- @APACHECTL@ restart
-stop:
- @APACHECTL@ stop
+install-exec-local:
+ @APXS@ -i -a -n 'authopenid' mod_auth_openid.la
+mod_auth_singleid.la: libmodauthsingleid.la
+ ${APXS} -c -o $@ $< ${APACHE_CFLAGS} ${OPKELE_CFLAGS} ${OPKELE_LIBS}
diff --git a/module/consumer.cc b/module/consumer.cc
new file mode 100644
index 0000000..8ade43a
--- /dev/null
+++ b/module/consumer.cc
@@ -0,0 +1,243 @@
+
+#include "consumer.h"
+
+using opkele::assoc_t;
+using opkele::association;
+using opkele::endpoint_t;
+using opkele::failed_lookup;
+using opkele::params;
+using opkele::prequeue_RP;
+using opkele::secret_t;
+
+using std::string;
+using std::vector;
+
+class Consumer :
+ public prequeue_RP
+{
+private: // types
+ vector<endpoint_t> endpoints;
+
+public: // interface
+ Consumer(const char *url, singleid_board_t *board)
+ : _url(url), _board(board)
+ { }
+
+public: // overrides
+
+ virtual void begin_queueing() const
+ { _endpoints.clear(); }
+
+ virtual void queue_endpoint(const openid_endpoint_t& oep)
+ { _endpoints.push(oep); }
+
+ virtual void set_normalized_id(const string& nid)
+ { _normalized = nid; }
+
+ virtual const string get_normalized_id() const
+ { return _normalized; }
+
+ virtual const string get_this_url()
+ { return _url; }
+
+ virtual assoc_t store_assoc(const string& server, const string& handle,
+ const string& type, const secret_t& secret,
+ int expires_in);
+
+ virtual assoc_t find_assoc(const string& server);
+
+ virtual assoc_t retrieve_assoc(const string& server, const string& handle);
+
+ virtual assoc_t invalidate_assoc(const string& server, const string& handle);
+
+ virtual void check_nonce(const string& server, const string& nonce);
+
+private: // data
+ singleid_board_t _board;
+ endpoints _endpoints;
+ string _normalized;
+ string _url;
+};
+
+assoc_t
+Consumer::store_assoc(const string& server, const string& handle,
+ const string& type, const secret_t& secret,
+ int expires_in)
+{
+ singleid_assoc_t data;
+ int res;
+
+ data.server = server.c_str();
+ data.handle = handle.c_str();
+ data.type = type.c_str();
+ data.secret = secret.data();
+ data.n_secret = secret.size();
+ data.expires = expires_in;
+
+ {
+ LockShared lock; /* scoped lock */
+ res = singleid_board_store_assoc (_board, &data);
+ }
+
+ if (!res)
+ throw dump_RP("association data was too large to fit in shared storage");
+
+ return assoc_t(new association(server, handle, type, secret, expires_on, false));
+}
+
+assoc_t
+Consumer::find_assoc(const string& server)
+{
+ singleid_assoc_t data = { 0, };
+ association assoc = NULL;
+
+ {
+ LockShared lock;
+ if (singleid_board_find_assoc (_board, server.c_str(), NULL, &data))
+ assoc = new association(data.server, data.handle, data.type,
+ secret_t(data.secret, data.secret + data.n_secret),
+ data.expires, false);
+ }
+
+ if (!assoc)
+ throw failed_lookup("could not find association for server: " + server);
+
+ return assoc_t(assoc);
+}
+
+assoc_t
+Consumer::retrieve_assoc(const string& server, const string& handle)
+{
+ singleid_assoc_t data = { 0, };
+ association assoc = NULL;
+
+ {
+ LockShared lock;
+ if (singleid_board_find_assoc (_board, server.c_str(), handle.c_str(), &data))
+ assoc = new association(data.server, data.handle, data.type,
+ secret_t(data.secret, data.secret + data.n_secret),
+ data.expires, false);
+ }
+
+ if (!assoc)
+ throw failed_lookup("could not retrieve association for server: " + server);
+
+ return assoc_t(assoc);
+}
+
+assoc_t
+Consumer::invalidate_assoc(const string& server, const string& handle)
+{
+ LockShared lock;
+ singleid_board_invalidate_assoc (_board, server.c_str(), handle.c_str());
+}
+
+
+void
+Consumer::check_nonce(const string& server, const string& nonce)
+{
+ LockShared lock;
+ singleid_board_check_nonce (_board, server.c_str(), nonce.c_str());
+}
+
+/* -----------------------------------------------------------------------
+ * AUTHENTICATION
+ */
+
+static void
+filter_openid_params (params_t &params, params_t &extensions)
+{
+ for (params_t::iterator it = params.begin(); it != params.end(); ) {
+ const string& name = it->first;
+ if (name.find ("openid.") == 0) {
+ /* Extension params have at least a second dot */
+ if (name.find ('.', sizeof ("openid.")))
+ extensions.insert(*it);
+
+ /* We erase an increment together, must use post-increment operator */
+ it->erase(it++);
+ } else {
+ /* Did not match, just go to next element */
+ ++it;
+ }
+ }
+}
+
+static void
+start_auth (sid_request_t *req, Consumer &consumer, params_t &params,
+ const string& trust_root, const string &identity)
+{
+ /* Remove all openid params, and stash away extensions */
+ filter_openid_params (params);
+ string return_to = params.append_query (consumer.get_this_url(), "");
+
+ params_t result;
+
+ try {
+ openid_message_t cm;
+ consumer.initiate (identity);
+ result = consumer.checkid_ (cm, opkele::mode_checkid_setup, return_to, trust_root);
+ output = result.append_query (consumer.get_endpoint().uri);
+
+ } catch (failed_xri_resolution &ex) {
+ sid_request_respond (xxx, xxx, xxx);
+ return;
+
+ } catch (failed_discovery &ex) {
+ sid_request_respond (xxx, xxx, xxx);
+ return;
+
+ } catch (bad_input &ex) {
+ sid_request_respond (xxx, xxx, xxx);
+ return;
+
+ } catch (exception &ex) {
+ sid_request_respond (xxx, xxx, xxx);
+ xxx log xxx;
+ return;
+ }
+
+ sid_request_respond (xxx, xxx, xxx);
+}
+
+static void
+complete_auth (sid_request_t *req, Consumer &consumer, params_t &params)
+{
+ try {
+ consumer.id_res(AdaptorFix(params));
+ string identity = consumer.get_claimed_id();
+ sid_request_authenticated (req, identity.c_str());
+ } catch (exception &ex) {
+ sid_request_respond (xxx, xxx, xxx);
+ }
+}
+
+static void
+cancelled_auth (sid_request_t *req, Consumer &consumer, params_t &params)
+{
+ sid_request_respond (xxx, xxx, xxx);
+}
+
+void
+sid_consumer_authenticate(sid_request_t *req, sid_storage_t *store, const char *identity)
+{
+ params_t params;
+
+ assert (req);
+ assert (store);
+
+ const char *qs = sid_request_qs (req);
+ parse_query_string (qs, params);
+
+ const char *url = sid_request_url (req);
+ Consumer consumer(url, store);
+
+ if (params.has_param("openid.assoc_handle"))
+ complete_auth (req, consumer, params);
+ else if (params.has_param("openid.mode") && params.get_param("openid.mode") == "cancel")
+ cancelled_auth (req, consumer, params);
+ else
+ begin_auth (req, consumer, params, trust_root, identity);
+
+ consumer.close();
+}
diff --git a/module/consumer.h b/module/consumer.h
new file mode 100644
index 0000000..18ddbe4
--- /dev/null
+++ b/module/consumer.h
@@ -0,0 +1,14 @@
+#ifndef CONSUMER_H_
+#define CONSUMER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+singleid_consumer_authenticate (request_rec *r, );
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* CONSUMER_H_ */
diff --git a/module/mod_auth_singleid.c b/module/mod_auth_singleid.c
new file mode 100644
index 0000000..5d8ddb4
--- /dev/null
+++ b/module/mod_auth_singleid.c
@@ -0,0 +1,1621 @@
+/*
+ * Copyright (c) 2009, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Stef Walter <stef@memberwebs.com>
+ *
+ */
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_log.h>
+#if 0
+#include <http_protocol.h>
+#include <http_request.h>
+#include <ap_config.h>
+#include <apr_strings.h>
+#include <apr_file_io.h>
+#include <apr_lib.h>
+#endif
+
+#include "consumer.h"
+#include "storage.h"
+
+/* Apache defines these */
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+
+#include "config.h"
+#include <unistd.h>
+
+extern "C" module AP_MODULE_DECLARE_DATA auth_singleid_module;
+
+#if 0
+
+/* Keep track of a unique identifier */
+static void* conn_current = NULL;
+
+/* And increment this when it goes out of scope */
+static unsigned int conn_seen = 0;
+
+/*
+ * Per directory configuration.
+ */
+typedef struct httpauth_context {
+ const char* socketname;
+ int socket;
+
+ int types;
+ const char* handler;
+ const char* domain;
+ char* needed_groups;
+ int alloced_groups;
+ apr_pool_t* child_pool;
+
+ int address_seed;
+ int retries;
+
+ int shared_version;
+ void *shared_block;
+} httpauth_context_t;
+
+/*
+ * Tagged onto a request once authenticated, used for access
+ * groups and revalidating an already authenticated request.
+ */
+typedef struct httpauth_request {
+ const char *user;
+ const char *groups;
+} httpauth_request_t;
+
+/*
+ * Shared between all instances of a httpauth_context in
+ * different processes on a server.
+ */
+typedef struct httpauth_shared {
+ int version;
+ struct sockaddr_any address;
+} httpauth_shared_t;
+
+/* TODO: Support proxy authentication properly */
+
+#define AUTH_PREFIX_BASIC "Basic"
+#define AUTH_PREFIX_DIGEST "Digest"
+#define AUTH_PREFIX_NTLM "NTLM"
+
+#define AUTH_TYPE_BASIC 1 << 1
+#define AUTH_TYPE_DIGEST 1 << 2
+#define AUTH_TYPE_NTLM 1 << 3
+#define AUTH_TYPE_ANY 0x0000FFFF
+
+#endif
+
+#define SINGLEID_AUTHTYPE "SINGLEID"
+
+/* -------------------------------------------------------------------------------
+ * SHARED MEMORY
+ */
+
+static apr_global_mutex_t *shared_lock = NULL;
+static const char *shared_lock_name = NULL;
+
+static int
+shared_initialize (apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
+{
+ apr_file_t *file = NULL;
+ const char *tmpdir;
+ char *lock_name;
+ int rc;
+
+ /* This may be called more than once */
+ if (shared_lock)
+ return OK;
+
+ rc = apr_temp_dir_get (&tmpdir, p);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, s,
+ "auth-singleid: couldn't get temporary directory");
+
+ if (rc == APR_SUCCESS) {
+ lock_name = apr_pstrcat (p, tmpdir, "/", "mod-auth-singleid.lock.XXXXXX", NULL);
+ rc = apr_file_mktemp (&file, lock_name, 0, p);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, NULL,
+ "auth-singleid: couldn't create temporary file: %s", lock_name);
+ }
+
+ if (file != NULL)
+ apr_file_close (file);
+
+ if (rc == APR_SUCCESS) {
+ rc = apr_global_mutex_create (&shared_lock, lock_name, APR_LOCK_DEFAULT, p);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, s,
+ "auth-singleid: couldn't create shared memory lock: %s", lock_name);
+ }
+
+#ifdef AP_NEED_SET_MUTEX_PERMS
+ if (rc == APR_SUCCESS) {
+ rc = unixd_set_global_mutex_perms (shared_lock);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, s,
+ "auth-singleid: Could not set permissions on lock. "
+ "check User and Group directives");
+ }
+#endif
+
+ if (rc == APR_SUCCESS)
+ shared_lock_name = lock_name;
+
+ return OK;
+}
+
+static void
+shared_child (apr_pool_t *p, server_rec *s)
+{
+ apr_status_t rc;
+
+ if (!shared_lock || !shared_lock_name)
+ return;
+
+ rc = apr_global_mutex_child_init (&shared_lock, shared_lock_name, p);
+ if (rc != APR_SUCCESS) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, s,
+ "httpauth: couldn't create lock for shared memory in child: %s", shared_lock_name);
+ shared_lock = NULL;
+ }
+}
+
+static void*
+shared_create (apr_pool_t* p, size_t size)
+{
+ const char *tmpdir;
+ char *filename;
+ apr_file_t *file;
+ apr_mmap_t *map;
+ void *addr;
+ int rc;
+
+ /* Get the temp directory */
+ rc = apr_temp_dir_get (&tmpdir, p);
+ if (rc != APR_SUCCESS) {
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, NULL,
+ "auth-singleid: couldn't get temporary directory");
+ }
+
+ /* Create the shared file */
+ if (rc == APR_SUCCESS) {
+ filename = apr_pstrcat (p, tmpdir, "/", "mod-auth-singleid.shared.XXXXXX", NULL);
+ rc = apr_file_mktemp (&file, filename, 0, p);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, NULL,
+ "auth-singleid: couldn't create temporary file: %s", filename);
+ }
+
+ /* Write a shared block to file */
+ if (rc == APR_SUCCESS) {
+ memset (&shared, 0, sizeof (shared));
+ xxxxxx
+ rc = apr_file_write_full (file, &xxxx, size, NULL);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, NULL,
+ "auth-singleid: couldn't write to temporary file: %s", filename);
+ }
+
+ /* Map the shared file into memory */
+ if (rc == APR_SUCCESS) {
+ rc = apr_mmap_create (&map, file, 0, size, APR_MMAP_READ | APR_MMAP_WRITE, p);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, NULL,
+ "auth-singleid: couldn't map temporary file: %s", filename);
+ }
+
+ /* Get the actual address of the mapping */
+ if (rc == APR_SUCCESS) {
+ rc = apr_mmap_offset (&addr, map, 0);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, NULL,
+ "auth-singleid: couldn't get shared memory");
+ }
+
+ if (rc == APR_SUCCESS)
+ return addr;
+
+ return NULL;
+}
+
+/* -------------------------------------------------------------------------------------------------
+ * COMMON STORAGE
+ */
+
+typedef struct storage_context {
+ void* shared;
+ size_t size;
+} storage_context_t;
+
+/* -------------------------------------------------------------------------------------------------
+ * OPENID CONSUMER
+ */
+
+#if 0
+static void* storage_shared = NULL;
+static size_t storage_size = NULL;
+#endif
+
+#if 0
+
+static int
+shared_get_if_changed (httpauth_context_t *ctx, int version, httpauth_shared_t *shared)
+{
+ httpauth_shared_t *block;
+ int ret = 0;
+
+ if (!ctx->shared_block || !shared_lock)
+ return 0;
+
+ apr_global_mutex_lock (shared_lock);
+
+ block = ctx->shared_block;
+ if (block->version != version) {
+ ret = 1;
+ if (shared)
+ memcpy (shared, block, sizeof (*shared));
+ }
+
+ apr_global_mutex_unlock (shared_lock);
+
+ return ret;
+}
+
+static void
+shared_set_if_changed (httpauth_context_t *ctx, httpauth_shared_t *shared)
+{
+ httpauth_shared_t *block;
+
+ if (!ctx->shared_block || !shared_lock)
+ return;
+
+ apr_global_mutex_lock (shared_lock);
+
+ block = ctx->shared_block;
+ if (memcmp (shared, block, sizeof (*shared)) != 0) {
+
+ /* Increment the version beyond all */
+ if (block->version > shared->version)
+ shared->version = block->version;
+ ++shared->version;
+
+ /* And write it out */
+ memcpy (block, shared, sizeof (*shared));
+ }
+
+ apr_global_mutex_unlock (shared_lock);
+}
+#endif
+
+/* -------------------------------------------------------------------------------
+ * Per Directory Config and Context Code
+ */
+
+static void*
+dir_config_creator (apr_pool_t* p, char* dir)
+{
+#if 0
+ httpauth_context_t* ctx;
+ httpauth_shared_t shared;
+ const char *tmpdir;
+ char *filename;
+ apr_file_t *file;
+ apr_mmap_t *map;
+ void *addr;
+ int rc;
+
+ ctx = (httpauth_context_t*)apr_pcalloc(p, sizeof(*ctx));
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->socket = -1;
+ ctx->types = 0xFFFFFFFF;
+ ctx->child_pool = p;
+ ctx->needed_groups = NULL;
+ ctx->alloced_groups = 0;
+ ctx->shared_version = 0;
+ ctx->retries = 1;
+
+ if (!dir)
+ return ctx;
+
+ /* Get the temp directory */
+ rc = apr_temp_dir_get (&tmpdir, p);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, NULL,
+ "httpauth: couldn't get temporary directory");
+
+ /* Create the shared file */
+ if (rc == APR_SUCCESS) {
+ filename = apr_pstrcat (p, tmpdir, "/", "mod-httpauth.board.XXXXXX", NULL);
+ rc = apr_file_mktemp (&file, filename, 0, p);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, NULL,
+ "httpauth: couldn't create temporary file: %s", filename);
+ }
+
+ /* Write a shared block to file */
+ if (rc == APR_SUCCESS) {
+ memset (&shared, 0, sizeof (shared));
+ rc = apr_file_write_full (file, &shared, sizeof (shared), NULL);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, NULL,
+ "httpauth: couldn't write to temporary file: %s", filename);
+ }
+
+ /* Map the shared file into memory */
+ if (rc == APR_SUCCESS) {
+ rc = apr_mmap_create (&map, file, 0, sizeof (shared),
+ APR_MMAP_READ | APR_MMAP_WRITE, p);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, NULL,
+ "httpauth: couldn't map temporary file: %s", filename);
+ }
+
+ /* Get the actual address of the mapping */
+ if (rc == APR_SUCCESS) {
+ rc = apr_mmap_offset (&addr, map, 0);
+ if (rc != APR_SUCCESS)
+ ap_log_error (APLOG_MARK, APLOG_ERR, rc, NULL,
+ "httpauth: couldn't get shared memory");
+ }
+
+ if (rc == APR_SUCCESS)
+ ctx->shared_block = addr;
+
+ return ctx;
+#endif
+ return NULL;
+}
+
+#if 0
+static const char* set_socket(cmd_parms* cmd, void* config, const char* val)
+{
+ struct sockaddr_any sany;
+
+ if (sock_any_pton_n (val, &sany, 1, DEFAULT_PORT | SANY_OPT_NORESOLV) == -1)
+ return "Invalid socket name or ip in HttpAuthSocket";
+
+ ((httpauth_context_t*)config)->socketname = val;
+ return NULL;
+}
+
+static const char* set_handler(cmd_parms* cmd, void* config, const char* val)
+{
+ httpauth_context_t* conf = (httpauth_context_t*)config;
+ conf->handler = val;
+ return NULL;
+}
+
+static const char* set_types(cmd_parms* cmd, void* config, const char* val)
+{
+ httpauth_context_t* conf = (httpauth_context_t*)config;
+ int type = 0;
+
+ if(strcasecmp(val, AUTH_PREFIX_BASIC) == 0)
+ type = AUTH_TYPE_BASIC;
+ else if(strcasecmp(val, AUTH_PREFIX_DIGEST) == 0)
+ type = AUTH_TYPE_DIGEST;
+ else if(strcasecmp(val, AUTH_PREFIX_NTLM) == 0)
+ type = AUTH_TYPE_NTLM;
+ else if(strcasecmp(val, "any"))
+ type = AUTH_TYPE_ANY;
+ else
+ return "Invalid type in HttpAuthTypes";
+
+ if(conf->types == 0xFFFFFFFF)
+ conf->types = type;
+ else
+ conf->types |= type;
+
+ return NULL;
+}
+
+static const char* set_domain(cmd_parms* cmd, void* config, const char* val)
+{
+ httpauth_context_t* conf = (httpauth_context_t*)config;
+ conf->domain = trim_space(apr_pstrdup(cmd->pool, val));
+ return NULL;
+}
+#endif
+
+static const command_rec command_table[] =
+{
+#if 0
+ AP_INIT_RAW_ARGS( "HttpAuthSocket", set_socket, NULL, OR_AUTHCFG,
+ "The socket that httpauthd is listening on" ),
+ AP_INIT_TAKE1( "HttpAuthHandler", set_handler, NULL, OR_AUTHCFG,
+ "The handler that httpauthd should use to authenticate" ),
+ AP_INIT_ITERATE( "HttpAuthTypes", set_types, NULL, OR_AUTHCFG,
+ "The types of authentiction allowed (Basic, Digest, NTLM ...)" ),
+ AP_INIT_RAW_ARGS( "HttpAuthDigestDomain", set_domain, NULL, OR_AUTHCFG,
+ "The domain for which digest authentication is relevant" ),
+#endif
+ { NULL }
+};
+
+#if 0
+
+/* -------------------------------------------------------------------------------
+ * Socket handling code
+ */
+
+static apr_status_t cleanup_socket(void *fdv)
+{
+ close((int)(long)fdv);
+ return OK;
+}
+
+void disconnect_socket(httpauth_context_t* ctx, server_rec* s)
+{
+ if(ctx->socket != -1)
+ {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "httpauth: disconnecting from daemon");
+
+ apr_pool_cleanup_kill(ctx->child_pool, (void*)(long)ctx->socket,
+ cleanup_socket);
+ close(ctx->socket);
+ ctx->socket = -1;
+
+ /* Make sure we send our list of groups to daemon again */
+ if (ctx->needed_groups)
+ ctx->needed_groups[0] = 0;
+ }
+}
+
+void read_junk(httpauth_context_t* ctx, request_rec* r)
+{
+ char buf[16];
+ const char* t;
+ int said = 0;
+ int l;
+
+ if(ctx->socket == -1)
+ return;
+
+ /* Make it non blocking */
+ fcntl(ctx->socket, F_SETFL, fcntl(ctx->socket, F_GETFL, 0) | O_NONBLOCK);
+
+ for(;;)
+ {
+ l = read(ctx->socket, buf, sizeof(buf) - 1);
+ if(l <= 0)
+ break;
+
+ buf[l] = 0;
+ t = trim_start(buf);
+
+ if(!said && *t)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "httpauth: received junk data from daemon");
+ said = 1;
+ }
+ }
+
+ fcntl(ctx->socket, F_SETFL, fcntl(ctx->socket, F_GETFL, 0) & ~O_NONBLOCK);
+}
+
+int read_line(httpauth_context_t* ctx, request_rec* r, char** line)
+{
+ int l;
+ int al = 256;
+ char* t;
+ const char* e;
+
+ e = t = NULL;
+ *line = NULL;
+
+ for(;;)
+ {
+ if(!*line || t + 2 == e)
+ {
+ char* n;
+ int d;
+
+ n = (char*)apr_palloc(r->pool, al * 2);
+
+ if(*line)
+ memcpy(n, *line, al);
+
+ al *= 2;
+
+ /* The difference */
+ d = t - *line;
+
+ *line = n;
+ t = n + d;
+ e = n + al;
+ }
+
+ l = read(ctx->socket, (void*)t, sizeof(char));
+
+ /* We got a character */
+ if(l == 1)
+ {
+ /* Skip junky CRLFs */
+ if(*t == '\r')
+ {
+ *t = ' ';
+ continue;
+ }
+
+ /* End of line */
+ else if(*t == '\n')
+ {
+ t++;
+ break;
+ }
+
+ t++;
+ }
+
+ /* If it's the end of file then return that */
+ else if(l == 0)
+ {
+ /* Disconnect from socket quietly so we can reconnect later */
+ disconnect_socket(ctx, r->server);
+ return -1;
+ }
+
+ /* Transient errors */
+ else if(l == -1 && errno == EAGAIN)
+ continue;
+
+ /* Fatal errors */
+ else if(l == -1)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(errno), r,
+ "httpauth: couldn't read data from daemon");
+ return -1;
+ }
+ }
+
+ *t = 0;
+ return 0;
+}
+
+int
+read_response (httpauth_context_t *ctx, request_rec *r,
+ int *code, int *ccode, char **details,
+ int return_errors)
+{
+ int c, ret = -1;
+ char *line;
+ char *t;
+ char *t2;
+
+ if (read_line (ctx, r, &line) == -1)
+ return -1;
+
+ line = trim_space (line);
+
+ ap_log_rerror (APLOG_MARK, APLOG_DEBUG, 0, r,
+ "httpauth: received response line from daemon: %s", line);
+
+ /* Get response code */
+ t = ap_getword_nc (r->pool, &line, ' ');
+ c = strtol (t, &t2, 10);
+ if (*t2 || c < 100 || c > 999) {
+ ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
+ "httpauth: protocol error: invalid code: %s", t);
+ goto finally;
+ }
+
+ if (code)
+ *code = c;
+
+ if (c >= 400 && !return_errors) {
+ ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
+ "httpauth: received error from httpauthd: %d %s", c, line);
+ goto finally;
+ }
+
+ /* Get the second response code if we're a 200 */
+ if (c == 200) {
+ t = ap_getword_nc (r->pool, &line, ' ');
+ c = strtol (t, &t2, 10);
+ if (*t2 || c < 100 || c > 999) {
+ ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
+ "httpauth: protocol error: invalid code: %s", t);
+ goto finally;
+ }
+
+ if (ccode)
+ *ccode = c;
+ }
+
+ if (details)
+ *details = trim_space (line);
+
+ ret = 0;
+
+finally:
+ if (ret < 0 && ctx->socket >= 0) {
+ disconnect_socket (ctx, r->server);
+ ++ctx->address_seed;
+ }
+
+ return ret;
+}
+
+static int
+read_process_headers(httpauth_context_t* ctx, int ccode,
+ request_rec* r, char **groups)
+{
+ char* line;
+ const char* name;
+ apr_table_t* headers;
+ int c = 0;
+
+ if(ccode > 299)
+ headers = r->err_headers_out;
+ else
+ headers = r->headers_out;
+
+ for(;;)
+ {
+ if(read_line(ctx, r, &line) == -1)
+ return -1;
+
+ /* If that's it then break */
+ if(!*line)
+ break;
+
+ if(apr_isspace(*line))
+ {
+ line = (char*)trim_start(line);
+
+ /* End of headers */
+ if(!*line)
+ break;
+
+ if(c > 0)
+ {
+ /*
+ * TODO: We really should be supporting headers split
+ * across lines. But httpauthd doesn't currently produce
+ * headers like that, so we don't need to care about it.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "httpauth: protocol error: server sent us an split header, which we don't support.");
+ }
+ else
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "httpauth: protocol error: invalid headers.");
+ }
+ }
+
+ name = ap_getword_nc(r->pool, &line, ':');
+ if(!name || !*name)
+ break;
+
+ /*
+ * If that was the end of the line, then it's an
+ * invalid header :(
+ */
+ if(!*line)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "httpauth: protocol header: invalid headers");
+ return -1;
+ }
+
+ line = trim_space(line);
+
+ if(strcasecmp(name, "WWW-Authenticate") == 0)
+ {
+ if(strncasecmp(line, AUTH_PREFIX_BASIC, strlen(AUTH_PREFIX_BASIC)) == 0 &&
+ !(ctx->types & AUTH_TYPE_BASIC))
+ continue;
+
+ else if(strncasecmp(line, AUTH_PREFIX_DIGEST, strlen(AUTH_PREFIX_DIGEST)) == 0 &&
+ !(ctx->types & AUTH_TYPE_DIGEST))
+ continue;
+
+ /* Only allow unknown if we don't have it */
+ else if(!(ctx->types & AUTH_TYPE_ANY))
+ continue;
+
+ /* Fix up when we're a proxy */
+ if(r->proxyreq == PROXYREQ_PROXY)
+ name = "Proxy-Authenticate";
+ }
+
+ else if(strcasecmp(name, "Authentication-Info") == 0)
+ {
+ if(r->proxyreq == PROXYREQ_PROXY)
+ name = "Proxy-Authentication-Info";
+ }
+
+ else if (strcasecmp(name, "X-HttpAuth-Groups") == 0)
+ {
+ if (groups && line)
+ *groups = line;
+ }
+
+ c++;
+ apr_table_addn(headers, name, line);
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "httpauth: received %d headers from daemon", c);
+
+ return 0;
+}
+
+int write_data(httpauth_context_t* ctx, server_rec* s, const char* data)
+{
+ int r;
+
+ if(ctx->socket == -1)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+ "httpauth: Socket to httpauthd daemon closed. Can't write data.");
+ return -1;
+ }
+
+ while(*data != 0)
+ {
+ r = write(ctx->socket, data, strlen(data));
+
+ if(r > 0)
+ data += r;
+
+ else if(r == -1)
+ {
+ if(errno == EAGAIN)
+ continue;
+
+ /* The other end closed. no message */
+ if(errno == EPIPE)
+ disconnect_socket(ctx, s);
+
+ else
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(errno), s,
+ "httpauth: Couldn't write data to daemon");
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+try_connect_socket (httpauth_context_t *ctx, struct sockaddr_any *sany,
+ request_rec *r)
+{
+ char peername[256];
+ int rc;
+
+ if (sock_any_ntop (sany, peername, sizeof (peername), 0) < 0)
+ strcpy (peername, "[unknown]");
+
+ ctx->socket = socket (SANY_TYPE (*sany), SOCK_STREAM, 0);
+ if(ctx->socket == -1) {
+ ap_log_rerror (APLOG_MARK, APLOG_CRIT, APR_FROM_OS_ERROR (errno), r,
+ "httpauth: Can't create socket");
+ return -1;
+ }
+
+ if (connect (ctx->socket, &SANY_ADDR (*sany), SANY_LEN(*sany)) != 0) {
+ rc = APR_FROM_OS_ERROR (errno);
+ ap_log_rerror (APLOG_MARK, APLOG_CRIT, rc, r,
+ "httpauth: Can't connect to httpauthd at: %s", peername);
+ close (ctx->socket);
+ ctx->socket = -1;
+ return -1;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "httpauth: connected to daemon: %s", peername);
+
+ return 0;
+}
+
+static int
+connect_socket(httpauth_context_t* ctx, request_rec* r)
+{
+ httpauth_shared_t shared;
+ struct sockaddr_any sany[16];
+ int i, which, count = 0;
+ int rc = -1;
+
+ disconnect_socket(ctx, r->server);
+ memset (&shared, 0, sizeof (shared));
+
+ /* Find out what everyone else is connected to */
+ if (shared_get_if_changed (ctx, ctx->shared_version, &shared) && shared.version > 0) {
+ ap_log_rerror (APLOG_MARK, APLOG_DEBUG, 0, r,
+ "httpauth: trying shared address...");
+ rc = try_connect_socket (ctx, &shared.address, r);
+ }
+
+ /* Now try to connect to all the other addresses */
+ if (rc < 0) {
+ ap_log_rerror (APLOG_MARK, APLOG_DEBUG, 0, r,
+ "httpauth: resolving daemon address(s)");
+
+ count = sock_any_pton_n (ctx->socketname, sany, 16, DEFAULT_PORT);
+ if (count < 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
+ "httpauth: Invalid socket name or ip: %s", ctx->socketname);
+ rc = -1;
+ }
+
+ /* We know how many addresses we have to retry with */
+ if (count > 0)
+ ctx->retries = count;
+
+ for (i = 0; i != count; ++i) {
+ which = (i + ctx->address_seed) % count;
+ rc = try_connect_socket (ctx, &sany[which], r);
+
+ /* Successful, then let others know we're connected here */
+ if (rc >= 0) {
+ memcpy (&shared.address, &sany[which], sizeof (shared.address));
+ break;
+ }
+ }
+ }
+
+ /* Yay, successful */
+ if (rc >= 0) {
+ shared_set_if_changed (ctx, &shared);
+ ctx->shared_version = shared.version;
+ apr_pool_cleanup_register(ctx->child_pool, (void*)(long)ctx->socket,
+ cleanup_socket, cleanup_socket);
+ errno = 0;
+ }
+
+ return rc;
+}
+
+int connect_httpauth(httpauth_context_t* ctx, request_rec* r)
+{
+ int ret = -1;
+ int code;
+ char* details;
+ const char* t;
+
+ if(connect_socket(ctx, r) == -1)
+ goto finally;
+
+ if(read_response(ctx, r, &code, NULL, &details, 0) == -1)
+ goto finally;
+
+ if(code != 100)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "httpauth: protocol error (Expected 100, got %d)", code);
+ goto finally;
+ }
+
+ /* Check theversion number */
+ details = trim_space(details);
+
+ if(strcmp(details, "HTTPAUTH/1.0") != 0)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "httpauth: Daemon speaking incompatible protocol version: %s", details);
+ goto finally;
+ }
+
+ /* Send our handler */
+ if(ctx->handler)
+ {
+ t = apr_pstrcat(r->pool, "SET Handler ", ctx->handler, "\n", NULL);
+
+ if(write_data(ctx, r->server, t) == -1)
+ goto finally;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "httpauth: sent handler to daemon: %s", t);
+
+ if(read_response(ctx, r, &code, NULL, NULL, 0) == -1)
+ goto finally;
+
+ if(code != 202)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "httpauth: protocol error: couldn't send handler to daemon (Expected 202, got %d)", code);
+ goto finally;
+ }
+ }
+
+ /* Send any setup info we have */
+ if(ctx->domain)
+ {
+ t = apr_pstrcat(r->pool, "SET Domain ", ctx->domain, "\n", NULL);
+
+ if(write_data(ctx, r->server, t) == -1)
+ goto finally;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "httpauth: sent domains to daemon: %s", t);
+
+ if(read_response(ctx, r, &code, NULL, NULL, 0) == -1)
+ goto finally;
+
+ if(code != 202)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "httpauth: protocol error: couldn't send domain to daemon (Expected 202, got %d)", code);
+ goto finally;
+ }
+ }
+
+ /* We're cool! */
+ ret = 0;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "httpauth: handshake with daemon completed");
+
+finally:
+ if(ret == -1 && ctx->socket >= 0) {
+ disconnect_socket(ctx, r->server);
+ ++ctx->address_seed;
+ }
+
+ return ret;
+}
+
+/* Make sure our connection identifier is unique */
+static apr_status_t connection_gone (void *data)
+{
+ conn_current = NULL;
+ conn_seen++;
+ return APR_SUCCESS;
+}
+
+int write_request(httpauth_context_t* ctx, request_rec* r)
+{
+ char pidid[40];
+ char connid[40];
+ int i, c = 0;
+ const char* t;
+ const apr_array_header_t* hdrs_arr;
+ const apr_table_entry_t* elts;
+
+ /* When the connection goes away, call our handler */
+ if(conn_current != r->connection)
+ {
+ conn_current = r->connection;
+ apr_pool_cleanup_register(r->connection->pool, r,
+ connection_gone, apr_pool_cleanup_null);
+ }
+
+ /* A unique per connection id */
+ snprintf(connid, sizeof(connid), "0x%X-%X-%X",
+ (unsigned int)r->connection, conn_seen, (unsigned int)r->connection->id);
+ connid[sizeof(connid) - 1] = 0;
+ snprintf(pidid, sizeof(pidid), "%d", (unsigned int)getpid());
+ pidid[sizeof(pidid) - 1] = 0;
+ t = apr_pstrcat(r->pool, pidid, ":", connid, NULL);
+
+ /* Send the request header to httpauthd */
+ t = apr_pstrcat(r->pool, "AUTH ", t, " ", r->method,
+ " ", r->unparsed_uri, "\n", NULL);
+
+ if(write_data(ctx, r->server, t) == -1)
+ return -1;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "httpauth: sent auth request to daemon: %s", t);
+
+ /* Now send the headers to httpauthd */
+
+ hdrs_arr = apr_table_elts(r->headers_in);
+ elts = (const apr_table_entry_t*)hdrs_arr->elts;
+
+ for(i = 0; i < hdrs_arr->nelts; i++)
+ {
+ if(!elts[i].val)
+ continue;
+
+ /* Filter out headers we don't want */
+ if(strcasecmp(elts[i].key, r->proxyreq == PROXYREQ_PROXY ?
+ "Proxy-Authorization" : "Authorization") == 0)
+ {
+ t = trim_start(elts[i].val);
+
+ if(strncasecmp(t, AUTH_PREFIX_BASIC, strlen(AUTH_PREFIX_BASIC)) == 0 &&
+ !(ctx->types & AUTH_TYPE_BASIC))
+ continue;
+
+ else if(strncasecmp(t, AUTH_PREFIX_DIGEST, strlen(AUTH_PREFIX_DIGEST)) == 0 &&
+ !(ctx->types & AUTH_TYPE_DIGEST))
+ continue;
+
+ else if(strncasecmp(t, AUTH_PREFIX_NTLM, strlen(AUTH_PREFIX_NTLM)) == 0 &&
+ !(ctx->types & AUTH_TYPE_NTLM))
+ continue;
+
+ /* Only allow unknown if we don't have it */
+ else if(!(ctx->types & AUTH_TYPE_ANY))
+ continue;
+
+ /* Extra blank line when at end */
+ t = apr_pstrcat(r->pool, "Authorization: ", elts[i].val, "\n", NULL);
+
+ if(write_data(ctx, r->server, t) == -1)
+ return HTTP_INTERNAL_SERVER_ERROR;
+
+ c++;
+ }
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "httpauth: sent %d headers to daemon", c);
+
+ return write_data(ctx, r->server, "\n");
+}
+
+static int
+write_needed_groups(httpauth_context_t *ctx, request_rec *r)
+{
+ const apr_array_header_t *reqs_arr = ap_requires(r);
+ require_line *reqs;
+ const char *groups = NULL;
+ const char *text;
+ char *word;
+ register int x;
+ int m = r->method_number;
+ int code, len;
+
+ if(reqs_arr) {
+ reqs = (require_line*)reqs_arr->elts;
+ for (x = 0; x < reqs_arr->nelts; x++) {
+ if (!(reqs[x].method_mask & (AP_METHOD_BIT << m)))
+ continue;
+
+ text = reqs[x].requirement;
+ word = ap_getword_white (r->pool, &text);
+
+ /* Append all groups to the string */
+ if (strcmp (word, "group") == 0 && text && text[0]) {
+ if (!groups)
+ groups = text;
+ else
+ groups = apr_pstrcat (r->pool, text, " ", groups, NULL);
+ }
+ }
+ }
+
+ /* No groups, no need to send */
+ if (!groups && !ctx->needed_groups)
+ return 0;
+
+ if (!groups)
+ groups = "";
+
+ /* Equal groups, no need to send */
+ if (ctx->needed_groups && strcmp (groups, ctx->needed_groups) == 0)
+ return 0;
+
+ /* Groups changed, send to daemon */
+ text = apr_pstrcat (r->pool, "SET Groups ", groups, "\n", NULL);
+
+ if (write_data (ctx, r->server, text) < 0)
+ return -1;
+
+ ap_log_rerror (APLOG_MARK, APLOG_DEBUG, 0, r,
+ "httpauth: sent groups to daemon: %s", text);
+
+ if (read_response (ctx, r, &code, NULL, NULL, 1) < 0)
+ return -1;
+
+ if (code != 202) {
+ ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
+ "httpauth: couldn't send groups to daemon (Expected 202, got %d)", code);
+ /* Older versions of the daemon did not support the 'SET Groups' command */
+ if (code != 400)
+ return -1;
+ }
+
+ /* Save away the groups for next time */
+ len = strlen (groups);
+ if (len >= ctx->alloced_groups) {
+ if (len < 512)
+ len = 512;
+ ctx->needed_groups = (char*)apr_pcalloc (ctx->child_pool, len * 2);
+ ctx->alloced_groups = len * 2;
+ }
+ strcpy (ctx->needed_groups, groups);
+ return 0;
+}
+
+static httpauth_request_t*
+setup_request_hreq (request_rec *r, char *user, char *groups)
+{
+ httpauth_request_t* hreq;
+
+ hreq = (httpauth_request_t*)apr_pcalloc (r->pool, sizeof (*hreq));
+ hreq->user = r->user;
+ hreq->groups = groups;
+
+ if (groups)
+ apr_table_setn (r->subprocess_env, "HTTPAUTH_GROUPS", groups);
+ else
+ apr_table_unset (r->subprocess_env, "HTTPAUTH_GROUPS");
+
+ ap_set_module_config (r->request_config, &httpauth_module, hreq);
+
+ return hreq;
+}
+
+#endif
+
+typedef struct session_info {
+ const char *identifier;
+ time_t expiry;
+} session_info_t;
+
+static const char*
+session_cookie_value (request_rec *r, const char *name)
+{
+ const char *cookies;
+ const char *value;
+ char *pair;
+
+ cookies = apr_table_get (r->headers_in, "Cookie");
+ if (cookies == NULL)
+ return NULL;
+
+ while (*cookies) {
+ pair = ap_get_token (r->pool, &cookies, 0);
+ if (!pair)
+ break;
+ if (pair[0] == '$')
+ continue;
+
+ value = ap_stripprefix (pair, name);
+ if (value == pair)
+ continue;
+ while (isspace (value))
+ ++value;
+
+ if (value != '=')
+ continue;
+ while (isspace (value))
+ ++value;
+
+ return value;
+ }
+
+ return NULL;
+}
+
+static char*
+session_create_sig (apr_pool_t *p, const char *value)
+{
+ unsigned char digest[APR_SHA1_DIGESTSIZE];
+ char *sig;
+
+ apr_sha1_ctx_t ctx;
+ apr_sha1_init (&ctx);
+ apr_sha1_update (&ctx, session_secret, strlen (session_secret));
+ apr_sha1_update (&ctx, "\0", 1);
+ apr_sha1_update (&ctx, value, strlen (value));
+ apr_sha1_final (digest, &ctx);
+
+ sig = apr_pcalloc (p, apr_base64_encode_len (digest));
+ apr_base64_encode (sig, digest, sizeof (digest));
+ return sig;
+}
+
+static int
+session_validate_sig (apr_pool_t *p, const char *sig, const char *value)
+{
+ char *valid = session_create_sig (p, value);
+ return strcmp (sig, valid) == 0;
+}
+
+static session_info_t*
+session_load_info (request_rec *r)
+{
+ session_info_t *sess;
+ const char *value;
+ char *token, *sig;
+ const char *t;
+ long expiry;
+
+ value = session_cookie_value (r, "mod-auth-single-id");
+ if (!value)
+ return NULL;
+
+ sig = ap_get_token (r->pool, &value, 1);
+
+ /* The version of the session info, only 1 supported for now */
+ version = ap_get_token (r->pool, &value, 1);
+ if (strcmp(version, "1") != 0)
+ return NULL;
+
+ if (!session_validate_sig (r->pool, sig, value))
+ return NULL;
+
+ token = ap_get_token (r->pool, &value, 1);
+ expiry = strtol (token, &t, 10);
+ if (*t != '\0')
+ return NULL;
+
+ /* Don't let expired sessions be valid */
+ if (expiry < time (NULL))
+ return NULL;
+
+ /* The identifier */
+ identifier = ap_get_token (r->pool, &value, 1);
+ if (!ap_is_url (identifier))
+ return NULL;
+
+ sess = apr_pcalloc (r->pool, sizeof (session_info_t));
+ sess->expiry = expiry;
+ sess->identifier = identifier;
+
+ return sess;
+}
+
+static void
+session_send_info (request_rec *r, session_info_t *sess)
+{
+ char *cookie, *sig, *value;
+
+ /* Create the cookie value and sign it */
+ value = apr_psprintf (r->pool, "1 %d \"%s\"", sess->expiry, ap_escape_quotes (sess->identifier));
+ sig = session_create_sig (r->pool, value);
+
+ /* Build up the full cookie spec */
+ cookie = apr_psprintf (r->pool, "mod-auth-single-id=%s %s; httponly; max-age=86400", sig, value);
+ apr_table_addn (r->headers_out, "Set-Cookie", cookie);
+}
+
+static session_info_t*
+session_copy_info (apr_pool_t *p, session_info_t *sess)
+{
+ session_info_t *copy = apr_pmalloc (p, sizeof (*sess));
+ copy->expiry = sess->expiry;
+ copy->identifier = apr_pstrdup (sess->identifier);
+ return copy;
+}
+
+static void
+set_request_authenticated (request_rec *r, session_info_t *sess)
+{
+ r->user = sess->identifier;
+ r->ap_auth_type = SINGLEID_AUTHTYPE;
+ ap_set_module_config (r->request_config, &auth_singleid_module, sess);
+}
+
+singleid_request_xxxx
+{
+
+}
+
+static int
+hook_authenticate (request_rec* r)
+{
+ session_info_t *sess;
+#if 0
+ httpauth_context_t* ctx;
+ httpauth_request_t* hreq;
+#endif
+ const char* authtype;
+#if 0
+ int code = 0;
+ int ccode = 0;
+ char *groups = NULL;
+ char* details = NULL;
+#endif
+ request_rec* mainreq;
+#if 0
+ int retried = 0;
+#endif
+
+ /* Make sure it's for us */
+ if (!(authtype = ap_auth_type (r)) || strcasecmp (SINGLEID_AUTHTYPE, authtype) != 0)
+ return DECLINED;
+
+#if 0
+ ctx = (httpauth_context_t*)ap_get_module_config(r->per_dir_config, &httpauth_module);
+
+ if(!ctx->socketname || !ctx->handler)
+ return DECLINED;
+#endif
+
+ mainreq = r;
+
+ while (mainreq->main != NULL)
+ mainreq = mainreq->main;
+
+ while (mainreq->prev != NULL)
+ mainreq = mainreq->prev;
+
+ /* Check if we've already authenticated this request */
+ sess = ap_get_module_config (mainreq->request_config, &auth_singleid_module);
+ if (sess != NULL) {
+ if (mainreq != r) {
+ sess = session_copy_info (r->pool, sess);
+ set_request_authenticated (r, sess);
+ }
+ return OK;
+ }
+
+ /* Load the session info from the request and see if we've authenticated */
+ sess = session_load_info (r);
+ if (sess != NULL) {
+ set_request_authenticated (r, sess);
+ return OK;
+ }
+
+ singleid_consumer (r)
+ Consumer consumer(uri, )
+ opkele::params_t params;
+ parse_query_string(r, params);
+
+ /* Is it an openid response? */
+ if ()
+
+ query = openid_parse_response (r);
+ if (query != NULL) {
+ (r, )
+
+
+ }
+
+ /* Otherwise start a new openid authentication */
+
+
+#if 0
+ /*
+ * Check if we're in sync with the other processes,
+ * and connected to the same daemon
+ */
+ if (ctx->socket != -1 && shared_get_if_changed (ctx, ctx->shared_version, NULL)) {
+ ap_log_rerror (APLOG_MARK, APLOG_INFO, 0, r,
+ "httpauth: syncing connection with other processes");
+ disconnect_socket (ctx, r->server);
+ }
+
+/* For jumping to when a connection has been closed */
+retry:
+
+ if (ctx->socket == -1) {
+ if (connect_httpauth (ctx, r) == -1) {
+
+ if (ctx->socket == -1 && retried < ctx->retries) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "httpauth: trying to connect to daemon again");
+ ++retried;
+ goto retry;
+ }
+
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ /* Make sure we're starting on a clean slate */
+ read_junk (ctx, r);
+
+
+
+ /* Send off a request, along with groups, and read response */
+ if (write_needed_groups (ctx, r) == -1 ||
+ write_request (ctx, r) == -1 ||
+ read_response (ctx, r, &code, &ccode, &details, 0) == -1) {
+
+ /*
+ * If our connection was closed by httpauthd then this
+ * is where we get the error. Just do one retry to
+ * try and reconnect. This happens often when restarting
+ * httpauthd.
+ */
+
+ if (ctx->socket == -1 && retried < ctx->retries) {
+ ap_log_rerror (APLOG_MARK, APLOG_WARNING, 0, r,
+ "httpauth: reconnecting to to httpauthd");
+ ++retried;
+ goto retry;
+ }
+
+ ap_log_rerror (APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(errno), r,
+ "httpauth: couldn't send request to httpauthd");
+
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if(code != 200)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "httpauth: protocol error: unexpected code while authenticating: %d", code);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* Copy over other headers */
+ if(read_process_headers(ctx, ccode, r, &groups) == -1)
+ return HTTP_INTERNAL_SERVER_ERROR;
+
+ if (ccode == 200) {
+ ap_log_rerror (APLOG_MARK, APLOG_INFO, 0, r,
+ "httpauth: successful authentication for user: %s", details);
+
+ r->user = apr_pstrdup (r->pool, details);
+ r->ap_auth_type = HTTPAUTH_AUTHTYPE;
+
+ /* Mark request as successfully authenticated */
+ hreq = setup_request_hreq (r, details, groups);
+ return OK;
+ }
+
+ return ccode;
+}
+
+#endif
+#if 0
+
+static const char*
+find_word_quoted (const char *all, const char *word)
+{
+ const char *at;
+ char before, after;
+ size_t len = strlen (word);
+
+ at = all;
+ for (;;) {
+ at = strstr (at, word);
+ if (!at)
+ return NULL;
+
+ before = (at == all) ? 0 : *(at - 1);
+ after = *(at + len);
+
+ /* Beginning and end of a space delimited word */
+ if ((!before || isspace (before)) &&
+ (!after || isspace (after))) {
+ return at;
+ }
+
+ /* Beginning and end of a quoted word */
+ if ((before == '"' || before == '\'') && after == before)
+ return at;
+
+ at += len;
+ }
+}
+
+static int
+httpauth_access(request_rec *r)
+{
+ httpauth_context_t *ctx;
+ httpauth_request_t *hreq;
+ const char* authtype;
+ char *user = r->user;
+ int m = r->method_number;
+ int method_restricted = 0;
+ register int x;
+ const char *text, *word;
+ const apr_array_header_t *reqs_arr = ap_requires (r);
+ require_line *reqs;
+
+ /* Make sure it's for us */
+ if (!(authtype = ap_auth_type (r)) || strcasecmp (HTTPAUTH_AUTHTYPE, authtype) != 0)
+ return DECLINED;
+
+ if (!reqs_arr)
+ return OK;
+
+ /* Dig out our configuration */
+ ctx = ap_get_module_config (r->per_dir_config, &httpauth_module);
+ hreq = ap_get_module_config (r->request_config, &httpauth_module);
+ reqs = (require_line *)reqs_arr->elts;
+
+ for (x = 0; x < reqs_arr->nelts; x++) {
+ if (!(reqs[x].method_mask & (AP_METHOD_BIT << m)))
+ continue;
+
+ method_restricted = 1;
+
+ text = reqs[x].requirement;
+ word = ap_getword_white(r->pool, &text);
+
+ /* Any valid user */
+ if (strcmp (word, "valid-user") == 0) {
+ return OK;
+
+ /* Specific listed users */
+ } else if (strcmp (word, "user") == 0) {
+ while (text[0]) {
+ word = ap_getword_conf (r->pool, &text);
+ if (strcmp (user, word) == 0) {
+ return OK;
+ }
+ }
+
+ /* Specific groups */
+ } else if (strcmp (word, "group") == 0) {
+ if (hreq && hreq->groups) {
+ while (text[0]) {
+ word = ap_getword_conf (r->pool, &text);
+ if (find_word_quoted (hreq->groups, word))
+ return OK;
+ }
+ }
+
+ /* What is this? */
+ } else {
+ ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
+ "access to %s failed, reason: unknown require "
+ "directive:\"%s\"", r->uri, reqs[x].requirement);
+ }
+ }
+
+ if (!method_restricted)
+ return OK;
+
+ ap_log_rerror (APLOG_MARK, APLOG_ERR, 0, r,
+ "access to %s failed, reason: user %s not allowed access",
+ r->uri, user);
+ return HTTP_UNAUTHORIZED;
+}
+#endif
+
+static void
+register_hooks(apr_pool_t *p)
+{
+ ap_log_perror (APLOG_MARK, APLOG_ERR, 0, p, "mod_auth_singleid registering hooks");
+#if 0
+ ap_hook_post_config (httpauth_initialize, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_child_init (httpauth_child, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_check_user_id (hook_authenticate, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_auth_checker (httpauth_access, NULL, NULL, APR_HOOK_MIDDLE);
+#endif
+}
+
+module AP_MODULE_DECLARE_DATA auth_singleid_module = {
+ STANDARD20_MODULE_STUFF,
+ dir_config_creator, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ command_table, /* command table */
+ register_hooks /* register hooks */
+};
diff --git a/module/storage.c b/module/storage.c
new file mode 100644
index 0000000..3cb9cdd
--- /dev/null
+++ b/module/storage.c
@@ -0,0 +1,200 @@
+
+/* Yes, this looks backwards. */
+typedef char nonce_t[40];
+
+struct singleid_board {
+
+ /* The association with our server */
+ char server[256];
+ char handle[256];
+ unsigned char secret[256];
+ char type[64];
+ size_t n_secret;
+ time_t expires;
+
+ /* Book keeping for the records */
+ nonce_t *records;
+ size_t first;
+ size_t total;
+ size_t count;
+ int wrapped;
+};
+
+int
+singleid_board_store_assoc (singleid_board_t *board, const singleid_assoc_t *assoc)
+{
+ assert (board);
+ assert (assoc);
+
+ /* Check that we have enough space to store this information */
+ if ((assoc->server && strlen (assoc->server) > sizeof (board->server)) ||
+ (assoc->handle && strlen (assoc->handle) > sizeof (board->handle)) ||
+ (assoc->n_secret && assoc->n_secret > sizeof (board->secret)) ||
+ (assoc->type && strlen (assoc->type) > sizeof (board->type)))
+ return 0;
+
+ strcpy (board->server, assoc->server ? assoc->server : "");
+ strcpy (board->handle, assoc->handle ? assoc->handle : "");
+ memcpy (board->secret, assoc->secret, assoc->n_secret);
+ strcpy (board->type, assoc->type ? assoc->type : "");
+ board->expires = assoc->expires;
+ return 1;
+}
+
+int
+singleid_board_find_assoc (singleid_board_t *board, const char *server,
+ const char *handle, singleid_assoc_t *assoc)
+{
+ assert (board);
+ assert (assoc);
+
+ if (server && (strlen (server) > sizeof (board->server) ||
+ strcmp (server, board->server) != 0))
+ return 0;
+
+ if (handle && (strlen (handle) > sizeof (board->handle) ||
+ strcmp (handle, board->handle) != 0))
+ return 0;
+
+ assoc->server = board->server;
+ assoc->handle = board->handle;
+ assoc->type = board->type;
+ assoc->secret = board->secret;
+ assoc->n_secret = board->n_secret;
+ assoc->expires = board->expires;
+ return 1;
+}
+
+void
+singleid_board_invalidate_assoc (singleid_board_t *board, const char *server,
+ const char *handle)
+{
+ singleid_assoc_t dummy;
+ assert (board);
+
+ if (singleid_board_find_assoc (board, server, handle, &dummy)) {
+ board->server[0] = 0;
+ board->handle[0] = 0;
+ board->type[0] = 0;
+ memset (board->secret, 0, sizeof (board->secret));
+ board->n_secret = 0;
+ board->expires = 0;
+ board->secret[0] = 0;
+ }
+}
+
+#define nonce_compare(a, b) \
+ memcmp (a, b, sizeof (nonce_t))
+
+static void
+nonce_put (nonce_t rec, const char *nonce)
+{
+ size_t len = strlen (nonce);
+ char *dst = (char*)rec;
+
+ /* If it's short enough, then just board. Fast */
+ if (len < sizeof (rec)) {
+ memcpy (dst, nonce, len);
+ memset (dst + len, 0, sizeof (rec) - len);
+
+ /* Too long, need to compress into the record */
+ } else {
+ apr_sha1_ctx_t ctx;
+
+ assert (sizeof (nonce_t) == APR_SHA1_DIGESTSIZE + 20);
+ assert (len > 20);
+
+ /* The date prefix we just copy in */
+ memcpy (dst, nonce, 20);
+
+ /* Hash the rest into the buffer */
+ apr_sha1_init (&ctx);
+ apr_sha1_update (&ctx, nonce + 20, len - 20);
+ apr_sha1_final (dst + 20, &ctx);
+ }
+}
+
+static void
+insert_nonce_record (singleid_board_t *board, nonce_t rec, size_t at)
+{
+ nonce_t *records = board->records;
+ size_t from, to, num;
+
+ assert (board->total > 2);
+ assert (at < board->total);
+ assert (at != board->first);
+
+ /* Insertion right after latest, either ancient, more likely top */
+ if (at == board->first + 1 % board->total) {
+ /* We can just copy in at this point */
+
+ /* Our ring has empty space in it, so always push forwards, but only until first */
+ } else if (!board->wrapped) {
+ memmove (records + at + 1, records + at, sizeof (rec) * (board->first - at));
+ board->first += 1;
+
+ /* Move data backwards to make space */
+ } else if (board->first < at) {
+ memmove (records + board->first + 2, records + board->first + 1,
+ sizeof (rec) * (at - board->first));
+
+ /* Move data forwards to make space simply */
+ } else {
+ memmove (records + at + 1, records + at, sizeof (rec) * (board->first - at - 1));
+ }
+
+ memcpy (records[at], rec, sizeof (rec));
+ ++board->count;
+
+ /* Track whether we have a full ring or not. */
+ if (!board->wrapped && board->count > board->total)
+ board->wrapped = 1;
+}
+
+int
+singleid_board_check_nonce (singleid_board_t *board, const char *nonce)
+{
+ nonce_t *records;
+ nonce_t rec;
+ size_t at, lower, upper, mid;
+ int res;
+
+ assert (board);
+ assert (nonce);
+ assert (board->records);
+ assert (board->first < board->total);
+
+ nonce_put (rec, nonce);
+ records = board->records;
+
+ /* Best case scenario, new nonce is higher than our latest one */
+ res = nonce_compare (rec, records[top]);
+
+ /* Was the last nonce */
+ if (res == 0) {
+ return 1;
+
+ /* Newer than anything, push on top */
+ } else if (res < 0) {
+ at = (board->first + 1) % board->total;
+ insert_nonce_record (board, rec, at);
+ board->first = at;
+ return 0;
+ }
+
+ /* Do a binary search for the item */
+ for (lower = 0, upper = board->total; lower < upper; ) {
+ mid = lower + ((upper - lower) / 2); // Note: not (low + high) / 2 !!
+ at = at + board->first % board->total;
+ res = nonce_compare (rec, records[at]);
+ if (res == 0)
+ return 1; /* Have the nonce */
+ else if (res > 0)
+ lower = mid + 1;
+ else
+ upper = mid;
+ }
+
+ insert_nonce_record (board, rec, at);
+ return 0; /* Didn't find nonce */
+}
diff --git a/module/storage.h b/module/storage.h
new file mode 100644
index 0000000..b3a2ad7
--- /dev/null
+++ b/module/storage.h
@@ -0,0 +1,32 @@
+#ifndef BOARD_H_
+#define BOARD_H_
+
+/* Communications white-board between processes/threads */
+
+typedef struct singleid_board singleid_board_t;
+
+typedef struct singleid_assoc {
+ const char *server;
+ const char *handle;
+ const char *type;
+ const unsigned char *secret;
+ const size_t n_secret;
+ time_t expires;
+} singleid_assoc_t;
+
+int singleid_board_check_nonce (singleid_board_t *board,
+ const char *nonce);
+
+int singleid_board_store_assoc (singleid_board_t *board,
+ const singleid_assoc_t *assoc);
+
+int singleid_board_find_assoc (singleid_board_t *board,
+ const char *server,
+ const char *handle,
+ singleid_assoc_t *assoc);
+
+void singleid_board_invalidate_assoc (singleid_board_t *board,
+ const char *server,
+ const char *handle);
+
+#endif /* BOARD_H_ */