From 0cb3f6098d959479a96c26a92d91becc2110b30d Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 11 Jun 2008 21:48:27 +0000 Subject: Support getting groups from the server and limiting access based on LDAP groups. See #112 --- apache2x/mod_httpauth.c | 323 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 233 insertions(+), 90 deletions(-) (limited to 'apache2x') diff --git a/apache2x/mod_httpauth.c b/apache2x/mod_httpauth.c index 3e11750..d6697ae 100644 --- a/apache2x/mod_httpauth.c +++ b/apache2x/mod_httpauth.c @@ -67,10 +67,17 @@ typedef struct httpauth_context int types; const char* handler; const char* domain; + char* needed_groups; + int alloced_groups; apr_pool_t* child_pool; } httpauth_context_t; +typedef struct httpauth_request { + const char *user; + const char *groups; +} httpauth_request_t; + /* TODO: Support proxy authentication properly */ #define AUTH_PREFIX_BASIC "Basic" @@ -98,6 +105,8 @@ static void* httpauth_dir_config(apr_pool_t* p, char* dir) ctx->socket = -1; ctx->types = 0xFFFFFFFF; ctx->child_pool = p; + ctx->needed_groups = NULL; + ctx->alloced_groups = 0; return ctx; } @@ -184,6 +193,8 @@ void disconnect_socket(httpauth_context_t* ctx, server_rec* s) cleanup_socket); close(ctx->socket); ctx->socket = -1; + if (ctx->needed_groups) + ctx->needed_groups[0] = 0; } } @@ -223,7 +234,7 @@ void read_junk(httpauth_context_t* ctx, request_rec* r) int read_line(httpauth_context_t* ctx, request_rec* r, char** line) { int l; - int al = 128; + int al = 256; char* t; const char* e; @@ -300,7 +311,8 @@ int read_line(httpauth_context_t* ctx, request_rec* r, char** line) } int read_response(httpauth_context_t* ctx, request_rec* r, - int* code, int* ccode, char** details) + int* code, int* ccode, char** details, + int return_errors) { int c; char* line; @@ -328,7 +340,7 @@ int read_response(httpauth_context_t* ctx, request_rec* r, if(code) *code = c; - if(c >= 400) + if(c >= 400 && !return_errors) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "httpauth: received error from httpauthd: %d %s", c, line); @@ -357,7 +369,9 @@ int read_response(httpauth_context_t* ctx, request_rec* r, return 0; } -int read_copy_headers(httpauth_context_t* ctx, int ccode, request_rec* r) +static int +read_process_headers(httpauth_context_t* ctx, int ccode, + request_rec* r, char **groups) { char* line; const char* name; @@ -445,6 +459,12 @@ int read_copy_headers(httpauth_context_t* ctx, int ccode, request_rec* r) name = "Proxy-Authentication-Info"; } + else if (strcasecmp(name, "X-HttpAuth-Groups") == 0) + { + if (groups && line) + *groups = line; + } + c++; apr_table_addn(headers, name, line); } @@ -545,7 +565,7 @@ int connect_httpauth(httpauth_context_t* ctx, request_rec* r) if(connect_socket(ctx, r) == -1) goto finally; - if(read_response(ctx, r, &code, NULL, &details) == -1) + if(read_response(ctx, r, &code, NULL, &details, 0) == -1) goto finally; if(code != 100) @@ -576,13 +596,13 @@ int connect_httpauth(httpauth_context_t* ctx, request_rec* r) ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "httpauth: sent handler to daemon: %s", t); - if(read_response(ctx, r, &code, NULL, NULL) == -1) + 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 (Expected 202, got %d)", code); + "httpauth: protocol error: couldn't send handler to daemon (Expected 202, got %d)", code); goto finally; } } @@ -598,13 +618,13 @@ int connect_httpauth(httpauth_context_t* ctx, request_rec* r) ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "httpauth: sent domains to daemon: %s", t); - if(read_response(ctx, r, &code, NULL, NULL) == -1) + 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 (Expected 202, got %d)", code); + "httpauth: protocol error: couldn't send domain to daemon (Expected 202, got %d)", code); goto finally; } } @@ -711,12 +731,88 @@ int write_request(httpauth_context_t* ctx, request_rec* r) 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 int httpauth_authenticate(request_rec* r) { httpauth_context_t* ctx; + httpauth_request_t* hreq; const char* authtype; int code = 0; int ccode = 0; + char *groups = NULL; char* details = NULL; request_rec* mainreq; int retried = 0; @@ -739,14 +835,13 @@ static int httpauth_authenticate(request_rec* r) while(mainreq->prev != NULL) mainreq = mainreq->prev; - /* Check if we've already authenticated this request */ - details = ap_get_module_config(mainreq->request_config, &httpauth_module); - if(details) - { - r->user = apr_pstrdup(r->pool, details); - r->ap_auth_type = HTTPAUTH_AUTHTYPE; - return OK; - } + /* Check if we've already authenticated this request */ + hreq = ap_get_module_config (mainreq->request_config, &httpauth_module); + if (hreq) { + r->user = apr_pstrdup (r->pool, hreq->user); + r->ap_auth_type = HTTPAUTH_AUTHTYPE; + return OK; + } /* For jumping to when a connection has been closed */ retry: @@ -760,9 +855,10 @@ retry: /* Make sure we're starting on a clean slate */ read_junk(ctx, r); - /* Send off a request */ - if(write_request(ctx, r) == -1 || - read_response(ctx, r, &code, &ccode, &details) == -1) + /* 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 @@ -788,89 +884,136 @@ retry: if(code != 200) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "httpauth: protocol error: unexpected code: %d", code); + "httpauth: protocol error: unexpected code while authenticating: %d", code); return HTTP_INTERNAL_SERVER_ERROR; } /* Copy over other headers */ - if(read_copy_headers(ctx, ccode, r) == -1) + 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); + 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; + r->user = apr_pstrdup (r->pool, details); + r->ap_auth_type = HTTPAUTH_AUTHTYPE; - /* Mark request as successfully authenticated */ - ap_set_module_config(r->request_config, &httpauth_module, details); - return OK; - } + hreq = (httpauth_request_t*)apr_pcalloc (r->pool, sizeof (*hreq)); + hreq->user = details; + hreq->groups = groups; - return ccode; + /* Mark request as successfully authenticated */ + ap_set_module_config (r->request_config, &httpauth_module, hreq); + return OK; + } + + return ccode; } -static int httpauth_access(request_rec *r) +static const char* +find_word_quoted (const char *all, const char *word) { - httpauth_context_t* ctx; - const char* authtype; - char *user = r->user; - int m = r->method_number; - int method_restricted = 0; - register int x; - const char *t, *w; - 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; - - ctx = (httpauth_context_t*)ap_get_module_config(r->per_dir_config, - &httpauth_module); - - if (!reqs_arr) - return OK; - 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; - - t = reqs[x].requirement; - w = ap_getword_white(r->pool, &t); - if(!strcmp(w, "valid-user")) - return OK; - else if(!strcmp(w, "user")) - { - while (t[0]) - { - w = ap_getword_conf(r->pool, &t); - if (!strcmp(user, w)) { - return OK; - } - } - } - 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; + 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; + } +} - 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; +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, *at; + 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; } static void register_hooks(apr_pool_t *p) -- cgit v1.2.3