diff options
Diffstat (limited to 'module/mod_auth_singleid.c')
-rw-r--r-- | module/mod_auth_singleid.c | 1621 |
1 files changed, 1621 insertions, 0 deletions
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 */ +}; |