From 3dc8cb104d3392e00514bb829c50390a9598fe3c Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Thu, 12 Jun 2008 03:40:09 +0000 Subject: - apache2x module now supports connecting to multiple daemon addresses and failover between them. --- apache2x/mod_httpauth.c | 571 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 422 insertions(+), 149 deletions(-) (limited to 'apache2x/mod_httpauth.c') diff --git a/apache2x/mod_httpauth.c b/apache2x/mod_httpauth.c index d6697ae..58e6f2e 100644 --- a/apache2x/mod_httpauth.c +++ b/apache2x/mod_httpauth.c @@ -41,13 +41,23 @@ #include #include #include +#include #include #include +#include "apr_file_io.h" #include +#include + +/* Apache defines these */ +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION #include -#include "sock_any.h" +#include "sock-any.h" #include "stringx.h" #define DEFAULT_PORT 8020 @@ -60,24 +70,48 @@ static void* conn_current = NULL; /* And increment this when it goes out of scope */ static unsigned int conn_seen = 0; -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; -} -httpauth_context_t; +/* + * 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; + +static apr_global_mutex_t *shared_lock = NULL; +static const char *shared_lock_name = NULL; + /* TODO: Support proxy authentication properly */ #define AUTH_PREFIX_BASIC "Basic" @@ -92,33 +126,197 @@ typedef struct httpauth_request { #define HTTPAUTH_AUTHTYPE "HTTPAUTH" /* ------------------------------------------------------------------------------- - * Configuration code + * Shared memory */ -static void* httpauth_dir_config(apr_pool_t* p, char* dir) +static int +httpauth_initialize (apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { - httpauth_context_t* ctx; + 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, + "httpauth: couldn't get temporary directory"); + + if (rc == APR_SUCCESS) { + lock_name = apr_pstrcat (p, tmpdir, "/", "mod-httpauth.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, + "httpauth: couldn't create temporary file: %s", lock_name); + } + + 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, + "httpauth: couldn't create shared memory lock"); + } + + if (file != NULL) + apr_file_close (file); + shared_lock_name = lock_name; + + return OK; +} + +static void +httpauth_child (apr_pool_t *p, server_rec *s) +{ + apr_status_t rc; + + if (!shared_lock) + 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"); + shared_lock = NULL; + } +} + +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) + return 0; - ctx = (httpauth_context_t*)apr_pcalloc(p, sizeof(*ctx)); - memset(ctx, 0, sizeof(*ctx)); + apr_global_mutex_lock (shared_lock); - ctx->socket = -1; - ctx->types = 0xFFFFFFFF; - ctx->child_pool = p; - ctx->needed_groups = NULL; - ctx->alloced_groups = 0; - return ctx; + 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) + 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); +} + +/* ------------------------------------------------------------------------------- + * Per Directory Config and Context Code + */ + +static void* httpauth_dir_config (apr_pool_t* p, char* dir) +{ + 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; } static const char* set_socket(cmd_parms* cmd, void* config, const char* val) { - struct sockaddr_any sany; + struct sockaddr_any sany; - if(sock_any_pton(val, &sany, DEFAULT_PORT) == -1) - return "Invalid socket name or ip in HttpAuthSocket"; + 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; + ((httpauth_context_t*)config)->socketname = val; + return NULL; } static const char* set_handler(cmd_parms* cmd, void* config, const char* val) @@ -161,7 +359,7 @@ static const char* set_domain(cmd_parms* cmd, void* config, const char* val) static const command_rec httpauth_cmds[] = { - AP_INIT_TAKE1( "HttpAuthSocket", set_socket, NULL, OR_AUTHCFG, + 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" ), @@ -193,6 +391,8 @@ void disconnect_socket(httpauth_context_t* ctx, server_rec* s) 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; } @@ -310,63 +510,68 @@ int read_line(httpauth_context_t* ctx, request_rec* r, char** line) return 0; } -int read_response(httpauth_context_t* ctx, request_rec* r, - int* code, int* ccode, char** details, - int return_errors) +int +read_response (httpauth_context_t *ctx, request_rec *r, + int *code, int *ccode, char **details, + int return_errors) { - int c; - char* line; - char* t; - char* t2; - - if(read_line(ctx, r, &line) == -1) - return -1; - - line = trim_space(line); + 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; + } - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "httpauth: received response line from daemon: %s", line); + if (code) + *code = c; - /* 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); - return -1; - } + 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; + } - if(code) - *code = c; + /* 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(c >= 400 && !return_errors) - { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "httpauth: received error from httpauthd: %d %s", c, line); - return -1; - } + if (ccode) + *ccode = c; + } - /* 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); - return -1; - } + if (details) + *details = trim_space (line); - if(ccode) - *ccode = c; - } + ret = 0; - if(details) - *details = trim_space(line); +finally: + if (ret < 0 && ctx->socket >= 0) { + disconnect_socket (ctx, r->server); + ++ctx->address_seed; + } - return 0; + return ret; } static int @@ -513,46 +718,94 @@ int write_data(httpauth_context_t* ctx, server_rec* s, const char* data) return 0; } -int connect_socket(httpauth_context_t* ctx, request_rec* r) +static int +try_connect_socket (httpauth_context_t *ctx, struct sockaddr_any *sany, + request_rec *r) { - struct sockaddr_any sany; - int ret = -1; + char peername[256]; + int rc; - disconnect_socket(ctx, r->server); + if (sock_any_ntop (sany, peername, sizeof (peername), 0) < 0) + strcpy (peername, "[unknown]"); - if(sock_any_pton(ctx->socketname, &sany, DEFAULT_PORT) == -1) - { - ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, - "httpauth: Invalid socket name or ip."); - goto finally; - } + 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; + } - 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: %s", ctx->socketname); - goto finally; - } + 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; + } - apr_pool_cleanup_register(ctx->child_pool, (void*)(long)ctx->socket, - cleanup_socket, cleanup_socket); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "httpauth: connected to daemon: %s", peername); - if(connect(ctx->socket, &SANY_ADDR(sany), SANY_LEN(sany)) != 0) - { - ap_log_rerror(APLOG_MARK, APLOG_CRIT, APR_FROM_OS_ERROR(errno), r, - "httpauth: Can't connect to httpauthd"); - goto finally; - } + return 0; +} - ret = 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); + } -finally: - if(ret == -1) - disconnect_socket(ctx, r->server); + /* 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; - errno = 0; - return ret; + 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) @@ -631,13 +884,15 @@ int connect_httpauth(httpauth_context_t* ctx, request_rec* r) /* We're cool! */ ret = 0; - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "httpauth: connected to daemon"); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "httpauth: handshake with daemon completed"); finally: - if(ret == -1) - disconnect_socket(ctx, r->server); + if(ret == -1 && ctx->socket >= 0) { + disconnect_socket(ctx, r->server); + ++ctx->address_seed; + } - return ret; + return ret; } /* Make sure our connection identifier is unique */ @@ -811,6 +1066,7 @@ static int httpauth_authenticate(request_rec* r) httpauth_request_t* hreq; const char* authtype; int code = 0; + int ccode = 0; char *groups = NULL; char* details = NULL; @@ -843,43 +1099,60 @@ static int httpauth_authenticate(request_rec* r) return OK; } + /* + * 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) - return HTTP_INTERNAL_SERVER_ERROR; - } + if (ctx->socket == -1) { + if (connect_httpauth (ctx, r) == -1) { - /* Make sure we're starting on a clean slate */ - read_junk(ctx, r); + 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; + } - /* 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. - */ + return HTTP_INTERNAL_SERVER_ERROR; + } + } - if(ctx->socket == -1 && !retried) - { - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "httpauth: reconnecting to to httpauthd"); - retried = 1; - goto retry; - } + /* 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"); + 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; - } + return HTTP_INTERNAL_SERVER_ERROR; + } if(code != 200) { @@ -951,7 +1224,7 @@ httpauth_access(request_rec *r) int m = r->method_number; int method_restricted = 0; register int x; - const char *text, *word, *at; + const char *text, *word; const apr_array_header_t *reqs_arr = ap_requires (r); require_line *reqs; @@ -1018,10 +1291,10 @@ httpauth_access(request_rec *r) static void register_hooks(apr_pool_t *p) { - /* static const char* cfg_post[] = { "http_core.c", NULL }; */ - - ap_hook_check_user_id(httpauth_authenticate, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_auth_checker(httpauth_access, NULL, NULL, APR_HOOK_MIDDLE); + 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 (httpauth_authenticate, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_auth_checker (httpauth_access, NULL, NULL, APR_HOOK_MIDDLE); } module AP_MODULE_DECLARE_DATA httpauth_module = @@ -1029,7 +1302,7 @@ module AP_MODULE_DECLARE_DATA httpauth_module = STANDARD20_MODULE_STUFF, httpauth_dir_config, /* dir config creater */ NULL, /* dir merger --- default is to override */ - NULL, /* server config */ + NULL, /* server config */ NULL, /* merge server config */ httpauth_cmds, /* command table */ register_hooks /* register hooks */ @@ -1040,5 +1313,5 @@ module AP_MODULE_DECLARE_DATA httpauth_module = * object file, at least when statically compiled. * so we include this here */ -#include "../common/sock_any.c" +#include "../common/sock-any.c" #include "../common/stringx.c" -- cgit v1.2.3