summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2004-04-28 20:59:48 +0000
committerStef Walter <stef@memberwebs.com>2004-04-28 20:59:48 +0000
commitdbbf162dc9be0aef47f2d1f1fcddb7ae4e074d47 (patch)
treec06b131bc9f249557d9891d07977c4f0f3d0f15b
parent8368de7830f336533f9fe6369641070239bf739c (diff)
Tons of fixes, debugging, changes, added apache module
-rw-r--r--apache1x/Makefile40
-rw-r--r--apache1x/mod_httpauth.c722
-rw-r--r--common/buffer.c6
-rw-r--r--common/sock_any.c152
-rw-r--r--common/sock_any.h32
-rw-r--r--daemon/Makefile.am3
-rw-r--r--daemon/defaults.h1
-rw-r--r--daemon/httpauthd.c353
-rw-r--r--daemon/httpauthd.h35
-rw-r--r--daemon/ldap.c219
-rw-r--r--daemon/misc.c72
-rw-r--r--daemon/ntlm.c138
-rw-r--r--daemon/simple.c143
-rw-r--r--doc/httpauth.conf.582
-rw-r--r--doc/httpauth.conf.sample0
-rw-r--r--doc/protocol.txt95
-rw-r--r--sample/httpauthd.conf1
17 files changed, 1744 insertions, 350 deletions
diff --git a/apache1x/Makefile b/apache1x/Makefile
new file mode 100644
index 0000000..3ddc156
--- /dev/null
+++ b/apache1x/Makefile
@@ -0,0 +1,40 @@
+##
+## Makefile -- Build procedure for sample httpauth Apache module
+## Autogenerated via ``apxs -n httpauth -g''.
+##
+
+# the used tools
+APXS=apxs
+APACHECTL=apachectl
+
+# additional user defines, includes and libraries
+#DEF=-Dmy_define=my_value
+INC=-I../ -I../common/
+
+# the default target
+all: mod_httpauth.so
+
+# compile the DSO file
+mod_httpauth.so: mod_httpauth.c ../common/sock_any.c
+ $(APXS) -c -Wc,-g -Wc,-O0 $(DEF) $(INC) $(LIB) mod_httpauth.c ../common/sock_any.c
+
+# install the DSO file into the Apache installation
+# and activate it in the Apache configuration
+install: all
+ $(APXS) -i -a -n 'httpauth' mod_httpauth.so
+
+# cleanup
+clean:
+ -rm -f mod_httpauth.o mod_httpauth.so
+
+# reload the module by installing and restarting Apache
+reload: install restart
+
+# the general Apache start/restart/stop procedures
+start:
+ $(APACHECTL) start
+restart:
+ $(APACHECTL) restart
+stop:
+ $(APACHECTL) stop
+
diff --git a/apache1x/mod_httpauth.c b/apache1x/mod_httpauth.c
new file mode 100644
index 0000000..b112120
--- /dev/null
+++ b/apache1x/mod_httpauth.c
@@ -0,0 +1,722 @@
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_log.h>
+#include <http_protocol.h>
+#include <ap_config.h>
+#include <ap_alloc.h>
+
+#include "sock_any.h"
+
+#define DEFAULT_PORT 8020
+
+module MODULE_VAR_EXPORT httpauth_module;
+
+typedef struct httpauth_context
+{
+ const char* socketname;
+ int socket;
+ const char* method;
+ int types;
+ char* authtypes;
+ pool* child_pool;
+}
+httpauth_context_t;
+
+/* TODO: Support proxy authentication properly */
+
+#define AUTH_PREFIX_BASIC "Basic"
+#define AUTH_PREFIX_DIGEST "Digest"
+#define AUTH_PREFIX_NTLM "NTLM"
+
+#define AUTH_TYPE_BASIC 1 << 1
+#define AUTH_TYPE_DIGEST 1 << 2
+#define AUTH_TYPE_NTLM 1 << 3
+#define AUTH_TYPE_ANY 0x0000FFFF
+
+#define HTTPAUTH_AUTHTYPE "HTTPAUTH"
+
+/* -------------------------------------------------------------------------------
+ * Configuration code
+ */
+
+static void* httpauth_dir_config(pool* p, char* dir)
+{
+ httpauth_context_t* ctx;
+
+ ctx = (httpauth_context_t*)ap_pcalloc(p, sizeof(*ctx));
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->socket = -1;
+ ctx->types = 0xFFFFFFFF;
+ ctx->child_pool = p;
+ return ctx;
+}
+
+static const char* set_socket(cmd_parms* cmd, void* config, const char* val)
+{
+ struct sockaddr_any sany;
+
+ if(sock_any_pton(val, &sany, DEFAULT_PORT) == -1)
+ return "Invalid socket name or ip in HttpAuthSocket";
+
+ ((httpauth_context_t*)config)->socketname = val;
+ return NULL;
+}
+
+static const char* set_method(cmd_parms* cmd, void* config, const char* val)
+{
+ httpauth_context_t* conf = (httpauth_context_t*)config;
+ conf->method = ap_pstrdup(cmd->pool, val);
+ return NULL;
+}
+
+static const char* set_types(cmd_parms* cmd, void* config, const char* val)
+{
+ httpauth_context_t* conf = (httpauth_context_t*)config;
+ int type = 0;
+
+ if(strcasecmp(val, AUTH_PREFIX_BASIC) == 0)
+ type = AUTH_TYPE_BASIC;
+ else if(strcasecmp(val, AUTH_PREFIX_DIGEST) == 0)
+ type = AUTH_TYPE_DIGEST;
+ else if(strcasecmp(val, AUTH_PREFIX_NTLM) == 0)
+ type = AUTH_TYPE_NTLM;
+ else if(strcasecmp(val, "any"))
+ type = AUTH_TYPE_ANY;
+ else
+ return "Invalid type in HttpAuthTypes";
+
+ if(conf->types == 0xFFFFFFFF)
+ conf->types = type;
+ else
+ conf->types |= type;
+
+ return NULL;
+}
+
+static const command_rec httpauth_cmds[] =
+{
+ { "HttpAuthSocket", set_socket, NULL, OR_AUTHCFG, TAKE1,
+ "The socket that httpauthd is listening on" },
+ { "HttpAuthMethod", set_method, NULL, OR_AUTHCFG, TAKE1,
+ "The method that httpauthd should use to authenticate" },
+ { "HttpAuthTypes", set_types, NULL, OR_AUTHCFG, ITERATE,
+ "The types of authentiction allowed (Basic, Digest, NTLM ...)" },
+ { NULL, NULL, NULL, 0, 0, NULL }
+};
+
+/* -------------------------------------------------------------------------------
+ * Socket handling code
+ */
+
+const char* trim_start(const char* data)
+{
+ while(*data && ap_isspace(*data))
+ ++data;
+ return data;
+}
+
+char* trim_end(char* data)
+{
+ char* t = data + strlen(data);
+
+ while(t > data && ap_isspace(*(t - 1)))
+ {
+ t--;
+ *t = 0;
+ }
+
+ return data;
+}
+
+char* trim_space(char* data)
+{
+ data = (char*)trim_start(data);
+ return trim_end(data);
+}
+
+void read_junk(httpauth_context_t* ctx, request_rec* r)
+{
+ char buf[16];
+ const char* t;
+ int said = 0;
+ int l;
+
+ if(ctx->socket == -1)
+ return;
+
+ /* Make it non blocking */
+ fcntl(ctx->socket, F_SETFL, fcntl(ctx->socket, F_GETFL, 0) | O_NONBLOCK);
+
+ for(;;)
+ {
+ l = read(ctx->socket, buf, sizeof(buf) - 1);
+ if(l <= 0)
+ break;
+
+ buf[l] = 0;
+ t = trim_start(t);
+
+ if(!said && *t)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+ "httpauth: received junk data from daemon");
+ said = 1;
+ }
+ }
+
+ fcntl(ctx->socket, F_SETFL, fcntl(ctx->socket, F_GETFL, 0) & ~O_NONBLOCK);
+ errno = 0;
+}
+
+int read_line(httpauth_context_t* ctx, request_rec* r, char** line)
+{
+ int l;
+ int al = 128;
+ char* t;
+ const char* e;
+
+ e = t = NULL;
+ *line = NULL;
+
+ for(;;)
+ {
+ if(!*line || t + 2 == e)
+ {
+ char* n;
+ int d;
+
+ n = (char*)ap_palloc(r->pool, al * 2);
+
+ if(*line)
+ memcpy(n, *line, al);
+
+ al *= 2;
+
+ /* The difference */
+ d = t - *line;
+
+ *line = n;
+ t = n + d;
+ e = n + al;
+ }
+
+ l = read(ctx->socket, (void*)t, sizeof(char));
+
+ /* We got a character */
+ if(l == 1)
+ {
+ /* Skip junky CRLFs */
+ if(*t == '\r')
+ {
+ *t = ' ';
+ continue;
+ }
+
+ /* End of line */
+ else if(*t == '\n')
+ {
+ t++;
+ break;
+ }
+
+ t++;
+ }
+
+ /* If it's the end of file then return that */
+ else if(l == 0)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "httpauth: unexpected end of data from daemon");
+ return -1;
+ }
+
+ /* Transient errors */
+ else if(l == -1 && errno == EAGAIN)
+ continue;
+
+ /* Fatal errors */
+ else if(l == -1)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "httpauth: couldn't read data from daemon");
+ return -1;
+ }
+ }
+
+ *t = 0;
+ errno = 0;
+ return 0;
+}
+
+int read_response(httpauth_context_t* ctx, request_rec* r,
+ int* code, int* ccode, char** details)
+{
+ int c;
+ 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, 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, r,
+ "httpauth: protocol error: invalid code: %s", t);
+ return -1;
+ }
+
+ 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, r,
+ "httpauth: protocol error: invalid code: %s", t);
+ return -1;
+ }
+
+ if(ccode)
+ *ccode = c;
+ }
+
+ *details = trim_space(line);
+ return 0;
+}
+
+int read_copy_headers(httpauth_context_t* ctx, int ccode, request_rec* r)
+{
+ char* line;
+ const char* name;
+ table* headers;
+ int c = 0;
+
+ if(ccode > 299)
+ headers = r->err_headers_out;
+ else
+ headers = r->headers_out;
+
+ for(;;)
+ {
+ if(read_line(ctx, r, &line) == -1)
+ return -1;
+
+ /* If that's it then break */
+ if(!*line)
+ break;
+
+ if(ap_isspace(*line))
+ {
+ line = (char*)trim_start(line);
+
+ /* End of headers */
+ if(!*line)
+ break;
+
+ if(c > 0)
+ {
+ /*
+ * TODO: We really should be supporting headers split
+ * across lines. But httpauthd doesn't currently produce
+ * headers like that, so we don't need to care about it.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+ "httpauth: protocol error: server sent us an split header, which we don't support.");
+ }
+ else
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "httpauth: protocol error: invalid headers.");
+ }
+ }
+
+ name = ap_getword_nc(r->pool, &line, ':');
+ if(!name || !*name)
+ break;
+
+ /*
+ * If that was the end of the line, then it's an
+ * invalid header :(
+ */
+ if(!*line)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "httpauth: protocol header: invalid headers");
+ return -1;
+ }
+
+ line = trim_space(line);
+
+ /* Fix up when we're a proxy */
+ if(r->proxyreq == STD_PROXY)
+ {
+ if(strcasecmp(name, "WWW-Authenticate") == 0)
+ name = "Proxy-Authenticate";
+ else if(strcasecmp(name, "Authentication-Info") == 0)
+ name = "Proxy-Authentication-Info";
+ }
+
+ c++;
+ ap_table_addn(headers, name, line);
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r,
+ "httpauth: received %d headers from daemon", c);
+
+ return 0;
+}
+
+void disconnect_socket(httpauth_context_t* ctx, server_rec* s)
+{
+ if(ctx->socket != -1)
+ {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, s,
+ "httpauth: disconnecting from daemon");
+
+ /* TODO: Should we be closing this nicely somewhere? */
+ ap_pclosesocket(ctx->child_pool, ctx->socket);
+ ctx->socket = -1;
+ errno = 0;
+ }
+}
+
+int connect_socket(httpauth_context_t* ctx, request_rec* r)
+{
+ struct sockaddr_any sany;
+ int ret = -1;
+
+ disconnect_socket(ctx, r->server);
+
+ if(sock_any_pton(ctx->socketname, &sany, DEFAULT_PORT) == -1)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_CRIT, r,
+ "httpauth: Invalid socket name or ip.");
+ goto finally;
+ }
+
+ ctx->socket = ap_psocket(ctx->child_pool, SANY_TYPE(sany), SOCK_STREAM, 0);
+ if(ctx->socket == -1)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_CRIT, r,
+ "httpauth: Can't create socket: %s", ctx->socketname);
+ goto finally;
+ }
+
+ if(connect(ctx->socket, SANY_ADDR(sany), SANY_LEN(sany)) != 0)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_CRIT, r,
+ "httpauth: Can't connect to httpauthd");
+ goto finally;
+ }
+
+ ret = 0;
+
+finally:
+ if(ret == -1)
+ disconnect_socket(ctx, r->server);
+
+ errno = 0;
+ return ret;
+}
+
+int connect_httpauth(httpauth_context_t* ctx, request_rec* r)
+{
+ int ret = -1;
+ int code;
+ char* details;
+ const char* t;
+
+ if(connect_socket(ctx, r) == -1)
+ goto finally;
+
+ if(read_response(ctx, r, &code, NULL, &details) == -1)
+ goto finally;
+
+ if(code >= 400)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "httpauth: received error from httpauthd: %d", code);
+ goto finally;
+ }
+
+ if(code != 100)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "httpauth: protocol error (Expected 100, got %d)", code);
+ goto finally;
+ }
+
+ /*
+ * Not pretty code :) In order to keep from parsing up
+ * the whole available types string that we get from the
+ * client, and keeping an array etc... we just make sure
+ * that the auth type requested is in the string, and
+ * it's on word boundaries.
+ */
+
+ t = ap_strcasestr(details, ctx->method);
+ if(t)
+ {
+ /* Make sure we're at a parse mark */
+ if(t == details || ap_isspace(*(t - 1)))
+ {
+ /* Make sure end is at a parse mark */
+ t += strlen(ctx->method);
+ if(!*t || ap_isspace(*t))
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "httpauth: connected to daemon (methods: %s)", details);
+
+ /* We're cool! */
+ ret = 0;
+ goto finally;
+ }
+ }
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "httpauth: The configured method '%s' is not provided by httpauthd: %s",
+ ctx->method, details);
+
+finally:
+ if(ret == -1)
+ disconnect_socket(ctx, r->server);
+
+ return ret;
+}
+
+int write_data(httpauth_context_t* ctx, server_rec* s, const char* data)
+{
+ int r;
+
+ if(ctx->socket == -1)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR, s,
+ "httpauth: Socket to httpauthd daemon closed. Can't write data.");
+ return -1;
+ }
+
+ while(*data != 0)
+ {
+ r = write(ctx->socket, data, strlen(data));
+
+ if(r > 0)
+ data += r;
+
+ else if(r == -1)
+ {
+ if(errno == EAGAIN)
+ continue;
+
+ /* The other end closed. no message */
+ if(errno == EPIPE)
+ disconnect_socket(ctx, s);
+
+ else
+ ap_log_error(APLOG_MARK, APLOG_ERR, s,
+ "httpauth: Couldn't write data to daemon");
+
+ errno = 0;
+ return -1;
+ }
+ }
+
+ errno = 0;
+ return 0;
+}
+
+int write_request(httpauth_context_t* ctx, request_rec* r)
+{
+ int i, c = 0;
+ const char* t;
+ const array_header* hdrs_arr;
+ const table_entry* elts;
+
+ /*
+ * TODO: We need to use a valid connection id for
+ * NTLM connections to work properly.
+ */
+
+ /* Send the request header to httpauthd */
+ t = ap_pstrcat(r->pool, "AUTH ", ctx->method, " XXX ", r->method,
+ " ", r->unparsed_uri, "\n", NULL);
+
+ if(write_data(ctx, r->server, t) == -1)
+ return -1;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r,
+ "httpauth: sent auth request to daemon: %s", t);
+
+ /* Now send the headers to httpauthd */
+
+ hdrs_arr = ap_table_elts(r->headers_in);
+ elts = (const table_entry*)hdrs_arr->elts;
+
+ for(i = 0; i < hdrs_arr->nelts; i++)
+ {
+ if(!elts[i].val)
+ continue;
+
+ /* Filter out headers we don't want */
+ if(strcasecmp(elts[i].key, r->proxyreq == STD_PROXY ?
+ "Proxy-Authorization" : "Authorization") == 0)
+ {
+ t = trim_start(elts[i].val);
+
+ if(strncasecmp(t, AUTH_PREFIX_BASIC, strlen(AUTH_PREFIX_BASIC)) == 0 &&
+ !(ctx->types & AUTH_TYPE_BASIC))
+ continue;
+
+ else if(strncasecmp(t, AUTH_PREFIX_DIGEST, strlen(AUTH_PREFIX_DIGEST)) == 0 &&
+ !(ctx->types & AUTH_TYPE_DIGEST))
+ continue;
+
+ else if(strncasecmp(t, AUTH_PREFIX_NTLM, strlen(AUTH_PREFIX_NTLM)) == 0 &&
+ !(ctx->types & AUTH_TYPE_NTLM))
+ continue;
+
+ /* Only allow unknown if we don't have it */
+ else if(!(ctx->types & AUTH_TYPE_ANY))
+ continue;
+
+ /* Extra blank line when at end */
+ t = ap_pstrcat(r->pool, "Authorization: ", elts[i].val, "\n", NULL);
+
+ if(write_data(ctx, r->server, t) == -1)
+ return SERVER_ERROR;
+
+ c++;
+ }
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r,
+ "httpauth: sent %d headers to daemon", c);
+
+ return write_data(ctx, r->server, "\n");
+}
+
+static int httpauth_authenticate(request_rec* r)
+{
+ httpauth_context_t* ctx;
+ const char* authtype;
+ int code = 0;
+ int ccode = 0;
+ char* details = NULL;
+
+ /* 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(!ctx->socketname || !ctx->method)
+ return DECLINED;
+
+ if(ctx->socket == -1)
+ {
+ if(connect_httpauth(ctx, r) == -1)
+ return SERVER_ERROR;
+ }
+
+ /* Make sure we're starting on a clean slate */
+ read_junk(ctx, r);
+
+ /* Send off a request */
+ if(write_request(ctx, r) == -1)
+ return SERVER_ERROR;
+
+ /* Read a response line */
+ if(read_response(ctx, r, &code, &ccode, &details) == -1)
+ return SERVER_ERROR;
+
+ if(code >= 400 && code < 600)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "httpauth: received server error from httpauthd: %d%s%s%s",
+ code, details ? " (" : "", details ? details : "", details ? ")" : "");
+ return SERVER_ERROR;
+ }
+
+ if(code != 200)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "httpauth: protocol error: unexpected code: %d", code);
+ return SERVER_ERROR;
+ }
+
+ /* Copy over other headers */
+ if(read_copy_headers(ctx, ccode, r) == -1)
+ return SERVER_ERROR;
+
+ if(ccode == 200)
+ {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "httpauth: successful authentication for user: %s", details);
+
+ (char*)(r->connection->user) = ap_pstrdup(r->connection->pool, details);
+ r->connection->ap_auth_type = HTTPAUTH_AUTHTYPE;
+ return OK;
+ }
+
+ return ccode;
+}
+
+static int httpauth_access(request_rec *r)
+{
+ /* TODO: We need to support require directives */
+ return OK;
+}
+
+/* Dispatch list for API hooks */
+module MODULE_VAR_EXPORT httpauth_module =
+{
+ STANDARD_MODULE_STUFF,
+ NULL, /* module initializer */
+ httpauth_dir_config, /* create per-dir config structures */
+ NULL, /* merge per-dir config structures */
+ NULL, /* create per-server config structures */
+ NULL, /* merge per-server config structures */
+ httpauth_cmds, /* table of config file commands */
+ NULL, /* [#8] MIME-typed-dispatched handlers */
+ NULL, /* [#1] URI to filename translation */
+ httpauth_authenticate, /* [#4] validate user id from request */
+ httpauth_access, /* [#5] check if the user is ok _here_ */
+ NULL, /* [#3] check access by host address */
+ NULL, /* [#6] determine MIME type */
+ NULL, /* [#7] pre-run fixups */
+ NULL, /* [#9] log a transaction */
+ NULL, /* [#2] header parser */
+ NULL, /* child_init */
+ NULL, /* child_exit */
+ NULL /* [#0] post read-request */
+#ifdef EAPI
+ ,NULL, /* EAPI: add_module */
+ NULL, /* EAPI: remove_module */
+ NULL, /* EAPI: rewrite_command */
+ NULL /* EAPI: new_connection */
+#endif
+};
+
+/*
+ * Apache modules seem to want to be all in one
+ * object file, at least when statically compiled.
+ * so we include this here
+ */
+#include "../common/sock_any.c"
diff --git a/common/buffer.c b/common/buffer.c
index 57d20e2..72f0b6c 100644
--- a/common/buffer.c
+++ b/common/buffer.c
@@ -214,13 +214,15 @@ int ha_bufreadline(int fd, ha_buffer_t* buf)
return 0;
/* Transient errors */
- else if(l == -1 && (errno == EINTR || errno == EAGAIN))
+ else if(l == -1 && errno == EAGAIN)
continue;
/* Fatal errors */
else if(l == -1)
{
- ha_message(LOG_ERR, "couldn't read data");
+ if(errno != EINTR)
+ ha_message(LOG_ERR, "couldn't read data");
+
return 0;
}
}
diff --git a/common/sock_any.c b/common/sock_any.c
new file mode 100644
index 0000000..32db795
--- /dev/null
+++ b/common/sock_any.c
@@ -0,0 +1,152 @@
+
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "sock_any.h"
+
+int sock_any_pton(const char* addr, struct sockaddr_any* any, int defport)
+{
+ size_t l;
+ const char* t;
+
+ memset(any, 0, sizeof(*any));
+
+ /* Look and see if we can parse an ipv4 address */
+ do
+ {
+ #define IPV4_CHARS "0123456789."
+ #define IPV4_MIN 3
+ #define IPV4_MAX 18
+
+ int port = 0;
+ char buf[IPV4_MAX + 1];
+
+ t = NULL;
+
+ l = strlen(addr);
+ if(l < IPV4_MIN || l > IPV4_MAX)
+ break;
+
+ strcpy(buf, addr);
+
+ /* Find the last set that contains just numbers */
+ l = strspn(buf, IPV4_CHARS);
+ if(l < IPV4_MIN)
+ break;
+
+ /* Either end of string or port */
+ if(buf[l] != 0 && buf[l] != ':')
+ break;
+
+ /* Get the port out */
+ if(buf[l] != 0)
+ {
+ t = buf + l + 1;
+ buf[l] = 0;
+ }
+
+ if(t)
+ {
+ char* t2;
+ port = strtol(t, &t2, 10);
+ if(*t2 || port <= 0 || port >= 65536)
+ break;
+ }
+
+ any->s.in.sin_family = AF_INET;
+ any->s.in.sin_port = htons((unsigned short)(port <= 0 ? defport : port));
+
+ if(inet_pton(AF_INET, buf, &(any->s.in.sin_addr)) <= 0)
+ break;
+
+ any->namelen = sizeof(any->s.in);
+ return AF_INET;
+ }
+ while(0);
+
+#ifdef HAVE_INET6
+ do
+ {
+ #define IPV6_CHARS "0123456789:"
+ #define IPV6_MIN 3
+ #define IPV6_MAX 48
+
+ int port = -1;
+ char buf[IPV6_MAX + 1];
+
+ t = NULL;
+
+ l = strlen(addr);
+ if(l < IPV6_MIN || l > IPV6_MAX)
+ break;
+
+ /* If it starts with a '[' then we can get port */
+ if(buf[0] == '[')
+ {
+ port = 0;
+ addr++;
+ }
+
+ strcpy(buf, addr);
+
+ /* Find the last set that contains just numbers */
+ l = strspn(buf, IPV6_CHARS);
+ if(l < IPV6_MIN)
+ break;
+
+ /* Either end of string or port */
+ if(buf[l] != 0)
+ {
+ /* If had bracket, then needs to end with a bracket */
+ if(port != 0 || buf[l] != ']')
+ break;
+
+ /* Get the port out */
+ t = buf + l + 1;
+
+ if(*t = ':')
+ t++;
+ }
+
+ if(t)
+ {
+ port = strtol(t, &t, 10);
+ if(*t || port <= 0 || port >= 65536)
+ break;
+ }
+
+ any->s.in6.sin6_family = AF_INET6;
+ any->s.in6.sin6_port = htons((unsigned short)port <= 0 : defport : port);
+
+ if(inet_pton(AF_INET6, buf, &(any->s.in6.sin6_addr)) >= 0)
+ break;
+
+ any->namelen = sizeof(any->s.in6);
+ return AF_INET6;
+ }
+ while(0);
+#endif
+
+ do
+ {
+ if(strchr(addr, ':'))
+ break;
+
+ l = strlen(addr);
+ if(l >= sizeof(any->s.un.sun_path))
+ break;
+
+ any->s.un.sun_family = AF_UNIX;
+ strcpy(any->s.un.sun_path, addr);
+
+ any->namelen = sizeof(any->s.un) - (sizeof(any->s.un.sun_path) - l);
+ return AF_UNIX;
+ }
+ while(0);
+
+ return -1;
+}
+
diff --git a/common/sock_any.h b/common/sock_any.h
new file mode 100644
index 0000000..f281020
--- /dev/null
+++ b/common/sock_any.h
@@ -0,0 +1,32 @@
+
+#ifndef __SOCK_ANY_H__
+#define __SOCK_ANY_H__
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+struct sockaddr_any
+{
+ size_t namelen;
+ union _sockaddr_any
+ {
+ /* The header */
+ struct sockaddr a;
+
+ /* The different types */
+ struct sockaddr_un un;
+ struct sockaddr_in in;
+#ifdef HAVE_INET6
+ struct sockaddr_in6 in6;
+#endif
+ } s;
+};
+
+#define SANY_ADDR(any) (&((any).s.a))
+#define SANY_LEN(any) ((any).namelen)
+#define SANY_TYPE(any) ((any).s.a.sa_family)
+
+int sock_any_pton(const char* addr, struct sockaddr_any* any, int defport);
+
+#endif /* __SOCK_ANY_H__ */
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index c93e89f..39a0532 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -4,10 +4,11 @@ bin_PROGRAMS = httpauthd
httpauthd_SOURCES = httpauthd.c httpauthd.h usuals.h compat.h compat.c \
buffer.c misc.c basic.h basic.c ntlm.c hash.c hash.h ntlmssp.h ntlmssp.c \
md5.c md5.h sha1.c sha1.h digest.h digest.c ldap.c defaults.h simple.c \
+ ../common/sock_any.c ../common/sock_any.h \
smblib/smblib.c smblib/smblib-util.c smblib/file.c smblib/smb-errors.c smblib/exper.c smblib/smblib-api.c \
rfcnb/rfcnb-io.c rfcnb/rfcnb-util.c rfcnb/session.c
-httpauthd_CFLAGS = -DLinux
+httpauthd_CFLAGS = -DLinux -I../common/ -I../
# man_MANS = rtfm.1
EXTRA_DIST = protocol.txt smblib rfcnb
diff --git a/daemon/defaults.h b/daemon/defaults.h
index a72e51a..828bec1 100644
--- a/daemon/defaults.h
+++ b/daemon/defaults.h
@@ -6,5 +6,6 @@
#define DEFAULT_PENDING_TIMEOUT 60
#define DEFAULT_TIMEOUT 900
#define DEFAULT_CACHEMAX 1024
+#define DEFAULT_PORT 8020
#endif /* __DEFAULTS_H__ */
diff --git a/daemon/httpauthd.c b/daemon/httpauthd.c
index 2dba6c2..8c0f000 100644
--- a/daemon/httpauthd.c
+++ b/daemon/httpauthd.c
@@ -10,12 +10,12 @@
#include <pthread.h>
#include <fcntl.h>
#include <err.h>
-#include <sys/un.h>
-#include <sys/socket.h>
+#include <signal.h>
#include "usuals.h"
#include "httpauthd.h"
#include "defaults.h"
+#include "sock_any.h"
/*
* This shouldn't be used by handlers,
@@ -100,7 +100,8 @@ httpauth_thread_t;
*/
int g_daemonized = 0; /* Currently running as a daemon */
-int g_debugging = 0; /* In debug mode */
+int g_console = 0; /* debug mode read write from console */
+int g_debuglevel = LOG_ERR; /* what gets logged to console */
const char* g_socket = DEFAULT_SOCKET; /* The socket to communicate on */
int g_maxthreads = DEFAULT_MAXTHREADS; /* The maximum number of threads */
@@ -118,13 +119,14 @@ pthread_mutexattr_t g_mutexattr;
* Forward Declarations
*/
-int usage();
-void* httpauth_thread(void* arg);
-int httpauth_processor(int ifd, int ofd);
-int process_auth(ha_request_t* req, ha_response_t* resp,
- ha_buffer_t* outb);
-int config_parse(const char* file, ha_buffer_t* buf);
-
+static int usage();
+static void* httpauth_thread(void* arg);
+static int httpauth_processor(int ifd, int ofd);
+static int httpauth_respond(int ofd, int scode, int ccode, const char* msg);
+static int process_auth(ha_request_t* req, ha_response_t* resp,
+ ha_buffer_t* outb);
+static int config_parse(const char* file, ha_buffer_t* buf);
+static void on_quit(int signal);
/* -----------------------------------------------------------------------
* Main Program
@@ -150,13 +152,21 @@ int main(int argc, char* argv[])
errx(1, "threading problem. can't create mutex");
/* Parse the arguments nicely */
- while((ch = getopt(argc, argv, "df:X")) != -1)
+ while((ch = getopt(argc, argv, "d:f:X")) != -1)
{
switch(ch)
{
/* Don't daemonize */
case 'd':
- daemonize = 0;
+ {
+ char* t;
+
+ daemonize = 0;
+ g_debuglevel = strtol(optarg, &t, 10);
+ if(*t || g_debuglevel > 4)
+ errx(1, "invalid debug log level");
+ g_debuglevel += LOG_ERR;
+ }
break;
/* The configuration file */
@@ -166,7 +176,8 @@ int main(int argc, char* argv[])
/* Process console input instead */
case 'X':
- g_debugging = 1;
+ g_console = 1;
+ daemonize = 0;
break;
/* Usage information */
@@ -183,6 +194,7 @@ int main(int argc, char* argv[])
if(argc != 0)
return usage();
+ ha_messagex(LOG_DEBUG, "starting up...");
/* From here on out we need to quit in an orderly fashion */
@@ -204,42 +216,37 @@ int main(int argc, char* argv[])
config_parse(conf, &cbuf);
- if(!g_debugging)
+ if(!g_console)
{
- struct sockaddr_un sau;
+ struct sockaddr_any sany;
/* Create the thread buffers */
threads = (httpauth_thread_t*)malloc(sizeof(httpauth_thread_t) * g_maxthreads);
if(!threads)
errx(1, "out of memory");
+ /* Get the socket type */
+ if(sock_any_pton(g_socket, &sany, DEFAULT_PORT) == -1)
+ errx(1, "invalid socket name or ip: %s", g_socket);
+
/* Create the socket */
- sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ sock = socket(SANY_TYPE(sany), SOCK_STREAM, 0);
if(sock < 0)
err(1, "couldn't open socket");
-
/* Unlink the socket file if it exists */
/* TODO: Is this safe? */
unlink(g_socket);
- /* Setup the socket */
- strncmp(sau.sun_path, g_socket, sizeof(sau.sun_path));
- sau.sun_path[sizeof(sau.sun_path) - 1] = 0;
-
- /* Bind to the socket */
- if(bind(sock, (struct sockaddr*)&sau,
- sizeof(sau) - (sizeof(sau.sun_path) - strlen(sau.sun_path))))
- err(1, "couldn't bind to socket: %s", sau.sun_path);
-
+ if(bind(sock, SANY_ADDR(sany), SANY_LEN(sany)) != 0)
+ err(1, "couldn't bind to socket: %s", g_socket);
/* Let 5 connections queue up */
if(listen(sock, 5) != 0)
err(1, "couldn't listen on socket");
- }
-
- /* TODO: Enable signal processing here */
+ ha_messagex(LOG_DEBUG, "created socket: %s", g_socket);
+ }
/* Initialize all the handlers */
for(h = g_handlers; h; h = h->next)
@@ -253,8 +260,9 @@ int main(int argc, char* argv[])
/* This is for debugging the internal processes */
- if(g_debugging)
+ if(g_console)
{
+ ha_messagex(LOG_DEBUG, "processing from console");
r = httpauth_processor(0, 1);
goto finally;
}
@@ -263,27 +271,41 @@ int main(int argc, char* argv[])
/* This is the daemon section of the code */
else
{
- /* TODO: Daemonize here, and disconnect from terminal */
+ if(daemonize)
+ {
+ /* Fork a daemon nicely here */
+ if(daemon(0, 0) == -1)
+ {
+ ha_message(LOG_ERR, "couldn't run httpauth as daemon");
+ exit(1);
+ }
+
+ g_daemonized = 1;
+ }
+
+ /* Handle some signals */
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+/* signal(SIGINT, on_quit);
+ signal(SIGTERM, on_quit); */
/* Open the system log */
openlog("httpauthd", 0, LOG_AUTHPRIV);
- g_daemonized = 1;
+ ha_messagex(LOG_DEBUG, "accepting connections");
/* Now loop and accept the connections */
while(!g_quit)
{
int fd;
- /* TODO: A nice way to break out of the loop here */
-
fd = accept(sock, 0, 0);
if(fd == -1)
{
switch(errno)
{
- case EAGAIN:
case EINTR:
+ case EAGAIN:
break;
case ECONNABORTED:
@@ -300,13 +322,15 @@ int main(int argc, char* argv[])
continue;
}
+ ha_messagex(LOG_INFO, "accepted connection: %d", fd);
+
/* Look for thread and also clean up others */
for(i = 0; i < g_maxthreads; i++)
{
/* Clean up quit threads */
if(threads[i].tid != 0)
{
- if(threads[i].fd == 0)
+ if(threads[i].fd == -1)
{
pthread_join(threads[i].tid, NULL);
threads[i].tid = 0;
@@ -327,12 +351,14 @@ int main(int argc, char* argv[])
break;
}
- fd = 0;
+ ha_messagex(LOG_DEBUG, "created thread for connection: %d", fd);
+ fd = -1;
+ break;
}
}
/* Check to make sure we have a thread */
- if(fd != 0)
+ if(fd != -1)
{
ha_messagex(LOG_ERR, "too many connections open (max %d)", g_maxthreads);
httpauth_respond(fd, HA_SERVER_ERROR, 0, "too many connections");
@@ -340,7 +366,15 @@ int main(int argc, char* argv[])
}
}
- /* TODO: Quit all threads here */
+ ha_messagex(LOG_INFO, "waiting for threads to quit");
+
+ /* Quit all threads here */
+ for(i = 0; i < g_maxthreads; i++)
+ {
+ /* Clean up quit threads */
+ if(threads[i].tid != 0)
+ pthread_join(threads[i].tid, NULL);
+ }
r = 0;
}
@@ -371,28 +405,129 @@ finally:
return r == -1 ? 1 : 0;
}
-int usage()
+static void on_quit(int signal)
{
- fprintf(stderr, "usage: httpauthd [-dX] [-f conffile]\n");
+ g_quit = 1;
+}
+
+static int usage()
+{
+ fprintf(stderr, "usage: httpauthd [-d level] [-X] [-f conffile]\n");
return 2;
}
-void* httpauth_thread(void* arg)
+static void* httpauth_thread(void* arg)
+{
+ httpauth_thread_t* thread = (httpauth_thread_t*)arg;
+ int r;
+
+ ASSERT(thread);
+ ASSERT(thread->fd != -1);
+
+ /* call the processor */
+ r = httpauth_processor(thread->fd, thread->fd);
+
+ /* mark this as done */
+ thread->fd = -1;
+
+ return (void*)r;
+}
+
+/* -----------------------------------------------------------------------
+ * Logging
+ */
+
+void log_request(ha_request_t* req, ha_buffer_t* buf, int fd)
+{
+ const httpauth_command_t* cmd;
+ const char* t;
+ const char* t2;
+ int i;
+
+ if(g_debuglevel < LOG_DEBUG)
+ return;
+
+ if(req->type == REQTYPE_IGNORE || req->type == -1)
+ return;
+
+ ha_bufcpy(buf, "");
+
+ for(i = 0; i < HA_MAX_ARGS; i++)
+ {
+ if(req->args[i])
+ {
+ ha_bufjoin(buf);
+ ha_bufmcat(buf, ha_buflen(buf) > 0 ? ", " : "", req->args[i], NULL);
+ }
+ }
+
+ t = ha_bufdata(buf);
+
+ t2 = NULL;
+
+ /* Figure out which command it is */
+ for(cmd = kCommands; cmd->name; cmd++)
+ {
+ if(cmd->code == req->type)
+ {
+ t2 = cmd->name;
+ break;
+ }
+ }
+
+ ASSERT(t2);
+
+ ha_messagex(LOG_DEBUG, "received request (from %d): "
+ "[ type: %s / args: %s ]", fd, t2, t);
+
+ for(i = 0; i < HA_MAX_HEADERS; i++)
+ {
+ if(req->headers[i].name)
+ {
+ ASSERT(req->headers[i].data);
+ ha_messagex(LOG_DEBUG, "received header (from %d): [ %s: %s ]", fd,
+ req->headers[i].name, req->headers[i].data);
+ }
+ }
+}
+
+void log_response(ha_response_t* resp, int fd)
{
- int fd, r;
+ int i;
+
+ if(g_debuglevel < LOG_DEBUG)
+ return;
+
+ ha_messagex(LOG_DEBUG, "sending response (to %d): "
+ "[ code: 200 / ccode: %d / detail: %s ]", fd,
+ resp->code, resp->detail ? resp->detail : "");
+
+ for(i = 0; i < HA_MAX_HEADERS; i++)
+ {
+ if(resp->headers[i].name)
+ {
+ ASSERT(resp->headers[i].data);
+ ha_messagex(LOG_DEBUG, "sending header (to %d): [ %s: %s ]", fd,
+ resp->headers[i].name, resp->headers[i].data);
+ }
+ }
+}
- ASSERT(arg);
+void log_respcode(int code, const char* msg, int fd)
+{
+ if(g_debuglevel < LOG_DEBUG)
+ return;
- fd = (int)arg;
- return (void*)httpauth_processor(fd, fd);
+ ha_messagex(LOG_DEBUG, "sending response (to %d): "
+ "[ code: %d / detail: %s ]", fd, code, msg ? msg : "");
}
/* -----------------------------------------------------------------------
* Command Parsing and Handling
*/
-int httpauth_read(int ifd, ha_request_t* req,
- ha_buffer_t* buf)
+static int httpauth_read(int ifd, ha_request_t* req,
+ ha_buffer_t* buf)
{
const httpauth_command_t* cmd;
char* t;
@@ -513,7 +648,7 @@ int httpauth_read(int ifd, ha_request_t* req,
}
else
{
- if(i < MAX_HEADERS)
+ if(i < HA_MAX_HEADERS)
{
t = ha_bufparseword(buf, ":");
@@ -550,7 +685,7 @@ int httpauth_read(int ifd, ha_request_t* req,
return more;
}
-int write_data(int ofd, const char* data)
+static int write_data(int ofd, const char* data)
{
int r;
@@ -566,7 +701,7 @@ int write_data(int ofd, const char* data)
else if(r == -1)
{
- if(errno == EAGAIN || errno == EINTR)
+ if(errno == EAGAIN)
continue;
/* The other end closed. no message */
@@ -580,7 +715,7 @@ int write_data(int ofd, const char* data)
return 0;
}
-int httpauth_respond(int ofd, int scode, int ccode, const char* msg)
+static int httpauth_respond(int ofd, int scode, int ccode, const char* msg)
{
char num[16];
@@ -626,12 +761,16 @@ int httpauth_respond(int ofd, int scode, int ccode, const char* msg)
if(msg && write_data(ofd, msg) < 0)
return HA_CRITERROR;
+ /* When the client code is 0, then caller should log */
+ if(ccode == 0)
+ log_respcode(scode, msg, ofd);
+
return write_data(ofd, "\n");
}
const char kHeaderDelimiter[] = ": ";
-int httpauth_write(int ofd, ha_response_t* resp)
+static int httpauth_write(int ofd, ha_response_t* resp)
{
int i;
int wrote = 0;
@@ -642,7 +781,7 @@ int httpauth_write(int ofd, ha_response_t* resp)
if(httpauth_respond(ofd, HA_SERVER_ACCEPT, resp->code, resp->detail) < 0)
return HA_CRITERROR;
- for(i = 0; i < MAX_HEADERS; i++)
+ for(i = 0; i < HA_MAX_HEADERS; i++)
{
if(resp->headers[i].name)
{
@@ -659,10 +798,12 @@ int httpauth_write(int ofd, ha_response_t* resp)
if(write_data(ofd, "\n") == -1)
return -1;
+ log_response(resp, ofd);
+
return 0;
}
-int httpauth_error(int ofd, int r)
+static int httpauth_error(int ofd, int r)
{
int scode = 0;
const char* msg = NULL;
@@ -691,7 +832,7 @@ int httpauth_error(int ofd, int r)
return httpauth_respond(ofd, scode, 0, msg);
}
-int httpauth_ready(int ofd, ha_buffer_t* buf)
+static int httpauth_ready(int ofd, ha_buffer_t* buf)
{
const char* t;
httpauth_loaded_t* h;
@@ -720,7 +861,7 @@ int httpauth_ready(int ofd, ha_buffer_t* buf)
}
}
-int httpauth_processor(int ifd, int ofd)
+static int httpauth_processor(int ifd, int ofd)
{
ha_buffer_t inb;
ha_buffer_t outb;
@@ -743,13 +884,16 @@ int httpauth_processor(int ifd, int ofd)
}
/* Now loop and handle the commands */
- while(result == -1)
+ while(result == -1 && !g_quit)
{
ha_bufreset(&outb);
ha_bufreset(&inb);
r = httpauth_read(ifd, &req, &inb);
+ if(g_quit)
+ continue;
+
if(ha_buferr(&inb))
r = HA_CRITERROR;
@@ -757,9 +901,11 @@ int httpauth_processor(int ifd, int ofd)
{
httpauth_error(ofd, r);
result = 1;
- continue;
+ break;
}
+ log_request(&req, &inb, ifd);
+
if(r == 0)
result = 0;
@@ -769,13 +915,19 @@ int httpauth_processor(int ifd, int ofd)
r = process_auth(&req, &resp, &outb);
+ if(g_quit)
+ continue;
+
if(ha_buferr(&outb))
r = HA_CRITERROR;
if(r < 0)
{
httpauth_error(ofd, r);
- result = 1;
+
+ if(r == HA_CRITERROR)
+ result = 1;
+
continue;
}
@@ -797,6 +949,8 @@ int httpauth_processor(int ifd, int ofd)
break;
default:
+ ha_messagex(LOG_WARNING, "received unknown command from client: %d", ifd);
+
if(httpauth_respond(ofd, HA_SERVER_BADREQ, 0, "Unknown command") == -1)
{
result = -1;
@@ -819,8 +973,8 @@ finally:
return result;
}
-int process_auth(ha_request_t* req, ha_response_t* resp,
- ha_buffer_t* outb)
+static int process_auth(ha_request_t* req, ha_response_t* resp,
+ ha_buffer_t* outb)
{
httpauth_loaded_t* h;
@@ -832,21 +986,21 @@ int process_auth(ha_request_t* req, ha_response_t* resp,
/* Check our connection argument */
if(!req->args[AUTH_ARG_CONN] || !(req->args[AUTH_ARG_CONN][0]))
{
- ha_messagex(LOG_ERR, "Missing connection ID in request");
+ ha_messagex(LOG_ERR, "missing connection ID in request");
return HA_BADREQ;
}
/* Check our uri argument */
if(!req->args[AUTH_ARG_URI] || !(req->args[AUTH_ARG_URI][0]))
{
- ha_messagex(LOG_ERR, "Missing URI in request");
+ ha_messagex(LOG_ERR, "missing URI in request");
return HA_BADREQ;
}
/* Check our connection arguments */
if(!req->args[AUTH_ARG_METHOD] || !(req->args[AUTH_ARG_METHOD][0]))
{
- ha_messagex(LOG_ERR, "Missing method in request");
+ ha_messagex(LOG_ERR, "missing method in request");
return HA_BADREQ;
}
@@ -856,13 +1010,16 @@ int process_auth(ha_request_t* req, ha_response_t* resp,
{
if(strcasecmp(h->ctx.name, req->args[0]) == 0)
{
+ ha_messagex(LOG_INFO, "processing request with method: %s (%s)",
+ h->ctx.name, h->ctx.handler->type);
+
/* Now let the handler handle it */
ASSERT(h->ctx.handler->f_process);
return (h->ctx.handler->f_process)(&(h->ctx), req, resp, outb);
}
}
- ha_messagex(LOG_ERR, "Unknown authentication type: %s", req->args[0]);
+ ha_messagex(LOG_ERR, "unknown authentication type: %s", req->args[0]);
return HA_BADREQ;
}
@@ -870,8 +1027,8 @@ int process_auth(ha_request_t* req, ha_response_t* resp,
* Configuration
*/
-ha_context_t* config_addhandler(ha_buffer_t* buf, const char* alias,
- ha_handler_t* handler, const ha_context_t* defaults)
+static ha_context_t* config_addhandler(ha_buffer_t* buf, const char* alias,
+ ha_handler_t* handler, const ha_options_t* defaults)
{
httpauth_loaded_t* loaded;
int len;
@@ -885,7 +1042,7 @@ ha_context_t* config_addhandler(ha_buffer_t* buf, const char* alias,
errx(1, "out of memory");
memset(loaded, 0, len);
- memcpy(&(loaded->ctx), defaults, sizeof(ha_context_t));
+ memcpy(&(loaded->ctx.opts), defaults, sizeof(ha_options_t));
if(handler->context_size)
{
@@ -917,7 +1074,7 @@ ha_context_t* config_addhandler(ha_buffer_t* buf, const char* alias,
for(;;)
{
if(strcasecmp(alias, l->ctx.name) == 0)
- errx(1, "duplicate handler section for '%s'", alias);
+ errx(1, "duplicate method section for '%s'", alias);
if(!(l->next))
break;
@@ -928,13 +1085,13 @@ ha_context_t* config_addhandler(ha_buffer_t* buf, const char* alias,
l->next = loaded;
}
+ ha_messagex(LOG_DEBUG, "configuration: method: %s (%s)", alias, handler->type);
return &(loaded->ctx);
}
-
-int config_parse(const char* file, ha_buffer_t* buf)
+static int config_parse(const char* file, ha_buffer_t* buf)
{
- ha_context_t defaults;
+ ha_options_t defaults;
ha_context_t* ctx = NULL;
int line = 0;
int fd;
@@ -957,6 +1114,8 @@ int config_parse(const char* file, ha_buffer_t* buf)
defaults.types = 0xFFFFFFFF; /* All types by default */
defaults.cache_timeout = DEFAULT_TIMEOUT; /* Timeout for cache */
defaults.cache_max = DEFAULT_CACHEMAX;
+ defaults.realm = "";
+
ha_bufreset(buf);
@@ -992,7 +1151,7 @@ int config_parse(const char* file, ha_buffer_t* buf)
name = ha_bufparseline(buf, 1);
if(!name || name[strlen(name) - 1] != ']')
- errx(1, "configuration section invalid (line %d)", line);
+ errx(1, "method section invalid (line %d)", line);
/* remove the bracket */
@@ -1011,7 +1170,7 @@ int config_parse(const char* file, ha_buffer_t* buf)
}
if(handler == NULL)
- errx(1, "unknown handler type '%s' (line %d)", name, line);
+ errx(1, "unknown method type '%s' (line %d)", name, line);
/* If we had a last handler then add it to handlers */
ctx = config_addhandler(buf, name, handler, &defaults);
@@ -1052,7 +1211,6 @@ int config_parse(const char* file, ha_buffer_t* buf)
exit(1);
recog = 1;
}
-
}
/* Otherwise we're in a handler */
@@ -1078,13 +1236,16 @@ int config_parse(const char* file, ha_buffer_t* buf)
/* Options that are legal in both global and internal sections */
if(!recog)
{
+ ha_options_t* opts = ctx ? &(ctx->opts) : &defaults;
+ ASSERT(opts);
+
if(strcmp(name, "cachetimeout") == 0)
{
int v;
if(ha_confint(name, value, 0, 86400, &v) < 0)
exit(1); /* Message already printed */
- (ctx ? ctx : &defaults)->cache_timeout = v;
+ opts->cache_timeout = v;
recog = 1;
}
@@ -1094,7 +1255,7 @@ int config_parse(const char* file, ha_buffer_t* buf)
if(ha_confint(name, value, 0, 0x7FFFFFFF, &v) < 0)
exit(1); /* Message already printed */
- (ctx ? ctx : &defaults)->cache_max = v;
+ opts->cache_max = v;
recog = 1;
}
@@ -1134,17 +1295,49 @@ int config_parse(const char* file, ha_buffer_t* buf)
if(types == 0)
errx(1, "no authentication types for '%s' (line %d)", name, line);
- (ctx ? ctx : &defaults)->types = types;
+ opts->types = types;
recog = 1;
}
+
+ else if(strcmp(name, "realm") == 0)
+ {
+ opts->realm = value;
+ recog = 1;
+ }
+
+ else if(strcmp(name, "digestignoreuri") == 0)
+ {
+ int v;
+ if(ha_confbool(name, value, &v) < 0)
+ exit(1); /* Message already printed */
+
+ opts->digest_ignoreuri = v;
+ recog = 1;
+ }
+
+ else if(strcmp(name, "digestdomains") == 0)
+ {
+ opts->digest_domains = value;
+ recog = 1;
+ }
+
+#ifdef _DEBUG
+ else if(strcmp(name, "digestdebugnonce") == 0)
+ {
+ opts->digest_debugnonce = value;
+ recog = 1;
+ }
+#endif
}
if(!recog)
errx(1, "unrecognized configuration setting '%s' (line %d)", name, line);
+ else
+ ha_messagex(LOG_DEBUG, "configuration: setting: [ %s: %s ]", name, value);
}
if(!g_handlers)
- ha_messagex(LOG_INFO, "no handlers found in configuration file");
+ ha_messagex(LOG_WARNING, "configuration: no methods found in configuration file");
return 0;
}
diff --git a/daemon/httpauthd.h b/daemon/httpauthd.h
index 95187be..cd1d39e 100644
--- a/daemon/httpauthd.h
+++ b/daemon/httpauthd.h
@@ -177,16 +177,29 @@ ha_handler_t;
-struct ha_options;
+typedef struct ha_options
+{
+ /* Basic options */
+ unsigned int types;
+ int cache_timeout;
+ int cache_max;
+
+ /* For basic and digest auth: */
+ const char* realm;
+
+ /* For digest auth: */
+ int digest_ignoreuri;
+ const char* digest_domains;
+ const char* digest_debugnonce;
+}
+ha_options_t;
/* Context passed to the handler functions below */
typedef struct ha_context
{
const char* name; /* A name assigned by the configuration file */
ha_handler_t* handler; /* The original handler structure */
- unsigned int types; /* The types of authentication allowed */
- int cache_timeout; /* Timeout for cached connections */
- int cache_max; /* Maximum amount of cached connections */
+ ha_options_t opts; /* The options */
void* data; /* Handler specific data */
}
ha_context_t;
@@ -202,7 +215,7 @@ ha_context_t;
* should be no need to change it unless we're
* adding or removing commands
*/
-#define MAX_ARGS 4
+#define HA_MAX_ARGS 4
/*
* The maximum number of pertinent headers to read
@@ -211,13 +224,13 @@ ha_context_t;
* of valid headers in httpauthd.c
*/
-#define MAX_HEADERS 2
+#define HA_MAX_HEADERS 2
/*
* The maximum number of handlers. If you add
* handlers make sure to update this number.
*/
-#define MAX_HANDLERS 4
+#define HA_MAX_HANDLERS 4
/* A single header in memory */
@@ -241,8 +254,8 @@ ha_header_t;
typedef struct ha_request
{
int type;
- const char* args[MAX_ARGS];
- ha_header_t headers[MAX_HEADERS];
+ const char* args[HA_MAX_ARGS];
+ ha_header_t headers[HA_MAX_HEADERS];
}
ha_request_t;
@@ -250,7 +263,7 @@ ha_request_t;
#define HA_SERVER_READY 100
#define HA_SERVER_ACCEPT 200
#define HA_SERVER_DECLINE 401
-#define HA_SERVER_BADREQ 402
+#define HA_SERVER_BADREQ 400
#define HA_SERVER_BUSY 500
/* A response to the client */
@@ -258,7 +271,7 @@ typedef struct ha_response
{
int code;
const char* detail;
- ha_header_t headers[MAX_HEADERS];
+ ha_header_t headers[HA_MAX_HEADERS];
}
ha_response_t;
diff --git a/daemon/ldap.c b/daemon/ldap.c
index 9a79d43..a5abd97 100644
--- a/daemon/ldap.c
+++ b/daemon/ldap.c
@@ -69,24 +69,17 @@ typedef struct ldap_context
int port; /* Port to connect to LDAP server on */
int scope; /* Scope for filter */
- const char* realm; /* The realm to use in authentication */
- const char* domains; /* Domains for which digest auth is valid */
-
int dobind; /* Bind to do simple authentication */
- int cache_max; /* Maximum number of connections at once */
- int cache_timeout;
int ldap_max; /* Number of open connections allowed */
int ldap_timeout; /* Maximum amount of time to dedicate to an ldap query */
+ ha_options_t* opts; /* Options from httpauthd.c */
+
/* Context ----------------------------------------------------------- */
hash_t* cache; /* Some cached records or basic */
LDAP** pool; /* Pool of available connections */
int pool_mark; /* Amount of connections allocated */
-
-#ifdef _DEBUG
- const char* debug_nonce;
-#endif
}
ldap_context_t;
@@ -104,19 +97,12 @@ static const ldap_context_t ldap_defaults =
NULL, /* dnmap */
389, /* port */
LDAP_SCOPE_SUBTREE, /* scope */
- "", /* realm */
- NULL, /* domains */
1, /* dobind */
- 1000, /* cache_max */
- 30, /* cache_timeout */
10, /* ldap_max */
30, /* ldap_timeout */
NULL, /* cache */
NULL, /* pool */
0 /* pool_mark */
-#ifdef _DEBUG
- , NULL /* debug_nonce */
-#endif
};
@@ -134,17 +120,17 @@ static int report_ldap(const char* msg, int code)
{
ASSERT(code != LDAP_SUCCESS);
- if(!msg)
- msg = "ldap error";
-
- ha_messagex(LOG_ERR, "%s: %s", msg, ldap_err2string(code));
-
switch(code)
{
case LDAP_NO_MEMORY:
+ ha_messagex(LOG_CRIT, "out of memory");
return HA_CRITERROR;
default:
+ if(!msg)
+ msg = "error";
+
+ ha_messagex(LOG_ERR, "ldap: %s: %s", msg, ldap_err2string(code));
return HA_FAILED;
};
}
@@ -155,7 +141,7 @@ static digest_record_t* get_cached_digest(ldap_context_t* ctx, unsigned char* no
ASSERT(ctx && nonce);
- if(ctx->cache_max == 0)
+ if(ctx->opts->cache_max == 0)
return NULL;
ha_lock(NULL);
@@ -193,7 +179,7 @@ static int save_cached_digest(ldap_context_t* ctx, digest_record_t* rec)
ASSERT(ctx && rec);
- if(ctx->cache_max == 0)
+ if(ctx->opts->cache_max == 0)
{
free_hash_object(NULL, rec);
return HA_FALSE;
@@ -201,7 +187,7 @@ static int save_cached_digest(ldap_context_t* ctx, digest_record_t* rec)
ha_lock(NULL);
- while(hash_count(ctx->cache) >= ctx->cache_max)
+ while(hash_count(ctx->cache) >= ctx->opts->cache_max)
hash_bump(ctx->cache);
r = hash_set(ctx->cache, rec->nonce, rec);
@@ -223,12 +209,12 @@ static int add_cached_basic(ldap_context_t* ctx, unsigned char* key)
ASSERT(ctx && key);
- if(ctx->cache_max == 0)
+ if(ctx->opts->cache_max == 0)
return HA_FALSE;
ha_lock(NULL);
- while(hash_count(ctx->cache) >= ctx->cache_max)
+ while(hash_count(ctx->cache) >= ctx->opts->cache_max)
hash_bump(ctx->cache);
r = hash_set(ctx->cache, key, BASIC_ESTABLISHED);
@@ -279,7 +265,7 @@ static const char* substitute_params(ldap_context_t* ctx, ha_buffer_t* buf,
case 'r':
ha_bufjoin(buf);
- ha_bufcpy(buf, ctx->realm);
+ ha_bufcpy(buf, ctx->opts->realm);
t++;
break;
@@ -388,6 +374,7 @@ static int parse_ldap_ha1(ha_buffer_t* buf, struct berval* bv, unsigned char* ha
/* Raw binary */
if(bv->bv_len == MD5_LEN)
{
+ ha_messagex(LOG_DEBUG, "ldap: found ha1 in raw binary format");
memcpy(ha1, bv->bv_val, MD5_LEN);
return HA_OK;
}
@@ -400,6 +387,7 @@ static int parse_ldap_ha1(ha_buffer_t* buf, struct berval* bv, unsigned char* ha
if(d && len == MD5_LEN)
{
+ ha_messagex(LOG_DEBUG, "ldap: found ha1 in hex encoded format");
memcpy(ha1, d, MD5_LEN);
return HA_OK;
}
@@ -413,6 +401,7 @@ static int parse_ldap_ha1(ha_buffer_t* buf, struct berval* bv, unsigned char* ha
if(d && len == MD5_LEN)
{
+ ha_messagex(LOG_DEBUG, "ldap: found ha1 in b64 encoded format");
memcpy(ha1, ha_bufdata(buf), MD5_LEN);
return HA_OK;
}
@@ -483,6 +472,7 @@ static int validate_ldap_password(ldap_context_t* ctx, LDAP* ld, LDAPMessage* en
if(strcmp(pw, p) == 0)
{
+ ha_messagex(LOG_DEBUG, "ldap: successful validate against password");
res = HA_OK;
break;
}
@@ -492,7 +482,7 @@ static int validate_ldap_password(ldap_context_t* ctx, LDAP* ld, LDAPMessage* en
}
if(res == HA_FALSE && unknown)
- ha_messagex(LOG_ERR, "LDAP does not contain any compatible passwords for user: %s", user);
+ ha_messagex(LOG_ERR, "ldap: server does not contain any compatible passwords for user: %s", user);
return res;
}
@@ -516,7 +506,7 @@ static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry,
if(ha1s)
{
- digest_makeha1(key, user, ctx->realm, clearpw);
+ digest_makeha1(key, user, ctx->opts->realm, clearpw);
for(b = ha1s ; *b; b++)
{
@@ -530,7 +520,7 @@ static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry,
if(r == HA_FALSE)
{
if(first)
- ha_messagex(LOG_ERR, "LDAP contains invalid HA1 digest hash for user: %s", user);
+ ha_messagex(LOG_ERR, "ldap: server contains invalid HA1 digest hash for user: %s", user);
first = 0;
continue;
@@ -538,6 +528,7 @@ static int validate_ldap_ha1(ldap_context_t* ctx, LDAP* ld, LDAPMessage* entry,
if(memcmp(key, k, MD5_LEN) == 0)
{
+ ha_messagex(LOG_DEBUG, "ldap: successful validate against ha1");
res = HA_OK;
break;
}
@@ -561,6 +552,7 @@ static LDAP* get_ldap_connection(ldap_context_t* ctx)
/* An open connection in the pool */
if(ctx->pool[i])
{
+ ha_messagex(LOG_DEBUG, "ldap: using cached connection");
ld = ctx->pool[i];
ctx->pool[i] = NULL;
return ld;
@@ -569,14 +561,14 @@ static LDAP* get_ldap_connection(ldap_context_t* ctx)
if(ctx->pool_mark >= ctx->ldap_max)
{
- ha_messagex(LOG_ERR, "too many open connections to LDAP");
+ ha_messagex(LOG_ERR, "ldap: too many open connections");
return NULL;
}
ld = ldap_init(ctx->servers, ctx->port);
if(!ld)
{
- ha_message(LOG_ERR, "couldn't initialize ldap connection");
+ ha_message(LOG_ERR, "ldap: couldn't initialize connection");
return NULL;
}
@@ -586,13 +578,14 @@ static LDAP* get_ldap_connection(ldap_context_t* ctx)
ctx->password ? ctx->password : "");
if(r != LDAP_SUCCESS)
{
- report_ldap("couldn't bind to LDAP server", r);
+ report_ldap("ldap: couldn't bind to LDAP server", r);
ldap_unbind_s(ld);
return NULL;
}
}
ctx->pool_mark++;
+ ha_messagex(LOG_DEBUG, "ldap: opened new connection (total %d)", ctx->pool_mark);
return ld;
}
@@ -621,6 +614,7 @@ static void save_ldap_connection(ldap_context_t* ctx, LDAP* ld)
/* An open connection in the pool */
if(!ctx->pool[i])
{
+ ha_messagex(LOG_DEBUG, "ldap: caching connection for later use");
ctx->pool[i] = ld;
ld = NULL;
break;
@@ -634,6 +628,7 @@ static void save_ldap_connection(ldap_context_t* ctx, LDAP* ld)
{
ldap_unbind_s(ld);
ctx->pool_mark--;
+ ha_messagex(LOG_DEBUG, "ldap: discarding connection (total %d)", ctx->pool_mark);
}
}
@@ -644,6 +639,8 @@ static int retrieve_user_entry(ldap_context_t* ctx, ha_buffer_t* buf, LDAP* ld,
struct timeval tv;
const char* filter;
const char* attrs[3];
+ const char* base;
+ int scope;
int r;
ASSERT(ctx && buf && ld && user && dn && entry && result);
@@ -667,15 +664,19 @@ static int retrieve_user_entry(ldap_context_t* ctx, ha_buffer_t* buf, LDAP* ld,
tv.tv_sec = ctx->ldap_timeout;
tv.tv_usec = 0;
- r = ldap_search_st(ld, *dn ? *dn : ctx->base,
- *dn ? LDAP_SCOPE_BASE : ctx->scope,
- filter, (char**)attrs, 0, &tv, result);
+ base = *dn ? *dn : ctx->base;
+ scope = *dn ? LDAP_SCOPE_BASE : ctx->scope;
+
+ ha_messagex(LOG_DEBUG, "ldap: performing search: [ base: %s / scope: %d / filter: %s ]",
+ base, scope, filter);
+
+ r = ldap_search_st(ld, base, scope, filter, (char**)attrs, 0, &tv, result);
if(r != LDAP_SUCCESS)
{
if(r == LDAP_NO_SUCH_OBJECT)
{
- ha_messagex(LOG_WARNING, "user not found in LDAP: %s", user);
+ ha_messagex(LOG_WARNING, "ldap: user not found in LDAP: %s", user);
return HA_FALSE;
}
@@ -689,15 +690,18 @@ static int retrieve_user_entry(ldap_context_t* ctx, ha_buffer_t* buf, LDAP* ld,
case 1:
*entry = ldap_first_entry(ld, *result);
if(!(*dn))
+ {
*dn = ldap_get_dn(ld, *entry);
+ ha_messagex(LOG_DEBUG, "ldap: found entry for user: %s", *dn);
+ }
return HA_OK;
case 0:
- ha_messagex(LOG_WARNING, "user not found in LDAP: %s", user);
+ ha_messagex(LOG_WARNING, "ldap: user not found in LDAP: %s", user);
break;
default:
- ha_messagex(LOG_WARNING, "more than one user found for filter: %s", filter);
+ ha_messagex(LOG_WARNING, "ldap: more than one user found for filter: %s", filter);
break;
};
@@ -739,6 +743,8 @@ static int complete_digest_ha1(ldap_context_t* ctx, digest_record_t* rec,
ret = HA_CRITERROR;
goto finally;
}
+
+ ha_messagex(LOG_INFO, "ldap: mapped %s to %s", user, dn);
}
/* Okay now we contact the LDAP server. */
@@ -761,10 +767,11 @@ static int complete_digest_ha1(ldap_context_t* ctx, digest_record_t* rec,
if(ret != HA_OK)
{
if(ret == HA_FALSE)
- ha_messagex(LOG_ERR, "LDAP contains invalid HA1 digest hash for user: %s", user);
+ ha_messagex(LOG_ERR, "ldap: server contains invalid HA1 for user: %s", user);
}
}
+ ha_messagex(LOG_DEBUG, "ldap: using HA1 from ldap");
ldap_value_free_len(ha1s);
goto finally;
}
@@ -779,17 +786,20 @@ static int complete_digest_ha1(ldap_context_t* ctx, digest_record_t* rec,
if(t)
{
- digest_makeha1(rec->ha1, user, ctx->realm, t);
+ digest_makeha1(rec->ha1, user, ctx->opts->realm, t);
ret = HA_OK;
}
ldap_value_free(pws);
if(ret == HA_OK)
+ {
+ ha_messagex(LOG_DEBUG, "ldap: using clear password from ldap");
goto finally;
+ }
}
- ha_messagex(LOG_ERR, "LDAP contains no cleartext password for user: %s", user);
+ ha_messagex(LOG_ERR, "ldap: server contains no clear password or HA1 for user: %s", user);
finally:
@@ -824,6 +834,8 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
/* Check and see if this connection is in the cache */
if(have_cached_basic(ctx, basic.key))
{
+ ha_messagex(LOG_NOTICE, "ldap: validated basic user against cache: %s",
+ basic.user);
found = 1;
ret = HA_OK;
goto finally;
@@ -832,7 +844,10 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
/* If we have a user name and password */
if(!basic.user || !basic.user[0] ||
!basic.password || !basic.password[0])
+ {
+ ha_messagex(LOG_NOTICE, "ldap: no valid basic auth info");
goto finally;
+ }
ld = get_ldap_connection(ctx);
@@ -857,6 +872,8 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
ret = HA_CRITERROR;
goto finally;
}
+
+ ha_messagex(LOG_INFO, "ldap: mapped %s to %s", basic.user, dn);
}
@@ -895,7 +912,9 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
if(r != LDAP_SUCCESS)
{
if(r == LDAP_INVALID_CREDENTIALS)
- ha_messagex(LOG_WARNING, "invalid login for: %s", basic.user);
+ ha_messagex(LOG_WARNING, "ldap: basic authentication (via bind) failed for user: %s",
+ basic.user);
+
else
report_ldap("couldn't bind to LDAP server", r);
@@ -903,6 +922,7 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
}
/* It worked! */
+ ha_messagex(LOG_NOTICE, "ldap: validated basic user using bind: %s", basic.user);
found = 1;
}
@@ -915,10 +935,15 @@ static int basic_ldap_response(ldap_context_t* ctx, const char* header,
ret = validate_ldap_ha1(ctx, ld, entry, buf, basic.user, basic.password);
if(ret == HA_OK)
+ {
+ ha_messagex(LOG_NOTICE, "ldap: validated basic user password/ha1: %s", basic.user);
found = 1;
+ }
else
- ha_messagex(LOG_WARNING, "invalid, unreadable or unrecognized password for user: %s", basic.user);
+ {
+ ha_messagex(LOG_WARNING, "ldap: invalid, unreadable or unrecognized password for user: %s", basic.user);
+ }
}
@@ -952,10 +977,10 @@ static int digest_ldap_challenge(ldap_context_t* ctx, ha_response_t* resp,
ASSERT(ctx && resp && buf);
#ifdef _DEBUG
- if(ctx->debug_nonce)
+ if(ctx->opts->digest_debugnonce)
{
- nonce_str = ctx->debug_nonce;
- ha_messagex(LOG_WARNING, "using debug nonce. security non-existant.");
+ nonce_str = ctx->opts->digest_debugnonce;
+ ha_messagex(LOG_WARNING, "simple: using debug nonce. security non-existant.");
}
else
#endif
@@ -969,7 +994,8 @@ static int digest_ldap_challenge(ldap_context_t* ctx, ha_response_t* resp,
}
/* Now generate a message to send */
- header = digest_challenge(buf, nonce_str, ctx->realm, ctx->domains, stale);
+ header = digest_challenge(buf, nonce_str, ctx->opts->realm,
+ ctx->opts->digest_domains, stale);
if(!header)
return HA_CRITERROR;
@@ -978,6 +1004,7 @@ static int digest_ldap_challenge(ldap_context_t* ctx, ha_response_t* resp,
resp->code = HA_SERVER_DECLINE;
ha_addheader(resp, "WWW-Authenticate", header);
+ ha_messagex(LOG_DEBUG, "ldap: created digest challenge with nonce: %s", nonce_str);
return HA_OK;
}
@@ -1003,18 +1030,18 @@ static int digest_ldap_response(ldap_context_t* ctx, const char* header,
return r;
#ifdef _DEBUG
- if(ctx->debug_nonce)
+ if(ctx->opts->digest_debugnonce)
{
- if(dg.nonce && strcmp(dg.nonce, ctx->debug_nonce) != 0)
+ if(dg.nonce && strcmp(dg.nonce, ctx->opts->digest_debugnonce) != 0)
{
ret = HA_FALSE;
resp->code = HA_SERVER_BADREQ;
- ha_messagex(LOG_WARNING, "digest response contains invalid nonce");
+ ha_messagex(LOG_WARNING, "ldap: digest response contains invalid nonce");
goto finally;
}
/* Do a rough hash into the real nonce, for use as a key */
- md5_string(nonce, ctx->debug_nonce);
+ md5_string(nonce, ctx->opts->digest_debugnonce);
/* Debug nonce's never expire */
expiry = time(NULL);
@@ -1028,25 +1055,29 @@ static int digest_ldap_response(ldap_context_t* ctx, const char* header,
if(r == HA_FALSE)
{
resp->code = HA_SERVER_BADREQ;
- ha_messagex(LOG_WARNING, "digest response contains invalid nonce");
+ ha_messagex(LOG_WARNING, "ldap: digest response contains invalid nonce");
}
goto finally;
}
}
-
rec = get_cached_digest(ctx, nonce);
/* Check to see if we're stale */
- if((expiry + ctx->cache_timeout) <= time(NULL))
+ if((expiry + ctx->opts->cache_timeout) <= time(NULL))
{
+ ha_messagex(LOG_INFO, "ldap: nonce expired, sending stale challenge: %s",
+ dg.username);
+
stale = 1;
goto finally;
}
if(!rec)
{
+ ha_messagex(LOG_INFO, "ldap: no record in cache, creating one: %s", dg.username);
+
/*
* If we're valid but don't have a record in the
* cache then complete the record properly.
@@ -1070,7 +1101,8 @@ static int digest_ldap_response(ldap_context_t* ctx, const char* header,
/* Increment our nonce count */
rec->nc++;
- ret = digest_check(ctx->realm, method, uri, buf, &dg, rec);
+ ret = digest_check(ctx->opts->realm, method,
+ ctx->opts->digest_ignoreuri ? NULL : uri, buf, &dg, rec);
if(ret == HA_BADREQ)
{
@@ -1084,8 +1116,12 @@ static int digest_ldap_response(ldap_context_t* ctx, const char* header,
resp->detail = dg.username;
/* Figure out if we need a new nonce */
- if((expiry + (ctx->cache_timeout - (ctx->cache_timeout / 8))) < time(NULL))
+ if((expiry + (ctx->opts->cache_timeout -
+ (ctx->opts->cache_timeout / 8))) < time(NULL))
{
+ ha_messagex(LOG_INFO, "ldap: nonce almost expired, creating new one: %s",
+ dg.username);
+
digest_makenonce(nonce, g_ldap_secret, NULL);
stale = 1;
}
@@ -1100,6 +1136,8 @@ static int digest_ldap_response(ldap_context_t* ctx, const char* header,
if(t[0])
ha_addheader(resp, "Authentication-Info", t);
+ ha_messagex(LOG_NOTICE, "ldap: validated digest user: %s", dg.username);
+
/* Put the connection into the cache */
if((r = save_cached_digest(ctx, rec)) < 0)
ret = r;
@@ -1179,18 +1217,6 @@ int ldap_config(ha_context_t* context, const char* name, const char* value)
return HA_OK;
}
- else if(strcmp(name, "realm") == 0)
- {
- ctx->realm = value;
- return HA_OK;
- }
-
- else if(strcmp(name, "digestdomains") == 0)
- {
- ctx->domains = value;
- return HA_OK;
- }
-
else if(strcmp(name, "ldapscope") == 0)
{
if(strcmp(value, "sub") == 0 || strcmp(value, "subtree") == 0)
@@ -1224,14 +1250,6 @@ int ldap_config(ha_context_t* context, const char* name, const char* value)
return ha_confint(name, value, 0, 86400, &(ctx->ldap_timeout));
}
-#ifdef _DEBUG
- else if(strcmp(name, "digestdebugnonce") == 0)
- {
- ctx->debug_nonce = value;
- return HA_OK;
- }
-#endif
-
return HA_FALSE;
}
@@ -1240,6 +1258,7 @@ int ldap_inithand(ha_context_t* context)
/* Global initialization */
if(!context)
{
+ ha_messagex(LOG_DEBUG, "ldap: generating secret");
return ha_genrandom(g_ldap_secret, DIGEST_SECRET_LEN);
}
@@ -1252,9 +1271,9 @@ int ldap_inithand(ha_context_t* context)
ASSERT(ctx);
/* Make sure there are some types of authentication we can do */
- if(!(context->types & (HA_TYPE_BASIC | HA_TYPE_DIGEST)))
+ if(!(context->opts.types & (HA_TYPE_BASIC | HA_TYPE_DIGEST)))
{
- ha_messagex(LOG_ERR, "LDAP module configured, but does not implement any "
+ ha_messagex(LOG_ERR, "ldap: module configured, but does not implement any "
"configured authentication type.");
return HA_FAILED;
}
@@ -1262,14 +1281,14 @@ int ldap_inithand(ha_context_t* context)
/* Check for mandatory configuration */
if(!ctx->servers)
{
- ha_messagex(LOG_ERR, "LDAP configuration incomplete. "
+ ha_messagex(LOG_ERR, "ldap: configuration incomplete. "
"Must have LDAPServers.");
return HA_FAILED;
}
if(!ctx->dnmap && (!ctx->filter || !ctx->base))
{
- ha_messagex(LOG_ERR, "LDAP configuration incomplete. "
+ ha_messagex(LOG_ERR, "ldap: configuration incomplete. "
"When not using LDAPDNMap must specify LDAPBase and LDAPFilter.");
return HA_FAILED;
}
@@ -1299,8 +1318,9 @@ int ldap_inithand(ha_context_t* context)
memset(ctx->pool, 0, sizeof(LDAP*) * ctx->ldap_max);
/* Copy some settings over for easy access */
- ctx->cache_max = context->cache_max;
- ctx->cache_timeout = context->cache_timeout;
+ ctx->opts = &(context->opts);
+
+ ha_messagex(LOG_INFO, "ldap: initialized handler");
}
return HA_OK;
@@ -1333,6 +1353,8 @@ void ldap_destroy(ha_context_t* context)
/* And free the connection pool */
free(ctx->pool);
}
+
+ ha_messagex(LOG_INFO, "ldap: uninitialized handler");
}
int ldap_process(ha_context_t* context, ha_request_t* req,
@@ -1341,7 +1363,7 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
ldap_context_t* ctx = (ldap_context_t*)context->data;
time_t t = time(NULL);
const char* header = NULL;
- int ret;
+ int ret, r;
ASSERT(req && resp && buf);
ASSERT(req->args[AUTH_ARG_METHOD]);
@@ -1350,21 +1372,24 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
ha_lock(NULL);
/* Purge out stale connection stuff. */
- hash_purge(ctx->cache, t - ctx->cache_timeout);
+ r = hash_purge(ctx->cache, t - ctx->opts->cache_timeout);
ha_unlock(NULL);
+ if(r > 0)
+ ha_messagex(LOG_DEBUG, "ldap: purged cache records: %d", r);
/* We use this below to detect whether to send a default response */
resp->code = -1;
/* Check the headers and see if we got a response thingy */
- if(context->types & HA_TYPE_DIGEST)
+ if(context->opts.types & HA_TYPE_DIGEST)
{
header = ha_getheader(req, "Authorization", HA_PREFIX_DIGEST);
if(header)
{
+ ha_messagex(LOG_DEBUG, "ldap: processing basic auth header");
ret = digest_ldap_response(ctx, header, req->args[AUTH_ARG_METHOD],
req->args[AUTH_ARG_URI], resp, buf);
if(ret < 0)
@@ -1373,11 +1398,12 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
}
/* Or a basic authentication */
- if(!header && context->types & HA_TYPE_BASIC)
+ if(!header && context->opts.types & HA_TYPE_BASIC)
{
header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC);
if(header)
{
+ ha_messagex(LOG_DEBUG, "ldap: processing digest auth header");
ret = basic_ldap_response(ctx, header, resp, buf);
if(ret < 0)
return ret;
@@ -1390,21 +1416,22 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
{
resp->code = HA_SERVER_DECLINE;
- if(context->types & HA_TYPE_DIGEST)
- {
- ret = digest_ldap_challenge(ctx, resp, buf, 0);
- if(ret < 0)
- return ret;
- }
-
- if(context->types & HA_TYPE_BASIC)
+ if(context->opts.types & HA_TYPE_BASIC)
{
- ha_bufmcat(buf, "BASIC realm=\"", ctx->realm , "\"", NULL);
+ ha_bufmcat(buf, "BASIC realm=\"", ctx->opts->realm , "\"", NULL);
if(ha_buferr(buf))
return HA_CRITERROR;
ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf));
+ ha_messagex(LOG_DEBUG, "ldap: sent basic auth request");
+ }
+
+ if(context->opts.types & HA_TYPE_DIGEST)
+ {
+ ret = digest_ldap_challenge(ctx, resp, buf, 0);
+ if(ret < 0)
+ return ret;
}
}
diff --git a/daemon/misc.c b/daemon/misc.c
index cda6dc2..a38eda9 100644
--- a/daemon/misc.c
+++ b/daemon/misc.c
@@ -8,7 +8,7 @@
#include <stdio.h>
#include <fcntl.h>
-extern int g_debugging;
+extern int g_debuglevel;
extern int g_daemonized;
extern pthread_mutex_t g_mutex;
@@ -29,11 +29,14 @@ void ha_messagex(int level, const char* msg, ...)
/* Either to syslog or stderr */
if(g_daemonized)
- vsyslog(level, msg, ap);
+ {
+ if(level < LOG_DEBUG)
+ vsyslog(level, msg, ap);
+ }
else
{
- if(g_debugging || level > LOG_INFO)
+ if(g_debuglevel >= level)
vwarnx(msg, ap);
}
@@ -50,21 +53,24 @@ void ha_message(int level, const char* msg, ...)
/* Either to syslog or stderr */
if(g_daemonized)
{
- char* m = (char*)alloca(strlen(msg) + MAX_MSGLEN + sizeof(kMsgDelimiter));
-
- if(m)
+ if(level < LOG_DEBUG)
{
- strcpy(m, msg);
- strcat(m, kMsgDelimiter);
- strerror_r(errno, m + strlen(m), MAX_MSGLEN);
- }
+ char* m = (char*)alloca(strlen(msg) + MAX_MSGLEN + sizeof(kMsgDelimiter));
- vsyslog(LOG_ERR, m ? m : msg, ap);
+ if(m)
+ {
+ strcpy(m, msg);
+ strcat(m, kMsgDelimiter);
+ strerror_r(errno, m + strlen(m), MAX_MSGLEN);
+ }
+
+ vsyslog(LOG_ERR, m ? m : msg, ap);
+ }
}
else
{
- if(g_debugging || level > LOG_INFO)
- vwarnx(msg, ap);
+ if(g_debuglevel >= level)
+ vwarn(msg, ap);
}
va_end(ap);
@@ -81,7 +87,7 @@ ha_header_t* ha_findheader(ha_request_t* req, const char* name)
ASSERT(req && name);
- for(i = 0; i < MAX_HEADERS; i++)
+ for(i = 0; i < HA_MAX_HEADERS; i++)
{
if(req->headers[i].name)
{
@@ -99,7 +105,7 @@ const char* ha_getheader(ha_request_t* req, const char* name, const char* prefix
ASSERT(req && name);
- for(i = 0; i < MAX_HEADERS; i++)
+ for(i = 0; i < HA_MAX_HEADERS; i++)
{
if(req->headers[i].name)
{
@@ -125,7 +131,7 @@ void ha_addheader(ha_response_t* resp, const char* name, const char* data)
ASSERT(resp && name && data);
- for(i = 0; i < MAX_HEADERS; i++)
+ for(i = 0; i < HA_MAX_HEADERS; i++)
{
if(!(resp->headers[i].name))
{
@@ -135,7 +141,7 @@ void ha_addheader(ha_response_t* resp, const char* name, const char* data)
}
}
- ha_messagex(LOG_ERR, "too many headers in response. discarding '%s'", name);
+ ha_messagex(LOG_WARNING, "too many headers in response. discarding '%s'", name);
}
@@ -145,12 +151,41 @@ void ha_addheader(ha_response_t* resp, const char* name, const char* data)
void ha_lock(pthread_mutex_t* mtx)
{
- int r = pthread_mutex_lock(mtx ? mtx : &g_mutex);
+ int r;
+
+#ifdef _DEBUG
+ int wait = 0;
+#endif
+
+ if(!mtx)
+ mtx = &g_mutex;
+
+#ifdef _DEBUG
+ r = pthread_mutex_trylock(mtx);
+ if(r == EBUSY)
+ {
+ wait = 1;
+ ha_message(LOG_DEBUG, "thread will block: %d", pthread_self());
+ r = pthread_mutex_lock(mtx);
+ }
+
+#else
+ r = pthread_mutex_lock(mtx);
+
+#endif
+
if(r != 0)
{
errno = r;
ha_message(LOG_CRIT, "threading problem. couldn't lock mutex");
}
+
+#ifdef _DEBUG
+ else if(wait)
+ {
+ ha_message(LOG_DEBUG, "thread unblocked: %d", pthread_self());
+ }
+#endif
}
void ha_unlock(pthread_mutex_t* mtx)
@@ -586,3 +621,4 @@ int ha_genrandom(unsigned char* data, size_t len)
close(dd);
return r == -1 ? HA_FAILED : HA_OK;
}
+
diff --git a/daemon/ntlm.c b/daemon/ntlm.c
index ef889f1..bbbac5e 100644
--- a/daemon/ntlm.c
+++ b/daemon/ntlm.c
@@ -44,7 +44,6 @@ typedef struct ntlm_context
const char* backup; /* Backup server if primary is down */
int pending_max; /* Maximum number of connections at once */
int pending_timeout; /* Timeout for authentication (in seconds) */
- const char* basic_realm; /* The realm for basic authentication */
/* Context ----------------------------------------------------------- */
hash_t* pending; /* Pending connections */
@@ -57,7 +56,7 @@ ntlm_context_t;
static const ntlm_context_t ntlm_defaults =
{
NULL, NULL, NULL, DEFAULT_PENDING_MAX, DEFAULT_PENDING_TIMEOUT,
- NULL, NULL, NULL
+ NULL, NULL
};
@@ -94,12 +93,13 @@ static ntlm_connection_t* makeconnection(ntlm_context_t* ctx)
ctx->domain, conn->nonce);
if(!conn->handle)
{
- ha_messagex(LOG_ERR, "couldn't connect to the domain server %s (backup: %s)",
+ ha_messagex(LOG_ERR, "ntlm: couldn't connect to the domain server %s (backup: %s)",
ctx->server, ctx->backup ? ctx->backup : "none");
free(conn);
return NULL;
}
+ ha_messagex(LOG_INFO, "ntlm: established connection to server");
return conn;
}
@@ -109,6 +109,7 @@ static void freeconnection(ntlm_connection_t* conn)
if(conn->handle)
{
+ ha_messagex(LOG_DEBUG, "ntlm: disconnected from server");
ntlmssp_disconnect(conn->handle);
conn->handle = NULL;
}
@@ -183,7 +184,10 @@ int ntlm_auth_basic(ntlm_context_t* ctx, char* key, const char* header,
*/
conn = getpending(ctx, key);
if(conn)
+ {
+ ha_messagex(LOG_WARNING, "ntlm: basic auth killed a pending ntlm auth in progress");
freeconnection(conn);
+ }
if((r = basic_parse(header, buf, &basic)) < 0)
return r;
@@ -196,43 +200,53 @@ int ntlm_auth_basic(ntlm_context_t* ctx, char* key, const char* header,
ha_unlock(NULL);
+ if(found)
+ ha_messagex(LOG_NOTICE, "ntlm: validated basic user against cache: %s", basic.user);
- /* Try to find a domain in the user */
- if((t = strchr(basic.user, '\\')) != NULL ||
- (t = strchr(basic.user, '/')) != NULL)
+ else
{
- /* Break at the domain */
- domain = basic.user;
- basic.user = t + 1;
- *t = 0;
-
- /* Make sure this is our domain */
- if(strcasecmp(domain, ctx->domain) != 0)
- domain = NULL;
- }
+ /* Try to find a domain in the user */
+ if((t = strchr(basic.user, '\\')) != NULL ||
+ (t = strchr(basic.user, '/')) != NULL)
+ {
+ /* Break at the domain */
+ domain = basic.user;
+ basic.user = t + 1;
+ *t = 0;
+
+ /* Make sure this is our domain */
+ if(strcasecmp(domain, ctx->domain) != 0)
+ domain = NULL;
+ }
- if(!domain)
- {
- /* Use the default domain if none specified */
- domain = ctx->domain;
- }
+ if(!domain)
+ {
+ /* Use the default domain if none specified */
+ domain = ctx->domain;
+ }
- /* Make sure above did not fail */
- if(!found && basic.user && basic.user[0] && basic.password &&
- domain && domain[0])
- {
- /* We need to lock to go into smblib */
- ha_lock(&g_smblib_mutex);
+ /* Make sure above did not fail */
+ if(basic.user && basic.user[0] && basic.password &&
+ domain && domain[0])
+ {
+ ha_messagex(LOG_DEBUG, "ntlm: checking user against server: %s", basic.user);
- /* Found in smbval/valid.h */
- if(ntlmssp_validuser(basic.user, basic.password, ctx->server,
- ctx->backup, domain) == NTV_NO_ERROR)
- {
- /* If valid then we return */
- found = 1;
- }
+ /* We need to lock to go into smblib */
+ ha_lock(&g_smblib_mutex);
- ha_unlock(&g_smblib_mutex);
+ /* Found in smbval/valid.h */
+ if(ntlmssp_validuser(basic.user, basic.password, ctx->server,
+ ctx->backup, domain) == NTV_NO_ERROR)
+ {
+ /* If valid then we return */
+ found = 1;
+ }
+
+ ha_unlock(&g_smblib_mutex);
+ }
+
+ if(found)
+ ha_messagex(LOG_NOTICE, "ntlm: validated basic user against server: %s", basic.user);
}
if(found)
@@ -300,7 +314,7 @@ int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header,
r = ntlmssp_decode_msg(&ntlmssp, d, len, &flags);
if(r != 0)
{
- ha_messagex(LOG_ERR, "decoding NTLM message failed (error %d)", r);
+ ha_messagex(LOG_WARNING, "ntlm: decoding NTLMSSP message failed (error %d)", r);
resp->code = HA_SERVER_BADREQ;
goto finally;
}
@@ -340,7 +354,7 @@ int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header,
}
else
{
- ha_messagex(LOG_ERR, "received out of order NTLM request from client");
+ ha_messagex(LOG_ERR, "ntlm: received out of order NTLM request from client");
resp->code = HA_SERVER_BADREQ;
}
@@ -421,6 +435,7 @@ int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header,
}
else
{
+ ha_messagex(LOG_DEBUG, "ntlm: sending ntlm challenge");
ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf));
resp->code = HA_SERVER_DECLINE;
}
@@ -439,14 +454,14 @@ int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header,
*/
if(!conn || !conn->handle)
{
- ha_messagex(LOG_ERR, "received out of order NTLM response from client");
+ ha_messagex(LOG_WARNING, "ntlm: received out of order NTLM response from client");
resp->code = HA_SERVER_BADREQ;
goto finally;
}
if(!ntlmssp.user)
{
- ha_messagex(LOG_ERR, "received NTLM response without user name");
+ ha_messagex(LOG_WARNING, "ntlm: received NTLM response without user name");
resp->code = HA_SERVER_BADREQ;
goto finally;
}
@@ -468,7 +483,7 @@ int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header,
* Note that we don't set a code here. This causes our
* caller to put in all the proper headers for us.
*/
- ha_messagex(LOG_ERR, "failed NTLM logon for user '%s'", ntlmssp.user);
+ ha_messagex(LOG_WARNING, "ntlm: failed NTLM logon for user '%s'", ntlmssp.user);
ret = HA_FALSE;
}
@@ -477,6 +492,7 @@ int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header,
{
int r;
resp->detail = ntlmssp.user;
+ ha_messagex(LOG_NOTICE, "ntlm: validated ntlm user against server", ntlmssp.user);
ha_lock(NULL);
@@ -500,7 +516,7 @@ int ntlm_auth_ntlm(ntlm_context_t* ctx, void* key, const char* header,
}
default:
- ha_messagex(LOG_ERR, "received invalid NTLM message (type %d)", ntlmssp.msg_type);
+ ha_messagex(LOG_WARNING, "ntlm: received invalid NTLM message (type %d)", ntlmssp.msg_type);
resp->code = HA_SERVER_BADREQ;
goto finally;
};
@@ -555,12 +571,6 @@ int ntlm_config(ha_context_t* context, const char* name, const char* value)
return ha_confint(name, value, 1, 86400, &(ctx->pending_timeout));
}
- else if(strcmp(name, "realm") == 0)
- {
- ctx->basic_realm = value;
- return HA_OK;
- }
-
return HA_FALSE;
}
@@ -574,7 +584,7 @@ int ntlm_init(ha_context_t* context)
ASSERT(ctx);
/* Make sure there are some types of authentication we can do */
- if(!(context->types & (HA_TYPE_BASIC | HA_TYPE_NTLM)))
+ if(!(context->opts.types & (HA_TYPE_BASIC | HA_TYPE_NTLM)))
{
ha_messagex(LOG_ERR, "NTLM module configured, but does not implement any "
"configured authentication type.");
@@ -599,6 +609,8 @@ int ntlm_init(ha_context_t* context)
ha_messagex(LOG_CRIT, "out of memory");
return HA_CRITERROR;
}
+
+ ha_messagex(LOG_INFO, "ntlm: initialized handler");
}
/* Global Initialization */
@@ -630,6 +642,8 @@ void ntlm_destroy(ha_context_t* context)
if(ctx->established)
hash_free(ctx->established);
+
+ ha_messagex(LOG_INFO, "ntlm: uninitialized handler");
}
/* Global Destroy */
@@ -649,7 +663,7 @@ int ntlm_process(ha_context_t* context, ha_request_t* req,
unsigned char key[NTLM_HASH_KEY_LEN];
const char* header = NULL;
time_t t = time(NULL);
- int ret;
+ int ret, r;
ASSERT(context && req && resp && buf);
ASSERT(req->args[AUTH_ARG_CONN]);
@@ -667,14 +681,16 @@ int ntlm_process(ha_context_t* context, ha_request_t* req,
* authenticated connections which have expired as
* well as half open connections which expire.
*/
- hash_purge(ctx->pending, t - ctx->pending_timeout);
- hash_purge(ctx->established, t - context->cache_timeout);
+ r = hash_purge(ctx->pending, t - ctx->pending_timeout);
+ r += hash_purge(ctx->established, t - context->opts.cache_timeout);
ha_unlock(NULL);
+ if(r > 0)
+ ha_messagex(LOG_DEBUG, "ntlm: purged info from cache: %d", r);
/* Look for a NTLM header */
- if(context->types & HA_TYPE_NTLM)
+ if(context->opts.types & HA_TYPE_NTLM)
{
header = ha_getheader(req, "Authorization", HA_PREFIX_NTLM);
if(header)
@@ -683,6 +699,7 @@ int ntlm_process(ha_context_t* context, ha_request_t* req,
while(*header && isspace(*header))
header++;
+ ha_messagex(LOG_DEBUG, "ntlm: processing ntlm auth header");
ret = ntlm_auth_ntlm(ctx, key, header, resp, buf);
if(ret < 0)
return ret;
@@ -690,7 +707,7 @@ int ntlm_process(ha_context_t* context, ha_request_t* req,
}
/* If basic is enabled, and no NTLM */
- if(!header && context->types & HA_TYPE_BASIC)
+ if(!header && context->opts.types & HA_TYPE_BASIC)
{
/* Look for a Basic header */
header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC);
@@ -700,6 +717,7 @@ int ntlm_process(ha_context_t* context, ha_request_t* req,
while(*header && isspace(*header))
header++;
+ ha_messagex(LOG_DEBUG, "ntlm: processing basic auth header");
ret = ntlm_auth_basic(ctx, key, header, resp, buf);
if(ret < 0)
return ret;
@@ -725,6 +743,11 @@ int ntlm_process(ha_context_t* context, ha_request_t* req,
}
ha_unlock(NULL);
+
+ if(resp->code == HA_SERVER_ACCEPT)
+ ha_messagex(LOG_NOTICE, "ntlm: validated user against connection cache");
+
+ /* TODO: We need to be able to retrieve the user here somehow */
}
@@ -734,18 +757,21 @@ int ntlm_process(ha_context_t* context, ha_request_t* req,
/* If authentication failed tell the browser about it */
resp->code = HA_SERVER_DECLINE;
- if(context->types & HA_TYPE_NTLM)
+ if(context->opts.types & HA_TYPE_NTLM)
+ {
ha_addheader(resp, "WWW-Authenticate", HA_PREFIX_NTLM);
+ ha_messagex(LOG_DEBUG, "ntlm: sent ntlm auth request");
+ }
- if(context->types & HA_TYPE_BASIC)
+ if(context->opts.types & HA_TYPE_BASIC)
{
- ha_bufmcat(buf, HA_PREFIX_BASIC, "realm=\"",
- ctx->basic_realm ? ctx->basic_realm : "", "\"", NULL);
+ ha_bufmcat(buf, HA_PREFIX_BASIC, "realm=\"", context->opts.realm, "\"", NULL);
if(ha_buferr(buf))
return HA_CRITERROR;
ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf));
+ ha_messagex(LOG_DEBUG, "ntlm: sent basic auth request");
}
}
diff --git a/daemon/simple.c b/daemon/simple.c
index 4c7fb28..5b4eb60 100644
--- a/daemon/simple.c
+++ b/daemon/simple.c
@@ -25,18 +25,12 @@ unsigned char g_simple_secret[DIGEST_SECRET_LEN];
typedef struct simple_context
{
+ /* Settings ----------------------------------------------------------- */
const char* filename; /* The file name with the user names */
- const char* realm; /* The realm for basic authentication */
- const char* domains; /* Domains for which digest auth is valid */
- int cache_max; /* Maximum number of connections at once */
- int cache_timeout;
+ ha_options_t* opts; /* Options from httpauthd.c */
/* Context ----------------------------------------------------------- */
hash_t* cache; /* Some cached records or basic */
-
-#ifdef _DEBUG
- const char* debug_nonce;
-#endif
}
simple_context_t;
@@ -57,7 +51,7 @@ static digest_record_t* get_cached_digest(simple_context_t* ctx, unsigned char*
ASSERT(ctx && nonce);
- if(ctx->cache_max == 0)
+ if(ctx->opts->cache_max == 0)
return NULL;
ha_lock(NULL);
@@ -95,7 +89,7 @@ static int save_cached_digest(simple_context_t* ctx, digest_record_t* rec)
ASSERT(ctx && rec);
- if(ctx->cache_max == 0)
+ if(ctx->opts->cache_max == 0)
{
free_hash_object(NULL, rec);
return HA_FALSE;
@@ -103,7 +97,7 @@ static int save_cached_digest(simple_context_t* ctx, digest_record_t* rec)
ha_lock(NULL);
- while(hash_count(ctx->cache) >= ctx->cache_max)
+ while(hash_count(ctx->cache) >= ctx->opts->cache_max)
hash_bump(ctx->cache);
r = hash_set(ctx->cache, rec->nonce, rec);
@@ -126,12 +120,12 @@ static int add_cached_basic(simple_context_t* ctx, unsigned char* key)
ASSERT(ctx && key);
- if(ctx->cache_max == 0)
+ if(ctx->opts->cache_max == 0)
return HA_FALSE;
ha_lock(NULL);
- while(hash_count(ctx->cache) >= ctx->cache_max)
+ while(hash_count(ctx->cache) >= ctx->opts->cache_max)
hash_bump(ctx->cache);
r = hash_set(ctx->cache, key, BASIC_ESTABLISHED);
@@ -158,11 +152,12 @@ static int complete_digest_ha1(simple_context_t* ctx, digest_record_t* rec,
int ret = HA_FALSE;
ASSERT(ctx && rec && buf && user && user[0]);
+ ha_messagex(LOG_DEBUG, "searching password file for user's ha1: %s", user);
f = fopen(ctx->filename, "r");
if(!f)
{
- ha_message(LOG_ERR, "can't open file for basic auth: %s", ctx->filename);
+ ha_message(LOG_ERR, "simple: can't open file for basic auth: %s", ctx->filename);
return HA_FAILED;
}
@@ -178,7 +173,7 @@ static int complete_digest_ha1(simple_context_t* ctx, digest_record_t* rec,
if(ferror(f))
{
- ha_message(LOG_ERR, "error reading basic password file");
+ ha_message(LOG_ERR, "simple: error reading basic password file");
ret = HA_FAILED;
break;
}
@@ -201,7 +196,7 @@ static int complete_digest_ha1(simple_context_t* ctx, digest_record_t* rec,
t2++;
/* Check the realm */
- if(strcmp(t, ctx->realm) == 0)
+ if(strcmp(t, ctx->opts->realm) == 0)
{
len = MD5_LEN;
@@ -209,6 +204,7 @@ static int complete_digest_ha1(simple_context_t* ctx, digest_record_t* rec,
t = ha_bufdechex(buf, t2, &len);
if(t && len == MD5_LEN)
{
+ ha_messagex(LOG_DEBUG, "simple: found ha1 for user: %s", user);
memcpy(rec->ha1, t, MD5_LEN);
ret = HA_OK;
break;
@@ -217,7 +213,7 @@ static int complete_digest_ha1(simple_context_t* ctx, digest_record_t* rec,
}
if(!t2 || ret != HA_OK)
- ha_messagex(LOG_WARNING, "user '%s' found in file, but password not in digest format", user);
+ ha_messagex(LOG_WARNING, "simple: user '%s' found in file, but password not in digest format", user);
}
}
}
@@ -243,15 +239,16 @@ static int validate_user_password(simple_context_t* ctx, ha_buffer_t* buf,
ASSERT(ctx && buf);
ASSERT(user && user[0] && clearpw);
+ ha_messagex(LOG_DEBUG, "simple: validating user against password file: %s", user);
f = fopen(ctx->filename, "r");
if(!f)
{
- ha_message(LOG_ERR, "can't open file for basic auth: %s", ctx->filename);
+ ha_message(LOG_ERR, "simple: can't open file for basic auth: %s", ctx->filename);
return HA_FAILED;
}
- digest_makeha1(ha1, user, ctx->realm, clearpw);
+ digest_makeha1(ha1, user, ctx->opts->realm, clearpw);
/*
* Note: There should be no returns or jumps between
@@ -265,7 +262,7 @@ static int validate_user_password(simple_context_t* ctx, ha_buffer_t* buf,
if(ferror(f))
{
- ha_message(LOG_ERR, "error reading basic password file");
+ ha_message(LOG_ERR, "simple: error reading basic password file");
ret = HA_FAILED;
break;
}
@@ -298,6 +295,7 @@ static int validate_user_password(simple_context_t* ctx, ha_buffer_t* buf,
if(strcmp(crypt(clearpw, t), t) == 0)
{
+ ha_messagex(LOG_DEBUG, "simple: found valid crypt password for user: %s", user);
ret = HA_OK;
break;
}
@@ -310,7 +308,7 @@ static int validate_user_password(simple_context_t* ctx, ha_buffer_t* buf,
t2++;
/* Check the realm */
- if(strcmp(t, ctx->realm) == 0)
+ if(strcmp(t, ctx->opts->realm) == 0)
{
len = MD5_LEN;
@@ -318,6 +316,7 @@ static int validate_user_password(simple_context_t* ctx, ha_buffer_t* buf,
t = ha_bufdechex(buf, t2, &len);
if(t && len == MD5_LEN && memcmp(ha1, t, MD5_LEN) == 0)
{
+ ha_messagex(LOG_DEBUG, "simple: found valid ha1 for user: %s", user);
ret = HA_OK;
break;
}
@@ -356,6 +355,7 @@ static int simple_basic_response(simple_context_t* ctx, const char* header,
/* Check and see if this connection is in the cache */
if(have_cached_basic(ctx, basic.key))
{
+ ha_messagex(LOG_NOTICE, "simple: validated basic user against cache: %s", basic.user);
found = 1;
ret = HA_OK;
goto finally;
@@ -369,9 +369,15 @@ static int simple_basic_response(simple_context_t* ctx, const char* header,
ret = validate_user_password(ctx, buf, basic.user, basic.password);
+ if(ret == HA_OK)
+ ha_messagex(LOG_NOTICE, "simple: validated basic user against file: %s", basic.user);
+
+ else
+ ha_messagex(LOG_WARNING, "simple: basic authentication failed for user: %s", basic.user);
+
finally:
- if(ret = HA_OK)
+ if(ret == HA_OK)
{
resp->code = HA_SERVER_ACCEPT;
resp->detail = basic.user;
@@ -384,7 +390,7 @@ finally:
}
static int simple_digest_challenge(simple_context_t* ctx, ha_response_t* resp,
- ha_buffer_t* buf, int stale)
+ ha_buffer_t* buf, int stale)
{
const char* nonce_str;
const char* header;
@@ -394,10 +400,10 @@ static int simple_digest_challenge(simple_context_t* ctx, ha_response_t* resp,
/* Generate an nonce */
#ifdef _DEBUG
- if(ctx->debug_nonce)
+ if(ctx->opts->digest_debugnonce)
{
- nonce_str = ctx->debug_nonce;
- ha_messagex(LOG_WARNING, "using debug nonce. security non-existant.");
+ nonce_str = ctx->opts->digest_debugnonce;
+ ha_messagex(LOG_WARNING, "simple: using debug nonce. security non-existant.");
}
else
#endif
@@ -412,7 +418,8 @@ static int simple_digest_challenge(simple_context_t* ctx, ha_response_t* resp,
/* Now generate a message to send */
- header = digest_challenge(buf, nonce_str, ctx->realm, ctx->domains, stale);
+ header = digest_challenge(buf, nonce_str, ctx->opts->realm,
+ ctx->opts->digest_domains, stale);
if(!header)
return HA_CRITERROR;
@@ -421,6 +428,7 @@ static int simple_digest_challenge(simple_context_t* ctx, ha_response_t* resp,
resp->code = HA_SERVER_DECLINE;
ha_addheader(resp, "WWW-Authenticate", header);
+ ha_messagex(LOG_DEBUG, "simple: created digest challenge with nonce: %s", nonce_str);
return HA_OK;
}
@@ -446,18 +454,18 @@ static int simple_digest_response(simple_context_t* ctx, const char* header,
return r;
#ifdef _DEBUG
- if(ctx->debug_nonce)
+ if(ctx->opts->digest_debugnonce)
{
- if(dg.nonce && strcmp(dg.nonce, ctx->debug_nonce) != 0)
+ if(dg.nonce && strcmp(dg.nonce, ctx->opts->digest_debugnonce) != 0)
{
resp->code = HA_SERVER_BADREQ;
ret = HA_FALSE;
- ha_messagex(LOG_WARNING, "digest response contains invalid nonce");
+ ha_messagex(LOG_WARNING, "simple: digest response contains invalid nonce");
goto finally;
}
/* Do a rough hash into the real nonce, for use as a key */
- md5_string(nonce, ctx->debug_nonce);
+ md5_string(nonce, ctx->opts->digest_debugnonce);
/* Debug nonce's never expire */
expiry = time(NULL);
@@ -471,7 +479,7 @@ static int simple_digest_response(simple_context_t* ctx, const char* header,
if(r == HA_FALSE)
{
resp->code = HA_SERVER_BADREQ;
- ha_messagex(LOG_WARNING, "digest response contains invalid nonce");
+ ha_messagex(LOG_WARNING, "simple: digest response contains invalid nonce");
}
ret = r;
@@ -482,14 +490,19 @@ static int simple_digest_response(simple_context_t* ctx, const char* header,
rec = get_cached_digest(ctx, nonce);
/* Check to see if we're stale */
- if((expiry + ctx->cache_timeout) <= time(NULL))
+ if((expiry + ctx->opts->cache_timeout) <= time(NULL))
{
+ ha_messagex(LOG_INFO, "simple: nonce expired, sending stale challenge: %s",
+ dg.username);
+
stale = 1;
goto finally;
}
if(!rec)
{
+ ha_messagex(LOG_INFO, "simple: no record in cache, creating one: %s", dg.username);
+
/*
* If we're valid but don't have a record in the
* cache then complete the record properly.
@@ -513,7 +526,8 @@ static int simple_digest_response(simple_context_t* ctx, const char* header,
/* Increment our nonce count */
rec->nc++;
- ret = digest_check(ctx->realm, method, uri, buf, &dg, rec);
+ ret = digest_check(ctx->opts->realm, method,
+ ctx->opts->digest_ignoreuri ? NULL : uri, buf, &dg, rec);
if(ret == HA_BADREQ)
{
@@ -527,8 +541,12 @@ static int simple_digest_response(simple_context_t* ctx, const char* header,
resp->detail = dg.username;
/* Figure out if we need a new nonce */
- if((expiry + (ctx->cache_timeout - (ctx->cache_timeout / 8))) < time(NULL))
+ if((expiry + (ctx->opts->cache_timeout -
+ (ctx->opts->cache_timeout / 8))) < time(NULL))
{
+ ha_messagex(LOG_INFO, "simple: nonce almost expired, creating new one: %s",
+ dg.username);
+
digest_makenonce(nonce, g_simple_secret, NULL);
stale = 1;
}
@@ -543,6 +561,8 @@ static int simple_digest_response(simple_context_t* ctx, const char* header,
if(t[0])
ha_addheader(resp, "Authentication-Info", t);
+ ha_messagex(LOG_NOTICE, "simple: validated digest user: %s", dg.username);
+
/* Put the connection into the cache */
if((r = save_cached_digest(ctx, rec)) < 0)
ret = r;
@@ -580,26 +600,6 @@ int simple_config(ha_context_t* context, const char* name, const char* value)
return HA_OK;
}
- else if(strcmp(name, "realm") == 0)
- {
- ctx->realm = value;
- return HA_OK;
- }
-
- else if(strcmp(name, "digestdomains") == 0)
- {
- ctx->domains = value;
- return HA_OK;
- }
-
-#ifdef _DEBUG
- else if(strcmp(name, "digestdebugnonce") == 0)
- {
- ctx->debug_nonce = value;
- return HA_OK;
- }
-#endif
-
return HA_FALSE;
}
@@ -608,6 +608,7 @@ int simple_init(ha_context_t* context)
/* Global initialization */
if(!context)
{
+ ha_messagex(LOG_DEBUG, "simple: generating secret");
return ha_genrandom(g_simple_secret, DIGEST_SECRET_LEN);
}
@@ -620,9 +621,9 @@ int simple_init(ha_context_t* context)
ASSERT(ctx);
/* Make sure there are some types of authentication we can do */
- if(!(context->types & (HA_TYPE_BASIC | HA_TYPE_DIGEST)))
+ if(!(context->opts.types & (HA_TYPE_BASIC | HA_TYPE_DIGEST)))
{
- ha_messagex(LOG_ERR, "Simple module configured, but does not implement any "
+ ha_messagex(LOG_ERR, "simple: module configured, but does not implement any "
"configured authentication type.");
return HA_FAILED;
}
@@ -631,7 +632,7 @@ int simple_init(ha_context_t* context)
/* Check to make sure the file exists */
if(!ctx->filename)
{
- ha_messagex(LOG_ERR, "Basic configuration incomplete. "
+ ha_messagex(LOG_ERR, "simple: configuration incomplete. "
"Must have a PasswordFile configured.");
return HA_FAILED;
}
@@ -639,7 +640,7 @@ int simple_init(ha_context_t* context)
fd = open(ctx->filename, O_RDONLY);
if(fd == -1)
{
- ha_message(LOG_ERR, "can't open file for simple authentication: %s", ctx->filename);
+ ha_message(LOG_ERR, "simple: can't open file for authentication: %s", ctx->filename);
return HA_FAILED;
}
@@ -655,9 +656,9 @@ int simple_init(ha_context_t* context)
}
/* Copy some settings over for easy access */
- ctx->cache_max = context->cache_max;
- ctx->cache_timeout = context->cache_timeout;
+ ctx->opts = &(context->opts);
+ ha_messagex(LOG_INFO, "simple: initialized handler");
}
return HA_OK;
@@ -673,6 +674,8 @@ void simple_destroy(ha_context_t* context)
if(ctx->cache)
hash_free(ctx->cache);
+
+ ha_messagex(LOG_INFO, "simple: uninitialized handler");
}
}
@@ -684,6 +687,7 @@ int simple_process(ha_context_t* context, ha_request_t* req,
int ret = HA_FALSE;
int found = 0;
basic_header_t basic;
+ int r;
ASSERT(context && req && resp && buf);
ASSERT(req->args[AUTH_ARG_METHOD]);
@@ -692,21 +696,24 @@ int simple_process(ha_context_t* context, ha_request_t* req,
ha_lock(NULL);
/* Purge the cache */
- hash_purge(ctx->cache, time(NULL) - ctx->cache_timeout);
+ r = hash_purge(ctx->cache, time(NULL) - ctx->opts->cache_timeout);
ha_unlock(NULL);
+ if(r > 0)
+ ha_messagex(LOG_DEBUG, "simple: purged cache records: %d", r);
/* We use this below to detect whether to send a default response */
resp->code = -1;
/* Check the headers and see if we got a response thingy */
- if(context->types & HA_TYPE_DIGEST)
+ if(context->opts.types & HA_TYPE_DIGEST)
{
header = ha_getheader(req, "Authorization", HA_PREFIX_DIGEST);
if(header)
{
+ ha_messagex(LOG_DEBUG, "simple: processing digest auth header");
ret = simple_digest_response(ctx, header, req->args[AUTH_ARG_METHOD],
req->args[AUTH_ARG_URI], resp, buf);
if(ret < 0)
@@ -715,8 +722,9 @@ int simple_process(ha_context_t* context, ha_request_t* req,
}
/* Or a basic authentication */
- if(!header && context->types & HA_TYPE_BASIC)
+ if(!header && context->opts.types & HA_TYPE_BASIC)
{
+ ha_messagex(LOG_DEBUG, "simple: processing basic auth header");
header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC);
if(header)
{
@@ -732,17 +740,18 @@ int simple_process(ha_context_t* context, ha_request_t* req,
{
resp->code = HA_SERVER_DECLINE;
- if(context->types & HA_TYPE_BASIC)
+ if(context->opts.types & HA_TYPE_BASIC)
{
- ha_bufmcat(buf, "BASIC realm=\"", ctx->realm , "\"", NULL);
+ ha_bufmcat(buf, "BASIC realm=\"", ctx->opts->realm , "\"", NULL);
if(ha_buferr(buf))
return HA_CRITERROR;
ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf));
+ ha_messagex(LOG_DEBUG, "simple: sent basic auth request");
}
- if(context->types & HA_TYPE_DIGEST)
+ if(context->opts.types & HA_TYPE_DIGEST)
{
ret = simple_digest_challenge(ctx, resp, buf, 0);
if(ret < 0)
diff --git a/doc/httpauth.conf.5 b/doc/httpauth.conf.5
new file mode 100644
index 0000000..fd26c7b
--- /dev/null
+++ b/doc/httpauth.conf.5
@@ -0,0 +1,82 @@
+.Dd April, 2004
+.Dt HTTPAUTH.CONF 5
+.Os httpauth
+.Sh NAME
+.Nm httpauth.conf
+.Nd the configuration file for
+.Em httpauthd
+.Sh DESCRIPTION
+The XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx
+.Nm
+scripting language is a regular expression language used for fine grained,
+buffer based search and replace. It is not limited to lines. A full description
+of what
+.Nm
+is capable of is outside the scope of this document.
+.Pp
+.Ar script
+is a text or compiled
+.Nm
+script. For details see the language documentation that came along with the distribution.
+.Pp
+When used with the
+.Fl f
+argument
+.Nm
+replaces files in place. Otherwise it reads from
+.Ar infile
+and writes to
+.Ar outfile
+\&. If either infile or outfile are missing or are equal to a dash
+.Sq Li -
+, then rep processes
+.Em stdin
+or
+.Em stdout
+respectively.
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl b
+Backup files where replacements have occurred. The backup files have an
+.Sq x_r
+extension appended to their filename.
+.It Fl i
+Prompt for confirmation before each replacement.
+.It Fl p
+Only output replaced text. Can be used as a rudimentary parser.
+.It Fl q
+Supress status messages. Only errors will be sent to stderr.
+.It Fl z
+Set the replacement buffer size to
+.Ar buffsize .
+This speeds up execution as regular expressions only have to act on a small
+portion of the whole file at once. However the largest match will be limited to
+roughly
+.Ar buffsize
+, so use this option with care. The script loops over each buffer until no more
+matches are found within it. Care is taken to overlap the buffers as much as
+possible to ensure that any match smaller than
+.Ar buffsize
+can be matched.
+.Sh NOTE
+The
+.Nm
+command uses
+.Xr getopt 3
+to parse it's arguments, which allows it to accept
+the
+.Sq Li --
+option which will stop processing of flag options at that point. This allows
+the processing of files with names that begin with a dash
+.Pq Sq - .
+.Sh BUGS
+When reading from
+.Em stdin
+you must specify a buffer size.
+.Sh SEE ALSO
+.Xr repc 1 ,
+.Xr rlib 3 ,
+.Xr pcre 3
+.Sh AUTHOR
+.An Nate Nielsen Aq nielsen@memberwebs.com \ No newline at end of file
diff --git a/doc/httpauth.conf.sample b/doc/httpauth.conf.sample
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/doc/httpauth.conf.sample
diff --git a/doc/protocol.txt b/doc/protocol.txt
index 05e32f2..b6bc0ba 100644
--- a/doc/protocol.txt
+++ b/doc/protocol.txt
@@ -1,22 +1,39 @@
- HTTP/AUTH PROTOCOL
+ HTTPAUTH PROTOCOL
The protocol used between the stateful authenticator and the web servers
that wish to authenticate is described below. It's a simple text protocol,
-similar to HTTP. The web servers send commands and headers to the daemon,
+similar to HTTP. The web server sends commands and headers to the daemon,
which replies with HTTP codes and headers.
Multiple authentication requests can be processed on the same connection,
-although the connection is not stateful. A authentication request initially
-processed through one connection to the daemon can later be completed
-through another. The commands are described below.
+although the connection is not necessarily stateful. A authentication
+request initially processed through one connection to the daemon can
+later be completed through another. The commands are described below.
-AUTH method uri
+After connecting to the daemon, you'll need to retrieve the initial
+'ready' response before sending requests. See below. If the server
+responds with a 5xx message then something's gone wrong and no requests
+will be accepted on the connection.
+
+
+REQUESTS -------------------------------------------------------------------
+
+AUTH authmethod connid method uri
The AUTH command asks the daemon to perform authentication
- for a given set of headers.
+ for a given set of header from the client. None of the
+ arguments should contain spaces.
+
+ authmethod: is the authentication method. Methods are
+ defined by the daemon in it's config file.
+
+ connid: a unique string identifying the connection from
+ the client. This is only important when NTLM is
+ being used. If not, pass a random string.
+
+ method: The HTTP method employed. 'GET' or 'POST' etc...
- method: is the authentication type. It might be 'NTLM'.
uri: the URI being authenticated.
The AUTH command is followed by HTTP headers, one per line
@@ -24,23 +41,63 @@ AUTH method uri
authentication headers for the authentication protocol being
used. Extraneous headers are ignored.
- If multiple HTTP headers with the same name are received, then
- the last one is used. Note that this is somewhat different than
- the HTTP protocol.
+ Headers should be specified on one line, not 'wrapped' as is
+ permissible in HTTP.
QUIT
This closes the connection to the daemon.
-The response from the daemon consists of an HTTP code, followed by headers
-one per line. Note that only the headers to be added for authentication are
-returned. For example:
- 401
- Header: value
- Header2: value
+RESPONSES -------------------------------------------------------------------
+
+The response from the daemon consists of a code, followed by a detail
+message value or set of values. This is separated from the code by a
+space. The content of the detail message is described below.
+
+The codes are similar to HTTP:
+
+ 100 Ready
+ (detail is the list of available authmethods)
+
+ 200 Successful Request
+ (detail is described below)
+
+ 4xx Request Error
+ (detail is an error message)
+
+ 5xx Server Error
+ (detail is an error message)
+
+
+READY
+
+After opening a connection to the daemon, you should receive a response
+(outlined below) of 100 indicating ready. The 'detail' value is set
+to the list of authmethods that the daemon is configured to provide.
+These are separated by spaces:
+
+ 100 Domain Simple Test LDAP
+
+
+SUCCESS
+
+Successful processing of a request returns a 200. The detail constists of,
+an HTTP code to send to the client and the user name (when authentication is
+successful). These are separated by spaces.
+
+In addition the daemon might send a set of headers that must be sent to
+the client. These are ended by a blank line.
+
+A response after client authentication failed might look like this:
+
+ 200 401
+ WWW-Authenticate: realm="blah"
+
+... or a client authentication success response like this:
+
+ 200 200 testo
+ Authorization-Info: Digest rspauth="2034980294820398" nonce="2049823094328" ...
-Success returns a 200, just like normal HTTP. Note that success can contain
-headers that must also be sent to the client.
diff --git a/sample/httpauthd.conf b/sample/httpauthd.conf
index ba8af0a..ae59ce2 100644
--- a/sample/httpauthd.conf
+++ b/sample/httpauthd.conf
@@ -4,6 +4,7 @@
MaxThreads: 18
CacheTimeout: 300
AuthTypes: Basic Digest
+Socket: 0.0.0.0:8020
[Simple]
Realm: blah