summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2008-06-11 21:48:27 +0000
committerStef Walter <stef@memberwebs.com>2008-06-11 21:48:27 +0000
commit0cb3f6098d959479a96c26a92d91becc2110b30d (patch)
tree22f1447d6c7ad77d802c476297cf9547f822f81a
parent67d7a6cc4d3234ac93e521632701e8d42513e042 (diff)
Support getting groups from the server and limiting access based on LDAP groups. See #112
-rw-r--r--ChangeLog5
-rw-r--r--apache2x/mod_httpauth.c323
-rw-r--r--common/buffer.c4
-rw-r--r--common/compat.c1
-rw-r--r--common/sock_any.c1
-rw-r--r--common/stringx.c201
-rw-r--r--common/stringx.h9
-rw-r--r--configure.in24
-rw-r--r--daemon/bd.c679
-rw-r--r--daemon/bd.h9
-rw-r--r--daemon/digest.c45
-rw-r--r--daemon/digest.h10
-rwxr-xr-xdaemon/dummy.c8
-rw-r--r--daemon/httpauthd.c22
-rw-r--r--daemon/httpauthd.h7
-rw-r--r--daemon/ldap.c98
-rw-r--r--daemon/misc.c6
-rw-r--r--daemon/mysql.c42
-rw-r--r--daemon/ntlmssp.c2
-rw-r--r--daemon/pgsql.c26
-rw-r--r--daemon/simple.c14
-rw-r--r--doc/httpauthd.conf.55
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/unit-test-ntlmssp.c1
-rw-r--r--tests/unit-test-stringx.c105
-rw-r--r--tools/mkha1.c5
26 files changed, 1160 insertions, 498 deletions
diff --git a/ChangeLog b/ChangeLog
index d71c1d1..c35abaf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+0.9.3 ???
+ - Support sending access groups to mod_httpauth apache2x module.
+ - Support retrieving LDAP access groups for users.
+ - Build warning fixes.
+
0.9.2 [22-05-2008]
- Authenticate sub requests properly in the apache module.
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)
diff --git a/common/buffer.c b/common/buffer.c
index 25494c9..088c2df 100644
--- a/common/buffer.c
+++ b/common/buffer.c
@@ -40,6 +40,8 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
/* -----------------------------------------------------------------------
* Memory Buffer
@@ -368,7 +370,7 @@ char* ha_bufparseline(ha_buffer_t* buf, int trim)
if(trim)
{
/* Knock out any previous whitespace */
- while(buf->_pp < buf->_rp && isblank(*(buf->_pp)))
+ while(buf->_pp < buf->_rp && strchr (" \t", *(buf->_pp)))
buf->_pp++;
}
diff --git a/common/compat.c b/common/compat.c
index 081a7ee..3ab7ae2 100644
--- a/common/compat.c
+++ b/common/compat.c
@@ -33,6 +33,7 @@
#include "compat.h"
#include <stdlib.h>
+#include <ctype.h>
#ifndef HAVE_REALLOCF
diff --git a/common/sock_any.c b/common/sock_any.c
index 714154a..5972e62 100644
--- a/common/sock_any.c
+++ b/common/sock_any.c
@@ -44,6 +44,7 @@
#include <netdb.h>
#include <string.h>
#include <stdio.h>
+#include <ctype.h>
#include "sock_any.h"
diff --git a/common/stringx.c b/common/stringx.c
index 2246cf8..24cf9d1 100644
--- a/common/stringx.c
+++ b/common/stringx.c
@@ -36,9 +36,16 @@
*
*/
-#include <string.h>
#include "stringx.h"
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#define WHITESPACE " \t\r\n\v"
+
const char* trim_start(const char* data)
{
while(*data && isspace(*data))
@@ -64,3 +71,195 @@ char* trim_space(char* data)
data = (char*)trim_start(data);
return trim_end(data);
}
+
+static int
+is_escaped (const char *string, const char *at)
+{
+ int escaped = 0;
+ while (at > string) {
+ at = at - 1;
+ if (*at != '\\')
+ break;
+ escaped = !escaped;
+ }
+ return escaped;
+}
+
+void
+str_unescape (char *str)
+{
+ int len = strlen (str);
+ char *at;
+
+ while (len > 0) {
+ at = strchr (str, '\\');
+ if (!at)
+ break;
+
+ len -= at - str;
+ str = at + 1;
+ --len;
+ memmove (at, str, len);
+ at[len] = 0;
+ }
+}
+
+char**
+str_array_parse_quoted (const char *data)
+{
+ char **array;
+ const char *at;
+ char quote;
+ int n;
+
+ assert (data);
+
+ array = str_array_create (NULL);
+
+ for (n = 0; 1; ++n) {
+ quote = 0;
+
+ /* Strip all leading blanks */
+ while (data && data[0] && strchr (WHITESPACE, data[0]))
+ ++data;
+
+ if (!data || !data[0])
+ break;
+
+ /* See if the next character is a quote */
+ if (data[0] == '\'' || data[0] == '\"') {
+ quote = data[0];
+ ++data;
+ }
+
+ if (quote) {
+ at = data;
+ do {
+ ++data;
+ data = strchr (data, quote);
+ } while (data && is_escaped (at, data));
+
+ if (!data) {
+ array = str_array_append (array, at);
+ } else {
+ array = str_array_appendn (array, at, data - at);
+ ++data;
+ }
+
+ str_unescape (array[n]);
+ } else {
+ at = data;
+ do {
+ ++data;
+ data = data + strcspn (data, WHITESPACE);
+ } while (*data && is_escaped (at, data));
+
+ array = str_array_appendn (array, at, data - at);
+ str_unescape (array[n]);
+ }
+
+ if (!array)
+ break;
+ }
+
+ return array;
+}
+
+char**
+str_array_create (const char *first, ...)
+{
+ char **array;
+ char *value;
+ va_list va;
+
+ array = calloc (32, sizeof (char*));
+ if (!array)
+ return NULL;
+
+ if (first) {
+ array[0] = strdup (first);
+ if (!array[0]) {
+ free (array);
+ return NULL;
+ }
+
+ va_start (va, first);
+ while ((value = va_arg (va, char*)) != NULL) {
+ array = str_array_append (array, value);
+ if (!array)
+ break;
+ }
+ va_end (va);
+ }
+
+ return array;
+}
+
+unsigned int
+str_array_length (char **array)
+{
+ unsigned int length = 0;
+
+ assert (array);
+
+ while (*array) {
+ ++length;
+ ++array;
+ }
+ return length;
+}
+
+char**
+str_array_append (char **array, const char *next)
+{
+ assert (array);
+ assert (next);
+
+ return str_array_appendn (array, next, strlen (next));
+}
+
+char**
+str_array_appendn (char **array, const char *next, unsigned int len)
+{
+ char **narray;
+ int num;
+
+ assert (array);
+ assert (next || !len);
+
+ num = str_array_length (array);
+
+ /*
+ * Actually because of intelligent libc this is not
+ * as inefficient as it looks.
+ */
+ narray = realloc (array, (num + 2) * sizeof (char*));
+ if (!narray) {
+ str_array_free (array);
+ return NULL;
+ }
+
+ narray[num] = malloc (len + 1);
+ if (!narray[num]) {
+ str_array_free (narray);
+ return NULL;
+ }
+ memcpy (narray[num], next, len);
+ narray[num][len] = 0;
+ narray[num + 1] = NULL;
+
+ return narray;
+}
+
+void
+str_array_free (char **array)
+{
+ char **a;
+
+ if (!array);
+ return;
+
+ for (a = array; *a; ++a)
+ free (*a);
+ free (array);
+}
diff --git a/common/stringx.h b/common/stringx.h
index c9a2832..4216d4f 100644
--- a/common/stringx.h
+++ b/common/stringx.h
@@ -43,4 +43,13 @@ const char* trim_start(const char* data);
char* trim_end(char* data);
char* trim_space(char* data);
+void str_unescape (char *str);
+
+unsigned int str_array_length (char **array);
+char** str_array_parse_quoted (const char *data);
+char** str_array_create (const char *first, ...);
+char** str_array_append (char **array, const char *next);
+char** str_array_appendn (char **array, const char *next, unsigned int len);
+void str_array_free (char **array);
+
#endif /* __STRINGX_H__ */
diff --git a/configure.in b/configure.in
index 36ffd9e..d57045e 100644
--- a/configure.in
+++ b/configure.in
@@ -51,17 +51,6 @@ AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
-# Debug mode
-AC_ARG_ENABLE(debug,
- AC_HELP_STRING([--enable-debug],
- [Compile binaries in debug mode]))
-
-if test "$enable_debug" = "yes"; then
- CFLAGS="$CFLAGS -g -O0"
- AC_DEFINE_UNQUOTED(_DEBUG, 1, [In debug mode])
- echo "enabling debug compile mode"
-fi
-
# Check for the various options
AC_ARG_WITH(ldap, [ --with-ldap with LDAP support])
AC_ARG_WITH(pgsql, [ --with-pgsql with Postgres support])
@@ -130,7 +119,7 @@ if test -n "$with_pgsql"; then
AC_CHECK_LIB([pq], [PQexec], ,
[ echo "ERROR: Postgres libpq library required."; exit 1] )
- AC_CHECK_HEADERS(libpq-fe.h)
+ AC_CHECK_HEADERS(libpq-fe.h, postgresql/libpq-fe.h)
AC_DEFINE_UNQUOTED(WITH_PGSQL, 1, [With PGSQL Support] )
fi
@@ -206,6 +195,17 @@ AC_SUBST(APACHECTL)
AC_DEFINE_UNQUOTED(CONF_PREFIX, "`eval echo ${sysconfdir}`", [Installation Prefix] )
+# Debug mode
+AC_ARG_ENABLE(debug,
+ AC_HELP_STRING([--enable-debug],
+ [Compile binaries in debug mode]))
+
+if test "$enable_debug" = "yes"; then
+ CFLAGS="$CFLAGS -g -O0 -Wall -Werror"
+ AC_DEFINE_UNQUOTED(_DEBUG, 1, [In debug mode])
+ echo "enabling debug compile mode"
+fi
+
AC_CONFIG_FILES([Makefile
daemon/Makefile
doc/Makefile
diff --git a/daemon/bd.c b/daemon/bd.c
index e11a56b..eb0bec0 100644
--- a/daemon/bd.c
+++ b/daemon/bd.c
@@ -29,6 +29,7 @@
#include "basic.h"
#include "md5.h"
#include "bd.h"
+#include "stringx.h"
static unsigned char g_digest_secret[DIGEST_SECRET_LEN];
@@ -38,193 +39,277 @@ static unsigned char g_digest_secret[DIGEST_SECRET_LEN];
* Defaults and Constants
*/
-#define BASIC_ESTABLISHED (void*)1
+enum {
+ RECORD_TYPE_BASIC,
+ RECORD_TYPE_DIGEST
+};
/* Kept by the us for validating the client */
-typedef struct digest_record
-{
- unsigned char nonce[DIGEST_NONCE_LEN];
- unsigned char userhash[MD5_LEN];
- unsigned char ha1[MD5_LEN];
- unsigned int nc;
-}
-digest_record_t;
+typedef struct bd_record {
+ int type;
+
+ /* Used for Digest */
+ unsigned char nonce[DIGEST_NONCE_LEN];
+ unsigned char userhash[MD5_LEN];
+ unsigned char ha1[MD5_LEN];
+ unsigned int nc;
+
+ /* Used for both */
+ char **groups;
+} bd_record_t;
/* -------------------------------------------------------------------------------
* Internal Functions
*/
-static void free_hash_object(void* arg, void* val)
+static void
+free_hash_object (void *arg, void *val)
{
- if(val && val != BASIC_ESTABLISHED)
- free(val);
+ bd_record_t *rec = val;
+ if (!rec)
+ return;
+ str_array_free (rec->groups);
+ free (rec);
}
-static digest_record_t* get_cached_digest(bd_context_t* ctx, ha_context_t* c,
- unsigned char* nonce)
+static int
+have_cached_basic (bd_context_t* ctx, unsigned char* key)
{
- digest_record_t* rec;
+ bd_record_t *rec;
- ASSERT(ctx && c && nonce);
+ ASSERT (ctx && key);
- if(c->cache_max == 0)
- return NULL;
+ ha_lock(NULL);
- ha_lock(NULL);
-
- rec = (digest_record_t*)hsh_get(ctx->cache, nonce);
+ rec = (bd_record_t*)hsh_get (ctx->cache, key);
+ if (rec && rec->type != RECORD_TYPE_BASIC)
+ rec = NULL;
+ else
+ hsh_touch (ctx->cache, key);
- /* Just in case it's a basic :) */
- if(rec && rec != BASIC_ESTABLISHED)
- hsh_rem(ctx->cache, nonce);
-
- ha_unlock(NULL);
+ ha_unlock(NULL);
- ASSERT(!rec || memcmp(nonce, rec->nonce, DIGEST_NONCE_LEN) == 0);
- return rec;
+ return rec != NULL;
}
-static int have_cached_basic(bd_context_t* ctx, unsigned char* key)
+static int
+add_cached_basic (bd_context_t *ctx, unsigned char *key, char **groups)
{
- int ret = 0;
+ bd_record_t *rec;
+ int r;
- ASSERT(ctx && key);
+ ASSERT (ctx && key);
- ha_lock(NULL);
+ rec = (bd_record_t*)malloc (sizeof (*rec));
+ if (!rec) {
+ str_array_free (groups);
+ ha_memerr (NULL);
+ return HA_CRITERROR;
+ }
- ret = (hsh_get(ctx->cache, key) == BASIC_ESTABLISHED);
+ memset (rec, 0, sizeof (*rec));
+ rec->type = RECORD_TYPE_BASIC;
+ rec->groups = groups;
- ha_unlock(NULL);
+ ha_lock (NULL);
- return ret;
-}
+ while (hsh_count (ctx->cache) >= ctx->cache_max)
+ hsh_bump (ctx->cache);
-static int save_cached_digest(bd_context_t* ctx, ha_context_t* c,
- digest_record_t* rec)
-{
- int r;
+ r = hsh_set (ctx->cache, key, rec);
- ASSERT(ctx && rec);
+ ha_unlock (NULL);
- if(c->cache_max == 0)
- {
- free_hash_object(NULL, rec);
- return HA_FALSE;
- }
+ if (!r) {
+ free_hash_object (NULL, rec);
+ ha_memerr (NULL);
+ return HA_CRITERROR;
+ }
- ha_lock(NULL);
+ return HA_OK;
+}
- while(hsh_count(ctx->cache) >= c->cache_max)
- hsh_bump(ctx->cache);
+static int
+prepare_digest_from_cached (bd_context_t *ctx, digest_context_t *dg,
+ ha_request_t *rq, unsigned char *nonce)
+{
+ bd_record_t *rec;
+ int ret;
+
+ ASSERT (dg && rq && nonce);
+
+ ha_lock (NULL);
+
+ rec = (bd_record_t*)hsh_get (ctx->cache, nonce);
+ if (rec && rec->type == RECORD_TYPE_DIGEST) {
+ ASSERT (memcmp (nonce, rec->nonce, DIGEST_NONCE_LEN) == 0);
+ memcpy (dg->server_userhash, rec->userhash, sizeof (dg->server_userhash));
+ memcpy (dg->server_ha1, rec->ha1, sizeof (dg->server_ha1));
+ dg->server_nc = ++(rec->nc);
+ hsh_touch (ctx->cache, nonce);
+ ret = HA_OK;
+ } else {
+ memset (dg->server_userhash, 0, sizeof (dg->server_userhash));
+ memset (dg->server_ha1, 0, sizeof (dg->server_ha1));
+ dg->server_nc = 1;
+ ret = HA_FALSE;
+ }
+
+ ha_unlock (NULL);
+
+ dg->server_uri = rq->req_args[AUTH_ARG_URI];
+ dg->server_method = rq->req_args[AUTH_ARG_METHOD];
+
+ return ret;
+}
- r = hsh_set(ctx->cache, rec->nonce, rec);
+static int
+add_digest_rec (bd_context_t *ctx, unsigned char *nonce,
+ const char *user, digest_context_t *dg, char **groups)
+{
+ bd_record_t* rec = (bd_record_t*)malloc (sizeof (*rec));
+ int r;
- ha_unlock(NULL);
+ ASSERT (nonce && user);
- if(!r)
- {
+ if(!rec) {
+ str_array_free (groups);
ha_memerr(NULL);
- return HA_CRITERROR;
- }
+ return HA_CRITERROR;
+ }
- return HA_OK;
-}
+ memset (rec, 0, sizeof (*rec));
+ rec->type = RECORD_TYPE_DIGEST;
+ memcpy (rec->nonce, nonce, DIGEST_NONCE_LEN);
+ memcpy (rec->ha1, dg->server_ha1, MD5_LEN);
-static int add_cached_basic(bd_context_t* ctx, ha_context_t* c,
- unsigned char* key)
-{
- int r;
+ /* We take ownership of groups */
+ rec->groups = groups;
+ rec->nc = dg->server_nc;
- ASSERT(ctx && c && key);
+ md5_string (rec->userhash, user);
- if(c->cache_max == 0)
- return HA_FALSE;
+ ha_lock (NULL);
- ha_lock(NULL);
+ while (hsh_count (ctx->cache) >= ctx->cache_max)
+ hsh_bump (ctx->cache);
- while(hsh_count(ctx->cache) >= c->cache_max)
- hsh_bump(ctx->cache);
+ r = hsh_set (ctx->cache, nonce, rec);
- r = hsh_set(ctx->cache, key, BASIC_ESTABLISHED);
+ ha_unlock (NULL);
- ha_unlock(NULL);
-
- if(!r)
- {
- ha_memerr(NULL);
- return HA_CRITERROR;
- }
+ if (!r) {
+ free_hash_object (NULL, rec);
+ ha_memerr (NULL);
+ return HA_CRITERROR;
+ }
- return HA_OK;
+ return HA_OK;
}
-digest_record_t* make_digest_rec(unsigned char* nonce, const char* user)
+static int
+include_group_headers (bd_context_t *ctx, ha_request_t *rq, unsigned char *key)
{
- digest_record_t* rec = (digest_record_t*)malloc(sizeof(*rec));
-
- ASSERT(nonce && user);
-
- if(!rec)
- {
- ha_memerr(NULL);
- return NULL;
- }
-
- memset(rec, 0, sizeof(*rec));
- memcpy(rec->nonce, nonce, DIGEST_NONCE_LEN);
-
- md5_string(rec->userhash, user);
- return rec;
+ bd_record_t *rec;
+ int have, all = 0;
+ char *header;
+ char **ug;
+ char **rg;
+
+ if (!rq->requested_groups || !rq->requested_groups[0])
+ return HA_OK;
+
+ ha_lock (NULL);
+
+ /* This starts a new block to join */
+ ha_bufcpy (rq->buf, "");
+
+ rec = hsh_get (ctx->cache, key);
+ if (rec) {
+ for (ug = rec->groups; ug && *ug; ++ug) {
+ have = all;
+ for (rg = rq->requested_groups; rg && *rg && !have; ++rg) {
+ if (strcasecmp (*rg, "*") == 0) {
+ have = all = 1;
+ break;
+ } else if (strcasecmp (*rg, *ug) == 0) {
+ have = 1;
+ break;
+ }
+ }
+
+ if (have) {
+ ha_bufjoin (rq->buf);
+ ha_bufcpy (rq->buf, "\"");
+ ha_bufjoin (rq->buf);
+ digest_escape (rq->buf, *ug);
+ ha_bufjoin (rq->buf);
+ ha_bufcpy (rq->buf, "\" ");
+ }
+ }
+ }
+
+ ha_unlock (NULL);
+
+ header = ha_bufdata (rq->buf);
+ if (!header)
+ return HA_CRITERROR;
+
+ ha_addheader (rq, "X-HttpAuth-Groups", header);
+ return HA_OK;
}
-static int do_basic_response(ha_request_t* rq, bd_context_t* ctx, const char* header)
+static int
+do_basic_response (ha_request_t* rq, bd_context_t* ctx, const char* header)
{
- basic_header_t basic;
- int ret = HA_FALSE;
+ basic_header_t basic;
+ char **groups = NULL;
+ int ret = HA_FALSE;
- ASSERT(header && rq);
+ ASSERT (header && rq);
- if((ret = basic_parse(header, rq->buf, &basic)) < 0)
- return ret;
+ if ((ret = basic_parse (header, rq->buf, &basic)) < 0)
+ return ret;
- /* Past this point we don't return directly */
+ /* Past this point we don't return directly */
- /* Check and see if this connection is in the cache */
- if(have_cached_basic(ctx, basic.key))
- {
- ha_messagex(rq, LOG_NOTICE, "validated basic user against cache: %s",
- basic.user);
- RETURN(HA_OK);
- }
+ /* Check and see if this connection is in the cache */
+ if (have_cached_basic (ctx, basic.key)) {
+ ha_messagex (rq, LOG_NOTICE, "validated basic user against cache: %s",
+ basic.user);
+ RETURN (HA_OK);
+ }
- /* If we have a user name and password */
- if(!basic.user || !basic.user[0] ||
- !basic.password || !basic.password[0])
- {
- ha_messagex(rq, LOG_NOTICE, "no valid basic auth info");
- RETURN(HA_FALSE);
- }
+ /* If we have a user name and password */
+ if(!basic.user || !basic.user[0] ||
+ !basic.password || !basic.password[0]) {
+ ha_messagex(rq, LOG_NOTICE, "no valid basic auth info");
+ RETURN(HA_FALSE);
+ }
- ASSERT(ctx->f_validate_basic);
- ret = ctx->f_validate_basic(rq, basic.user, basic.password);
+ ASSERT (ctx->f_validate_basic);
+ ret = ctx->f_validate_basic (rq, basic.user, basic.password, &groups);
-finally:
+ /*
+ * We put this connection into the successful connections.
+ * This takes ownership of groups.
+ */
+ if (ret == HA_OK)
+ ret = add_cached_basic (ctx, basic.key, groups);
- if(ret == HA_OK)
- {
- rq->resp_code = HA_SERVER_OK;
- rq->resp_detail = basic.user;
+finally:
- /* We put this connection into the successful connections */
- ret = add_cached_basic(ctx, rq->context, basic.key);
- }
+ if (ret == HA_OK) {
+ rq->resp_code = HA_SERVER_OK;
+ rq->resp_detail = basic.user;
+ include_group_headers (ctx, rq, basic.key);
+ }
- return ret;
+ return ret;
}
static int do_digest_challenge(ha_request_t* rq, bd_context_t* ctx, int stale)
{
- unsigned char nonce[DIGEST_NONCE_LEN];
const char* nonce_str;
const char* header;
@@ -264,208 +349,172 @@ static int do_digest_challenge(ha_request_t* rq, bd_context_t* ctx, int stale)
static int do_digest_response(ha_request_t* rq, bd_context_t* ctx, const char* header)
{
- unsigned char nonce[DIGEST_NONCE_LEN];
- digest_context_t dg;
- digest_record_t* rec = NULL;
- const char* t;
- time_t expiry;
- int ret = HA_FALSE;
- int stale = 0;
- int r;
+ unsigned char nonce[DIGEST_NONCE_LEN];
+ digest_context_t dg;
+ const char *t;
+ char **groups = NULL;
+ time_t expiry;
+ int ret = HA_FALSE;
+ int stale = 0;
+ int r, cached;
- ASSERT(ctx && header && rq);
+ ASSERT (ctx && header && rq);
- /* We use this below to send a default response */
- rq->resp_code = -1;
+ /* We use this below to send a default response */
+ rq->resp_code = -1;
- if((r = digest_parse(header, rq->buf, &(dg.client))) < 0)
- return r;
+ if ((r = digest_parse (header, rq->buf, &(dg.client))) < 0)
+ return r;
- if(!dg.client.username)
- {
- ha_messagex(rq, LOG_WARNING, "digest response contains no user name");
- RETURN(HA_FALSE);
- }
+ if (!dg.client.username) {
+ ha_messagex(rq, LOG_WARNING, "digest response contains no user name");
+ RETURN(HA_FALSE);
+ }
#ifdef _DEBUG
- if(rq->context->digest_debugnonce)
- {
- if(dg.client.nonce && strcmp(dg.client.nonce, rq->context->digest_debugnonce) != 0)
- {
- ha_messagex(rq, LOG_WARNING, "digest response contains invalid nonce");
- RETURN(HA_FALSE);
- }
-
- /* Do a rough hash into the real nonce, for use as a key */
- md5_string(nonce, rq->context->digest_debugnonce);
-
- /* Debug nonce's never expire */
- expiry = time(NULL);
- }
- else
+ if (rq->context->digest_debugnonce) {
+ if (dg.client.nonce && strcmp (dg.client.nonce, rq->context->digest_debugnonce) != 0) {
+ ha_messagex(rq, LOG_WARNING, "digest response contains invalid nonce");
+ RETURN(HA_FALSE);
+ }
+
+ /* Do a rough hash into the real nonce, for use as a key */
+ md5_string (nonce, rq->context->digest_debugnonce);
+
+ /* Debug nonce's never expire */
+ expiry = time (NULL);
+ } else
#endif
- {
- /* Parse out the nonce from that header */
- memset(nonce, 0, DIGEST_NONCE_LEN);
-
- if(dg.client.nonce)
- {
- size_t len = DIGEST_NONCE_LEN;
- void* d = ha_bufdechex(rq->buf, dg.client.nonce, &len);
-
- if(d && len == DIGEST_NONCE_LEN)
- memcpy(nonce, d, DIGEST_NONCE_LEN);
- }
-
- r = digest_checknonce(nonce, g_digest_secret, &expiry);
- if(r != HA_OK)
- {
- if(r == HA_FALSE)
- ha_messagex(rq, LOG_WARNING, "digest response contains invalid nonce");
-
- RETURN(r);
- }
-
- /* Check to see if we're stale */
- if((expiry + rq->context->cache_timeout) <= time(NULL))
- {
- ha_messagex(rq, LOG_INFO, "nonce expired, sending stale challenge: %s",
- dg.client.username);
-
- stale = 1;
- RETURN(HA_FALSE);
- }
- }
-
- /* See if we have this one from before. */
- rec = get_cached_digest(ctx, rq->context, nonce);
-
- /*
- * Fill in the required fields.
- */
- dg.server_nc = rec ? ++(rec->nc) : 0; /* Note bumping up nc */
- dg.server_uri = rq->req_args[AUTH_ARG_URI];
- dg.server_method = rq->req_args[AUTH_ARG_METHOD];
-
- /* Check the majority of the fields */
- ret = digest_pre_check(&dg, rq->context, rq->buf);
-
- if(ret != HA_OK)
- {
- if(ret == HA_BADREQ)
- {
- ret = HA_FALSE;
- rq->resp_code = HA_SERVER_BADREQ;
- }
-
- RETURN(ret);
- }
-
- /*
- * If this is the first instance then we pass off to our derived
- * handler for validation and completion of the ha1. This completes
- * the authentication, and leaves us the ha1 caching.
- */
- if(!rec)
- {
- ha_messagex(rq, LOG_INFO, "no record in cache, creating one: %s", dg.client.username);
-
- /*
- * If we're valid but don't have a record in the
- * cache then complete the record properly.
- */
-
- rec = make_digest_rec(nonce, dg.client.username);
- if(!rec)
- RETURN(HA_CRITERROR);
-
-
- ASSERT(ctx->f_validate_digest);
- r = ctx->f_validate_digest(rq, dg.client.username, &dg);
- if(r != HA_OK)
- RETURN(r);
-
- /* Save away pertinent information when successful*/
- memcpy(rec->ha1, dg.ha1, MD5_LEN);
- }
-
- /* We had a record so ... */
- else
- {
- /* Bump up the ncount */
- rec->nc++;
-
- /* Check the user name */
- if(md5_strcmp(rec->userhash, dg.client.username) != 0)
- {
- ha_messagex(NULL, LOG_ERR, "digest response contains invalid username");
- RETURN(HA_FALSE);
- }
-
- /* And do the validation ourselves */
- memcpy(dg.ha1, rec->ha1, MD5_LEN);
-
- ret = digest_complete_check(&dg, rq->context, rq->buf);
-
- if(ret != HA_OK)
- {
- if(ret == HA_BADREQ)
- {
- ret = HA_FALSE;
- rq->resp_code = HA_SERVER_BADREQ;
- }
-
- if(ret == HA_FALSE)
- ha_messagex(NULL, LOG_WARNING, "digest re-authentication failed for user: %s",
- dg.client.username);
-
- RETURN(ret);
- }
- }
-
-
- rq->resp_code = HA_SERVER_OK;
- rq->resp_detail = dg.client.username;
-
- /* Figure out if we need a new nonce */
- if((expiry + (rq->context->cache_timeout -
- (rq->context->cache_timeout / 8))) < time(NULL))
- {
- ha_messagex(rq, LOG_INFO, "nonce almost expired, creating new one: %s",
- dg.client.username);
-
- digest_makenonce(nonce, g_digest_secret, NULL);
- stale = 1;
- }
-
- t = digest_respond(&dg, rq->buf, stale ? nonce : NULL);
- if(!t)
- RETURN(HA_CRITERROR);
-
- if(t[0])
- ha_addheader(rq, "Authentication-Info", t);
-
- ha_messagex(rq, LOG_NOTICE, "validated digest user: %s", dg.client.username);
-
- /* Put the connection into the cache */
- if((ret = save_cached_digest(ctx, rq->context, rec)) == HA_OK)
- rec = NULL;
+ {
+ /* Parse out the nonce from that header */
+ memset (nonce, 0, DIGEST_NONCE_LEN);
+
+ if (dg.client.nonce) {
+ size_t len = DIGEST_NONCE_LEN;
+ void* d = ha_bufdechex (rq->buf, dg.client.nonce, &len);
+
+ if (d && len == DIGEST_NONCE_LEN)
+ memcpy (nonce, d, DIGEST_NONCE_LEN);
+ }
+
+ r = digest_checknonce (nonce, g_digest_secret, &expiry);
+ if (r != HA_OK) {
+ if (r == HA_FALSE)
+ ha_messagex (rq, LOG_WARNING, "digest response contains invalid nonce");
+ stale = 1;
+ RETURN (r);
+ }
+
+ /* Check to see if we're stale */
+ if ((expiry + rq->context->cache_timeout) <= time (NULL)) {
+ ha_messagex(rq, LOG_INFO, "nonce expired, sending stale challenge: %s",
+ dg.client.username);
+
+ stale = 1;
+ RETURN (HA_FALSE);
+ }
+ }
+
+ /*
+ * Fill in all the required fields from any cached response,
+ * and check the user name if cached. Otherwise initializes
+ * to default values.
+ */
+ cached = (prepare_digest_from_cached (ctx, &dg, rq, nonce) == HA_OK);
+
+ /* Check the majority of the fields */
+ ret = digest_pre_check (&dg, rq->context, rq->buf);
+ if (ret != HA_OK) {
+ if (ret == HA_BADREQ) {
+ ret = HA_FALSE;
+ rq->resp_code = HA_SERVER_BADREQ;
+ }
+
+ RETURN (ret);
+ }
+
+ /*
+ * If this is the first instance then we pass off to our derived
+ * handler for validation and completion of the ha1. This completes
+ * the authentication, and leaves us the ha1 caching.
+ */
+ if (!cached) {
+ ha_messagex (rq, LOG_INFO, "no record in cache, creating one: %s",
+ dg.client.username);
+
+ /*
+ * If we're valid but don't have a record in the
+ * cache then complete the record properly.
+ */
+
+ ASSERT (ctx->f_validate_digest);
+ r = ctx->f_validate_digest (rq, dg.client.username, &dg, &groups);
+ if (r != HA_OK)
+ RETURN (r);
+
+ /* Add the digest record to the cache */
+ r = add_digest_rec (ctx, nonce, dg.client.username, &dg, groups);
+ if (r != HA_OK)
+ RETURN (r);
+
+ /* We had a record so ... */
+ } else {
+
+ /* Check the user name */
+ if (md5_strcmp (dg.server_userhash, dg.client.username) != 0) {
+ ha_messagex(NULL, LOG_ERR, "digest response contains invalid username");
+ RETURN(HA_FALSE);
+ }
+
+ /* And do the validation ourselves */
+ ret = digest_complete_check (&dg, rq->context, rq->buf);
+
+ if (ret != HA_OK) {
+ if (ret == HA_BADREQ) {
+ ret = HA_FALSE;
+ rq->resp_code = HA_SERVER_BADREQ;
+ }
+
+ if (ret == HA_FALSE)
+ ha_messagex (NULL, LOG_WARNING, "digest re-authentication failed for user: %s",
+ dg.client.username);
+
+ RETURN (ret);
+ }
+ }
+
+ rq->resp_code = HA_SERVER_OK;
+ rq->resp_detail = dg.client.username;
+ include_group_headers (ctx, rq, nonce);
+
+ /* Figure out if we need a new nonce */
+ if ((expiry + (rq->context->cache_timeout -
+ (rq->context->cache_timeout / 8))) < time (NULL)) {
+ ha_messagex (rq, LOG_INFO, "nonce almost expired, creating new one: %s",
+ dg.client.username);
+
+ digest_makenonce (nonce, g_digest_secret, NULL);
+ stale = 1;
+ }
+
+ t = digest_respond (&dg, rq->buf, stale ? nonce : NULL);
+ if (!t)
+ RETURN (HA_CRITERROR);
+
+ if (t[0])
+ ha_addheader(rq, "Authentication-Info", t);
+
+ ha_messagex(rq, LOG_NOTICE, "validated digest user: %s", dg.client.username);
finally:
- /* If the record wasn't stored away then free it */
- if(rec)
- free(rec);
-
- /* If nobody above responded then challenge the client again */
- if(ret == HA_FALSE && rq->resp_code == -1)
- return do_digest_challenge(rq, ctx, stale);
+ /* If nobody above responded then challenge the client again */
+ if (ret == HA_FALSE && rq->resp_code == -1)
+ return do_digest_challenge (rq, ctx, stale);
- return ret;
+ return ret;
}
-
-
const char* bd_substitute(const ha_request_t* rq, const char* user, const char* str)
{
bd_context_t* ctx = (bd_context_t*)rq->context->ctx_data;
@@ -559,6 +608,7 @@ int bd_init(ha_context_t* context)
htc.f_freeval = free_hash_object;
htc.arg = NULL;
hsh_set_table_calls(ctx->cache, &htc);
+ ctx->cache_max = context->cache_max;
}
return HA_OK;
@@ -567,7 +617,6 @@ int bd_init(ha_context_t* context)
void bd_destroy(ha_context_t* context)
{
bd_context_t* ctx;
- int i;
if(!context)
return;
diff --git a/daemon/bd.h b/daemon/bd.h
index a42af68..1697cfb 100644
--- a/daemon/bd.h
+++ b/daemon/bd.h
@@ -39,8 +39,8 @@
* HA_OK: completed successfully
* HA_FAILED: error retrieving hash (should have logged error)
*/
-typedef int (*bd_validate_digest)(ha_request_t* rq,
- const char* user, digest_context_t* dg);
+typedef int (*bd_validate_digest)(ha_request_t* rq, const char* user,
+ digest_context_t* dg, char ***groups);
/*
* A callback for validating a given user's password.
@@ -50,8 +50,8 @@ typedef int (*bd_validate_digest)(ha_request_t* rq,
* HA_FALSE: invalid password
* HA_FAILED: error validating (should have logged error)
*/
-typedef int (*bd_validate_basic)(ha_request_t* rq,
- const char* user, const char* password);
+typedef int (*bd_validate_basic) (ha_request_t *rq, const char *user,
+ const char *password, char ***groups);
/*
* Escapes a value for sending to 'server'
@@ -71,6 +71,7 @@ typedef struct bd_context
/* Require locking --------------------------------------*/
hsh_t* cache; /* Some cached records or basic */
+ unsigned int cache_max; /* Maximum number of records in cache */
}
bd_context_t;
diff --git a/daemon/digest.c b/daemon/digest.c
index ecde6b7..21cb453 100644
--- a/daemon/digest.c
+++ b/daemon/digest.c
@@ -27,6 +27,7 @@
#include "digest.h"
#include "stringx.h"
+#include <ctype.h>
#include <syslog.h>
/* A globally unique counter used to guarantee uniqueness of nonces */
@@ -333,8 +334,9 @@ int digest_pre_check(digest_context_t* dg, const ha_context_t* opts, ha_buffer_t
if(*e || nc != dg->server_nc)
{
- ha_messagex(NULL, LOG_WARNING, "digest response has wrong nc value. "
- "possible replay attack: %s", dg->client.nc);
+ ha_messagex(NULL, LOG_WARNING, "digest response has wrong nc value: %s "
+ "possible replay attack, should be: %d",
+ dg->client.nc, dg->server_nc);
return HA_FALSE;
}
}
@@ -415,7 +417,7 @@ static int internal_check(digest_context_t* dg, const char* http_method, ha_buff
*/
/* Encode ha1 */
- t = ha_bufenchex(buf, dg->ha1, MD5_LEN);
+ t = ha_bufenchex(buf, dg->server_ha1, MD5_LEN);
if(t == NULL)
return HA_CRITERROR;
@@ -541,7 +543,7 @@ const char* digest_respond(digest_context_t* dg, ha_buffer_t* buf,
/* Otherwise we do the whole song and dance */
/* Encode ha1 */
- t = ha_bufenchex(buf, dg->ha1, MD5_LEN);
+ t = ha_bufenchex(buf, dg->server_ha1, MD5_LEN);
if(t == NULL)
return NULL;
@@ -614,3 +616,38 @@ void digest_makeha1(unsigned char* digest, const char* user,
md5_update(&md5, password, strlen(password));
md5_final(digest, &md5);
}
+
+#define MUST_ESCAPE "\"\' \t\n\r\v\\"
+
+void
+digest_escape (ha_buffer_t *buf, const char *orig)
+{
+ const char* t;
+ size_t pos;
+
+ assert (orig);
+ assert (buf);
+
+ ha_bufcpy(buf, "");
+
+ t = orig;
+ while (*t) {
+ pos = strcspn (t, MUST_ESCAPE);
+
+ if(pos > 0) {
+ ha_bufjoin (buf);
+ ha_bufncpy (buf, t, pos);
+ t += pos;
+ }
+
+ while (*t && !strchr (MUST_ESCAPE, *t)) {
+ char esc[3];
+ esc[0] = '\\';
+ esc[1] = *t;
+ esc[2] = '\0';
+ ha_bufjoin (buf);
+ ha_bufcpy (buf, esc);
+ t++;
+ }
+ }
+}
diff --git a/daemon/digest.h b/daemon/digest.h
index af8a1d7..7a0f59c 100644
--- a/daemon/digest.h
+++ b/daemon/digest.h
@@ -22,8 +22,11 @@
#ifndef __DIGEST_H__
#define __DIGEST_H__
+#include "httpauthd.h"
#include "md5.h"
+#include <sys/time.h>
+
#define DIGEST_NONCE_LEN sizeof(time_t) + sizeof(unsigned int) + MD5_LEN
#define DIGEST_SECRET_LEN 16
@@ -53,7 +56,8 @@ typedef struct digest_context
const char* server_uri;
const char* server_method;
- unsigned char ha1[MD5_LEN];
+ unsigned char server_userhash[MD5_LEN];
+ unsigned char server_ha1[MD5_LEN];
}
digest_context_t;
@@ -78,4 +82,8 @@ int digest_complete_check(digest_context_t* dg, const ha_context_t* opts, ha_buf
/* This assumes a digest_context that's been checked and validated successfully */
const char* digest_respond(digest_context_t* dg, ha_buffer_t* buf, unsigned char* next);
+void digest_makenonce(unsigned char* nonce, unsigned char* secret, unsigned char* old);
+int digest_checknonce(unsigned char* nonce, unsigned char* secret, time_t* tm);
+void digest_escape (ha_buffer_t *buf, const char *orig);
+
#endif /* __DIGEST_H__ */
diff --git a/daemon/dummy.c b/daemon/dummy.c
index ca83e54..245d32a 100755
--- a/daemon/dummy.c
+++ b/daemon/dummy.c
@@ -33,8 +33,8 @@
*/
/* Forward declarations for callbacks */
-static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg);
-static int validate_basic(ha_request_t* rq, const char* user, const char* password);
+static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg, char ***groups);
+static int validate_basic(ha_request_t* rq, const char* user, const char* password, char ***groups);
/* The defaults for the context */
static const bd_context_t dummy_defaults = BD_CALLBACKS(validate_digest, validate_basic, NULL);
@@ -43,12 +43,12 @@ static const bd_context_t dummy_defaults = BD_CALLBACKS(validate_digest, validat
* Internal Functions
*/
-static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg)
+static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg, char ***groups)
{
return HA_OK;
}
-static int validate_basic(ha_request_t* rq, const char* user, const char* password)
+static int validate_basic(ha_request_t* rq, const char* user, const char* password, char ***groups)
{
return HA_OK;
}
diff --git a/daemon/httpauthd.c b/daemon/httpauthd.c
index 1161517..09fb1d7 100644
--- a/daemon/httpauthd.c
+++ b/daemon/httpauthd.c
@@ -31,6 +31,7 @@
#include <pthread.h>
#include <fcntl.h>
#include <err.h>
+#include <ctype.h>
#include <signal.h>
#include "usuals.h"
@@ -85,6 +86,8 @@ httpauth_loaded_t;
/* The list of handlers in use */
httpauth_loaded_t* g_handlers = NULL;
+extern int pthread_mutexattr_settype (pthread_mutexattr_t *attr, int kind);
+
/* -----------------------------------------------------------------------
* Structures and Constants
*/
@@ -162,7 +165,6 @@ static void writepid(const char* pid);
static void* httpauth_thread(void* arg);
static int httpauth_processor(int ifd, int ofd);
static int httpauth_respond(ha_request_t* rq, int ofd, int scode, int ccode, const char* msg);
-static int process_auth(ha_request_t* rq);
static int config_parse(const char* file, ha_buffer_t* buf);
static void on_quit(int signal);
@@ -176,7 +178,6 @@ int main(int argc, char* argv[])
const char* pidfile = NULL;
httpauth_thread_t* threads = NULL;
httpauth_loaded_t* h;
- char peername[MAXPATHLEN];
struct sockaddr_any sany;
int daemonize = 1;
ha_buffer_t cbuf;
@@ -944,9 +945,6 @@ static int httpauth_error(ha_request_t* rq, int ofd, int r)
static int httpauth_ready(ha_request_t* rq, int ofd)
{
- const char* t;
- httpauth_loaded_t* h;
-
ASSERT(ofd != -1);
ASSERT(rq);
@@ -1027,6 +1025,14 @@ static int httpauth_set(ha_request_t* rq, ha_buffer_t* cbuf, int ofd)
rq->digest_domain = ha_bufcpy(rq->conn_buf, value ? value : "");
}
+ else if (strcasecmp (name, "Groups") == 0) {
+
+ /* we need to copy this string so it doesn't get destroyed on next req */
+ if (rq->requested_groups)
+ str_array_free (rq->requested_groups);
+ rq->requested_groups = str_array_parse_quoted (value ? value : "");
+ }
+
else if(strcasecmp(name, "Handler") == 0)
{
if(!value || !*value)
@@ -1048,7 +1054,7 @@ static int httpauth_set(ha_request_t* rq, ha_buffer_t* cbuf, int ofd)
if(value != NULL)
{
- ha_messagex(rq, LOG_ERR, "unknown authentication handler: %s", rq->req_args[0]);
+ ha_messagex(rq, LOG_ERR, "unknown authentication handler: %s", value);
return httpauth_respond(rq, ofd, HA_SERVER_BADREQ, 0, "Unknown Auth Handler");
}
}
@@ -1107,6 +1113,7 @@ static int httpauth_processor(int ifd, int ofd)
/* Set up some context stuff */
rq.digest_domain = "";
+ rq.requested_groups = NULL;
rq.buf = &buf;
rq.conn_buf = &cbuf;
@@ -1188,6 +1195,8 @@ finally:
close(ofd);
}
+ if (rq.requested_groups)
+ str_array_free (rq.requested_groups);
ha_messagex(&rq, LOG_INFO, "closed connection");
ha_buffree(&cbuf);
@@ -1320,7 +1329,6 @@ static int config_parse(const char* file, ha_buffer_t* buf)
if(ha_bufchar(buf) == '[')
{
ha_handler_t* handler = NULL;
- const char* x;
ha_bufeat(buf);
name = ha_bufparseline(buf, 1);
diff --git a/daemon/httpauthd.h b/daemon/httpauthd.h
index 1540138..e533dde 100644
--- a/daemon/httpauthd.h
+++ b/daemon/httpauthd.h
@@ -138,13 +138,13 @@ ha_context_t;
#define HA_MAX_ARGS 4
/*
- * The maximum number of pertinent headers to read
- * from the client. If you need to add valid headers
+ * The maximum number of pertinent headers to read/send
+ * from/to the client. If you need to add valid headers
* make sure to update this number *and* the list
* of valid headers in httpauthd.c
*/
-#define HA_MAX_HEADERS 2
+#define HA_MAX_HEADERS 8
/*
* The maximum number of handlers. If you add
@@ -183,6 +183,7 @@ typedef struct ha_request
/* Additional request info */
ha_context_t* context;
const char* digest_domain;
+ char** requested_groups;
/* The buffer in use for the request */
ha_buffer_t* buf;
diff --git a/daemon/ldap.c b/daemon/ldap.c
index a1aa1a1..f4324be 100644
--- a/daemon/ldap.c
+++ b/daemon/ldap.c
@@ -24,10 +24,16 @@
#include "md5.h"
#include "sha1.h"
#include "bd.h"
+#include "stringx.h"
#include <sys/time.h>
+#include <ctype.h>
+
+#define __USE_XOPEN
+#include <unistd.h>
/* LDAP library */
+#define LDAP_DEPRECATED 1
#include <ldap.h>
/* -------------------------------------------------------------------------------
@@ -73,6 +79,7 @@ typedef struct ldap_context
const char* base; /* Base for the filter */
const char* pw_attr; /* The clear password attribute */
const char* ha1_attr; /* Password for an encrypted Digest H(A1) */
+ const char* group_attr; /* The group attribute */
const char* user; /* User to bind as */
const char* password; /* Password to bind with */
const char* dnmap; /* For mapping users to dns */
@@ -90,8 +97,8 @@ typedef struct ldap_context
ldap_context_t;
/* Forward declarations for callbacks */
-static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg);
-static int validate_basic(ha_request_t* rq, const char* user, const char* password);
+static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg, char ***groups);
+static int validate_basic(ha_request_t* rq, const char* user, const char* password, char ***groups);
static void escape_ldap(const ha_request_t* rq, ha_buffer_t* buf, const char* value);
/* The defaults for the context */
@@ -104,6 +111,7 @@ static const ldap_context_t ldap_defaults =
NULL, /* base */
"userPassword", /* pw_attr */
NULL, /* ha1_attr */
+ NULL, /* group_attr */
NULL, /* user */
NULL, /* password */
NULL, /* dnmap */
@@ -563,10 +571,10 @@ static int retrieve_user_entry(const ha_request_t* rq, ldap_context_t* ctx, LDAP
{
struct timeval tv;
const char* filter;
- const char* attrs[3];
+ const char* attrs[4];
const char* base;
int scope;
- int r;
+ int r, i;
ASSERT(ctx && rq && ld && user && dn && entry && result);
@@ -582,9 +590,14 @@ static int retrieve_user_entry(const ha_request_t* rq, ldap_context_t* ctx, LDAP
filter = "(objectClass=*)";
}
- attrs[0] = ctx->dobind ? NULL : ctx->pw_attr;
- attrs[1] = ctx->dobind ? NULL : ctx->ha1_attr;
- attrs[2] = NULL;
+ i = 0;
+ if (ctx->group_attr)
+ attrs[i++] = ctx->group_attr;
+ if (ctx->dobind && ctx->pw_attr)
+ attrs[i++] = ctx->pw_attr;
+ if (ctx->dobind && ctx->ha1_attr)
+ attrs[i++] = ctx->ha1_attr;
+ attrs[i] = NULL;
tv.tv_sec = ctx->ldap_timeout;
tv.tv_usec = 0;
@@ -633,7 +646,43 @@ static int retrieve_user_entry(const ha_request_t* rq, ldap_context_t* ctx, LDAP
return HA_FALSE;
}
-static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg)
+static int
+get_user_groups (ldap_context_t *ctx, LDAP *ld, LDAPMessage *entry, char*** result)
+{
+ struct berval **vals, **v;
+ char **groups = NULL;
+ int ret = HA_OK;
+
+ if (!ctx->group_attr) {
+ *result = NULL;
+ return HA_OK;
+ }
+
+ groups = str_array_create (NULL);
+ vals = ldap_get_values_len (ld, entry, ctx->group_attr);
+ for (v = vals; v && *v; ++v) {
+ if ((*v)->bv_len) {
+ groups = str_array_appendn (groups, (*v)->bv_val, (*v)->bv_len);
+ if (!groups) {
+ ha_memerr (NULL);
+ ret = HA_CRITERROR;
+ break;
+ }
+ }
+ }
+
+ if (vals)
+ ldap_value_free_len (vals);
+ if (ret != HA_OK)
+ str_array_free (groups);
+ else
+ *result = groups;
+ return ret;
+}
+
+static int
+validate_digest (ha_request_t *rq, const char *user,
+ digest_context_t *dg, char ***groups)
{
ldap_context_t* ctx = (ldap_context_t*)rq->context->ctx_data;
LDAP* ld = NULL; /* freed in finally */
@@ -684,7 +733,7 @@ static int validate_digest(ha_request_t* rq, const char* user, digest_context_t*
for(h = ha1s; *h; ++h)
{
- r = parse_ldap_ha1(rq, *h, dg->ha1);
+ r = parse_ldap_ha1(rq, *h, dg->server_ha1);
if(r == HA_FALSE)
{
@@ -722,7 +771,7 @@ static int validate_digest(ha_request_t* rq, const char* user, digest_context_t*
{
foundany = 1;
- digest_makeha1(dg->ha1, user, rq->context->realm, password);
+ digest_makeha1(dg->server_ha1, user, rq->context->realm, password);
/* Run the actual check */
ret = digest_complete_check(dg, rq->context, rq->buf);
@@ -739,6 +788,13 @@ static int validate_digest(ha_request_t* rq, const char* user, digest_context_t*
finally:
+ /* Fill in the user's groups */
+ if (ret == HA_OK) {
+ r = get_user_groups (ctx, ld, entry, groups);
+ if (r < 0)
+ ret = r;
+ }
+
if(ha1s)
ldap_value_free_len(ha1s);
@@ -754,7 +810,9 @@ finally:
return ret;
}
-static int validate_basic(ha_request_t* rq, const char* user, const char* password)
+static int
+validate_basic (ha_request_t *rq, const char *user,
+ const char *password, char ***groups)
{
ldap_context_t* ctx = (ldap_context_t*)rq->context->ctx_data;
LDAP* ld = NULL;
@@ -845,7 +903,11 @@ static int validate_basic(ha_request_t* rq, const char* user, const char* passwo
/* Discard the connection since it's useless to us */
discard_ldap_connection(rq, ctx, ld);
ld = NULL;
- }
+ ret = HA_FALSE;
+
+ } else {
+ ret = HA_OK;
+ }
}
@@ -870,6 +932,12 @@ static int validate_basic(ha_request_t* rq, const char* user, const char* passwo
}
finally:
+ /* Fill in the user's groups */
+ if (ret == HA_OK) {
+ r = get_user_groups (ctx, ld, entry, groups);
+ if (r < 0)
+ ret = r;
+ }
if(results)
ldap_msgfree(results);
@@ -909,6 +977,12 @@ int ldap_config(ha_context_t* context, const char* name, const char* value)
return HA_OK;
}
+ else if(strcmp(name, "ldapgroupattr") == 0)
+ {
+ ctx->group_attr = value;
+ return HA_OK;
+ }
+
else if(strcmp(name, "ldappwattr") == 0)
{
ctx->pw_attr = value;
diff --git a/daemon/misc.c b/daemon/misc.c
index ca14ee3..49c585e 100644
--- a/daemon/misc.c
+++ b/daemon/misc.c
@@ -27,6 +27,7 @@
#include <err.h>
#include <stdio.h>
#include <fcntl.h>
+#include <unistd.h>
extern int g_debuglevel;
extern int g_daemonized;
@@ -53,7 +54,6 @@ static void vmessage(const ha_request_t* rq, int level, int err,
size_t len;
char* m;
int e = errno;
- int x;
if(g_daemonized)
{
@@ -390,7 +390,7 @@ int ha_uriparse(ha_buffer_t* buf, const char* suri, ha_uri_t* uri)
if(str[0] == '/')
{
deal_with_path:
- *str == 0;
+ *str = 0;
++str;
/*
@@ -611,7 +611,7 @@ int ha_uricmp(ha_uri_t* one, ha_uri_t* two)
/* The scheme */
if((r = uri_cmp_part_s(one->scheme, two->scheme, "http", 0)) != 0 ||
- (r = uri_cmp_part_s(one->host, two->host, NULL, 1)) != 0 != 0 ||
+ (r = uri_cmp_part_s(one->host, two->host, NULL, 1)) != 0 ||
(r = uri_cmp_part_n(one->port, two->port, 80)) != 0 ||
(r = uri_cmp_part_s(one->path, two->path, NULL, 0)) != 0 ||
(r = uri_cmp_part_s(one->query, two->query, NULL, 0)) != 0 ||
diff --git a/daemon/mysql.c b/daemon/mysql.c
index e926bb2..69a28ff 100644
--- a/daemon/mysql.c
+++ b/daemon/mysql.c
@@ -27,6 +27,9 @@
#include <sys/time.h>
+#define __USE_XOPEN
+#include <unistd.h>
+
/* Mysql library */
#include <mysql.h>
#include <errmsg.h>
@@ -48,14 +51,14 @@ typedef struct mysql_context
/* Readonly Settings ------------------------------------------------- */
const char* host; /* The connection host or path */
- unsigned int port; /* The connection port */
+ int port; /* The connection port */
const char* user; /* The pgsql user name */
const char* password; /* The pgsql password */
const char* database; /* The database name */
- const char* query; /* The query */
- const char* pw_column; /* The database query to retrieve a password */
+ const char* user_query; /* The database query to get the user info */
+ const char* pw_column; /* The database column with a password */
int pw_type; /* The type of password encoded in database */
- const char* ha1_column; /* The database query to retrieve a ha1 */
+ const char* ha1_column; /* The database column with a ha1 */
int mysql_max; /* Number of open connections allowed */
int mysql_timeout; /* Maximum amount of time to dedicate to a query */
@@ -68,8 +71,8 @@ typedef struct mysql_context
mysql_context_t;
/* Forward declarations for callbacks */
-static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg);
-static int validate_basic(ha_request_t* rq, const char* user, const char* password);
+static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg, char ***groups);
+static int validate_basic(ha_request_t* rq, const char* user, const char* password, char ***groups);
static void escape_mysql(const ha_request_t* rq, ha_buffer_t* buf, const char* value);
/* The defaults for the context */
@@ -82,7 +85,7 @@ static const mysql_context_t mysql_defaults =
NULL, /* user */
NULL, /* password */
NULL, /* database */
- NULL, /* query */
+ NULL, /* user_query */
NULL, /* pw_attr */
DB_PW_CLEAR, /* pw_type */
NULL, /* ha1_attr */
@@ -97,7 +100,7 @@ static const mysql_context_t mysql_defaults =
static pthread_mutex_t g_mysql_mutex;
static pthread_mutexattr_t g_mysql_mutexattr;
-
+extern int pthread_mutexattr_settype (pthread_mutexattr_t *attr, int kind);
/* -------------------------------------------------------------------------------
* Internal Functions
@@ -164,7 +167,6 @@ static int validate_ha1(ha_request_t* rq, mysql_context_t* ctx, const char* user
{
unsigned char dbha1[MD5_LEN];
unsigned char ha1[MD5_LEN];
- const char* p;
int r = dec_mysql_binary(rq, dbpw, dbha1, MD5_LEN);
@@ -344,7 +346,7 @@ static void discard_mysql_connection(const ha_request_t* rq, mysql_context_t* ct
static void save_mysql_connection(const ha_request_t* rq, mysql_context_t* ctx, MYSQL* my)
{
- int i, e;
+ int i;
ASSERT(ctx);
@@ -424,10 +426,10 @@ static int retrieve_user_rows(ha_request_t* rq, mysql_context_t* ctx,
if(!my)
RETURN(HA_FAILED);
- ASSERT(ctx->query);
+ ASSERT(ctx->user_query);
/* The map can have %u and %r to denote user and realm */
- query = bd_substitute(rq, user, ctx->query);
+ query = bd_substitute(rq, user, ctx->user_query);
if(!query)
RETURN(HA_CRITERROR);
@@ -472,7 +474,7 @@ finally:
return ret;
}
-static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg)
+static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg, char ***groups)
{
mysql_context_t* ctx = (mysql_context_t*)rq->context->ctx_data;
MYSQL_RES* res = NULL;
@@ -480,7 +482,7 @@ static int validate_digest(ha_request_t* rq, const char* user, digest_context_t*
int ret = HA_FALSE;
int pw_column = -1;
int ha1_column = -1;
- int r, i, foundany = 0;
+ int foundany = 0;
const char* v;
ASSERT(rq && user && dg);
@@ -511,7 +513,7 @@ static int validate_digest(ha_request_t* rq, const char* user, digest_context_t*
{
foundany = 1;
- digest_makeha1(dg->ha1, user, rq->context->realm, v);
+ digest_makeha1(dg->server_ha1, user, rq->context->realm, v);
ha_messagex(rq, LOG_DEBUG, "testing clear text password for digest auth");
/* Run the actual check */
@@ -527,7 +529,7 @@ static int validate_digest(ha_request_t* rq, const char* user, digest_context_t*
v = *(row + ha1_column);
if(v != NULL)
{
- ret = dec_mysql_binary(rq, v, dg->ha1, MD5_LEN);
+ ret = dec_mysql_binary(rq, v, dg->server_ha1, MD5_LEN);
if(ret < 0)
RETURN(ret)
else if(ret == HA_FALSE)
@@ -554,7 +556,7 @@ finally:
return ret;
}
-static int validate_basic(ha_request_t* rq, const char* user, const char* password)
+static int validate_basic(ha_request_t* rq, const char* user, const char* password, char ***groups)
{
mysql_context_t* ctx = (mysql_context_t*)rq->context->ctx_data;
MYSQL_RES* res = NULL;
@@ -562,7 +564,7 @@ static int validate_basic(ha_request_t* rq, const char* user, const char* passwo
int ret = HA_FALSE;
int pw_column = -1;
int ha1_column = -1;
- int i, foundany = 0;
+ int foundany = 0;
const char* v;
ASSERT(rq && user && password);
@@ -665,7 +667,7 @@ int mysql_config(ha_context_t* context, const char* name, const char* value)
if(strcmp(name, "dbquery") == 0)
{
- ctx->query = value;
+ ctx->user_query = value;
return HA_OK;
}
@@ -727,7 +729,7 @@ int mysql_initialize(ha_context_t* context)
ASSERT(ctx);
/* Check for mandatory configuration */
- if(!ctx->database || !ctx->query)
+ if(!ctx->database || !ctx->user_query)
{
ha_messagex(NULL, LOG_ERR, "mysql configuration incomplete. "
"Must have DBDatabase and DBQuery.");
diff --git a/daemon/ntlmssp.c b/daemon/ntlmssp.c
index ca8c5a8..1b51264 100644
--- a/daemon/ntlmssp.c
+++ b/daemon/ntlmssp.c
@@ -239,7 +239,7 @@ ntlm_msg3_getusername(unsigned char *raw_msg, unsigned msglen,
return 16;
else {
/* Win9x client leave username in uppercase...fix it: */
- while (*username!=(unsigned char)NULL) {
+ while (*username!=(unsigned char)0) {
c=tolower((int)*username);
*username=(unsigned char)c;
username++;
diff --git a/daemon/pgsql.c b/daemon/pgsql.c
index 0306cbf..f19624a 100644
--- a/daemon/pgsql.c
+++ b/daemon/pgsql.c
@@ -25,10 +25,17 @@
#include "sha1.h"
#include "bd.h"
+#define __USE_XOPEN
+#include <unistd.h>
+
#include <sys/time.h>
/* Postgresql library */
+#ifdef HAVE_LIBPQ_FE_H
#include <libpq-fe.h>
+#else
+#include <postgresql/libpq-fe.h>
+#endif
/* -------------------------------------------------------------------------------
* Structures
@@ -66,8 +73,8 @@ typedef struct pgsql_context
pgsql_context_t;
/* Forward declarations for callbacks */
-static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg);
-static int validate_basic(ha_request_t* rq, const char* user, const char* password);
+static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg, char ***groups);
+static int validate_basic(ha_request_t* rq, const char* user, const char* password, char ***groups);
static void escape_pgsql(const ha_request_t* rq, ha_buffer_t* buf, const char* value);
/* The defaults for the context */
@@ -136,7 +143,7 @@ static int dec_pgsql_binary(const ha_request_t* rq, const char* enc,
}
/* Raw binary postgres encoded */
- d = PQunescapeBytea(enc, &enclen);
+ d = PQunescapeBytea((const unsigned char*)enc, &enclen);
if(d != NULL)
{
if(enclen == len)
@@ -170,7 +177,6 @@ static int validate_ha1(ha_request_t* rq, pgsql_context_t* ctx, const char* user
{
unsigned char dbha1[MD5_LEN];
unsigned char ha1[MD5_LEN];
- const char* p;
int r = dec_pgsql_binary(rq, dbpw, dbha1, MD5_LEN);
@@ -383,7 +389,7 @@ static void discard_pgsql_connection(const ha_request_t* rq, pgsql_context_t* ct
static void save_pgsql_connection(const ha_request_t* rq, pgsql_context_t* ctx, PGconn* pg)
{
- int i, e;
+ int i;
ASSERT(ctx);
@@ -518,14 +524,14 @@ finally:
return ret;
}
-static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg)
+static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg, char ***groups)
{
pgsql_context_t* ctx = (pgsql_context_t*)rq->context->ctx_data;
PGresult* res = NULL;
int ret = HA_FALSE;
int pw_column = -1;
int ha1_column = -1;
- int r, i, foundany = 0;
+ int i, foundany = 0;
ASSERT(rq && user && dg);
@@ -554,7 +560,7 @@ static int validate_digest(ha_request_t* rq, const char* user, digest_context_t*
{
foundany = 1;
- digest_makeha1(dg->ha1, user, rq->context->realm, PQgetvalue(res, i, pw_column));
+ digest_makeha1(dg->server_ha1, user, rq->context->realm, PQgetvalue(res, i, pw_column));
ha_messagex(rq, LOG_DEBUG, "testing clear text password for digest auth");
/* Run the actual check */
@@ -569,7 +575,7 @@ static int validate_digest(ha_request_t* rq, const char* user, digest_context_t*
{
if(!PQgetisnull(res, i, ha1_column))
{
- ret = dec_pgsql_binary(rq, PQgetvalue(res, i, ha1_column), dg->ha1, MD5_LEN);
+ ret = dec_pgsql_binary(rq, PQgetvalue(res, i, ha1_column), dg->server_ha1, MD5_LEN);
if(ret < 0)
RETURN(ret)
else if(ret == HA_FALSE)
@@ -596,7 +602,7 @@ finally:
return ret;
}
-static int validate_basic(ha_request_t* rq, const char* user, const char* password)
+static int validate_basic(ha_request_t* rq, const char* user, const char* password, char ***groups)
{
pgsql_context_t* ctx = (pgsql_context_t*)rq->context->ctx_data;
PGresult* res = NULL;
diff --git a/daemon/simple.c b/daemon/simple.c
index 1c37c23..1e3cc64 100644
--- a/daemon/simple.c
+++ b/daemon/simple.c
@@ -28,9 +28,11 @@
#include "hash.h"
#include "bd.h"
#include "md5.h"
+#include "stringx.h"
#include <stdio.h>
#include <fcntl.h>
+#include <unistd.h>
#define SIMPLE_MAXLINE 256
@@ -49,8 +51,8 @@ typedef struct simple_context
simple_context_t;
/* Forward declarations for callbacks */
-static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg);
-static int validate_basic(ha_request_t* rq, const char* user, const char* password);
+static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg, char ***groups);
+static int validate_basic(ha_request_t* rq, const char* user, const char* password, char ***groups);
/* The defaults for the context */
static const simple_context_t simple_defaults =
@@ -63,7 +65,8 @@ static const simple_context_t simple_defaults =
* Internal Functions
*/
-static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg)
+static int validate_digest(ha_request_t* rq, const char* user,
+ digest_context_t* dg, char ***groups)
{
simple_context_t* ctx = (simple_context_t*)rq->context->ctx_data;
FILE* f;
@@ -142,7 +145,7 @@ static int validate_digest(ha_request_t* rq, const char* user, digest_context_t*
}
ha_messagex(rq, LOG_DEBUG, "found ha1 for user: %s", user);
- memcpy(dg->ha1, t, MD5_LEN);
+ memcpy(dg->server_ha1, t, MD5_LEN);
foundgood = 1;
/* Try to do the validation */
@@ -168,7 +171,8 @@ static int validate_digest(ha_request_t* rq, const char* user, digest_context_t*
return ret;
}
-static int validate_basic(ha_request_t* rq, const char* user, const char* password)
+static int validate_basic(ha_request_t* rq, const char* user,
+ const char* password, char ***groups)
{
simple_context_t* ctx = (simple_context_t*)rq->context->ctx_data;
FILE* f;
diff --git a/doc/httpauthd.conf.5 b/doc/httpauthd.conf.5
index 7bca8b6..567b0b6 100644
--- a/doc/httpauthd.conf.5
+++ b/doc/httpauthd.conf.5
@@ -243,6 +243,11 @@ should be taken that the filter only returns one record.
[ Required when
.Em LDAPDNMap
is missing ]
+.It Cd LDAPGroupAttr
+The name of an attribute on the LDAP server that contains the groups that
+the user is in.
+.Pp
+[ Optional ]
.It Cd LDAPHA1Attr
A HA1 is a special kind of digest containing the user name, realm and
password. This can be used in place of cleartext passwords when doing
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 75719b1..acfe56f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,10 +1,12 @@
UNIT_TESTS = \
- unit-test-ntlmssp.c
+ unit-test-ntlmssp.c \
+ unit-test-stringx.c
INCLUDES= \
-I$(top_srcdir) \
- -I$(top_builddir)
+ -I$(top_builddir) \
+ -I$(top_srcdir)/common
noinst_PROGRAMS= \
run-tests
diff --git a/tests/unit-test-ntlmssp.c b/tests/unit-test-ntlmssp.c
index b12102f..8d8a647 100644
--- a/tests/unit-test-ntlmssp.c
+++ b/tests/unit-test-ntlmssp.c
@@ -93,5 +93,4 @@ void unit_test_ntlmssp_decode (CuTest *cu)
*/
#include "common/buffer.c"
-#include "common/stringx.c"
#include "daemon/ntlmssp.c"
diff --git a/tests/unit-test-stringx.c b/tests/unit-test-stringx.c
new file mode 100644
index 0000000..e1bb2a2
--- /dev/null
+++ b/tests/unit-test-stringx.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2008, 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 "config.h"
+
+#include "run-tests.h"
+
+#include "common/stringx.h"
+
+static const char* PARSES[] = {
+ "one 'twenty \"two\"' 'three",
+ " one 'twenty \"two\"' 'three' \n",
+ "one twenty\\ \\\"two\\\" \nthree ",
+ " one\n \"twenty \\\"two\\\"\" three",
+ NULL
+};
+
+/*
+ * Each test looks like (on one line):
+ * void unit_test_xxxxx (CuTest* cu)
+ *
+ * Each setup looks like (on one line):
+ * void unit_setup_xxxxx (void)
+ *
+ * Each teardown looks like (on one line):
+ * void unit_teardown_xxxxx (void)
+ *
+ * Tests be run in the order specified here.
+ */
+
+void unit_test_str_array_general (CuTest *cu)
+{
+ char** array;
+
+ array = str_array_create ("one", "two", "three", NULL);
+ CuAssertPtrNotNull (cu, array);
+ CuAssertStrEquals (cu, "one", array[0]);
+ CuAssertStrEquals (cu, "two", array[1]);
+ CuAssertStrEquals (cu, "three", array[2]);
+
+ array = str_array_append (array, "four");
+ CuAssertStrEquals (cu, "four", array[3]);
+
+ array = str_array_appendn (array, "five and fifty", 4);
+ CuAssertStrEquals (cu, "five", array[4]);
+
+ str_array_free (array);
+}
+
+void unit_test_str_array_parse (CuTest *cu)
+{
+ char** array;
+ int i;
+
+ for (i = 0; PARSES[i]; ++i) {
+ array = str_array_parse_quoted (PARSES[i]);
+ // fprintf (stderr, "%d --- %s", i, PARSES[i]);
+ CuAssertPtrNotNull (cu, array);
+ CuAssertIntEquals (cu, 3, str_array_length (array));
+ CuAssertStrEquals (cu, "one", array[0]);
+ CuAssertStrEquals (cu, "twenty \"two\"", array[1]);
+ CuAssertStrEquals (cu, "three", array[2]);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Code being tested
+ */
+
+#include "common/stringx.c"
diff --git a/tools/mkha1.c b/tools/mkha1.c
index 97c7dcd..c61c725 100644
--- a/tools/mkha1.c
+++ b/tools/mkha1.c
@@ -41,6 +41,7 @@
#include <stdio.h>
#include <unistd.h>
#include <string.h>
+#include <err.h>
#include "compat.h"
#include "buffer.h"
@@ -147,9 +148,9 @@ static char* check_value(ha_buffer_t* buf, char* value, const char* name)
}
if(value[0] == 0)
- errx("%s is empty");
+ errx(1, "%s is empty", name);
if(strchr(value, ':') != NULL)
- errx("%s must be a not contain colons");
+ errx(1, "%s must be a not contain colons", name);
return value;
}