summaryrefslogtreecommitdiff
path: root/daemon/ntlm.c
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2006-05-10 17:33:18 +0000
committerStef Walter <stef@memberwebs.com>2006-05-10 17:33:18 +0000
commitf5164b1be2a17e2dba5908f08ba270cf035d6546 (patch)
tree85c5d7973851d9f3105e92b900a50a43c12f1a94 /daemon/ntlm.c
parente593016a80ceee52b6e3244512ff4307f8c208fa (diff)
Some missing files
Diffstat (limited to 'daemon/ntlm.c')
-rw-r--r--daemon/ntlm.c825
1 files changed, 825 insertions, 0 deletions
diff --git a/daemon/ntlm.c b/daemon/ntlm.c
new file mode 100644
index 0000000..dc22f7c
--- /dev/null
+++ b/daemon/ntlm.c
@@ -0,0 +1,825 @@
+/*
+ * Copyright (c) 2004, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ *
+ */
+
+#include "usuals.h"
+#include "httpauthd.h"
+#include "hash.h"
+#include "defaults.h"
+#include "md5.h"
+#include "basic.h"
+#include "stringx.h"
+
+#include <syslog.h>
+
+/* The NTLM headers */
+#include "ntlmssp.h"
+
+/* -------------------------------------------------------------------------------
+ * Defaults and Constants
+ */
+
+#define NTLM_HASH_KEY_LEN MD5_LEN
+#define NTLM_ESTABLISHED (void*)1
+
+
+/* -------------------------------------------------------------------------------
+ * Structures and Globals
+ */
+
+/* A pending connection */
+typedef struct ntlm_connection
+{
+ void* handle;
+ unsigned char nonce[NONCE_LEN];
+ unsigned int flags;
+}
+ntlm_connection_t;
+
+
+/* The main context */
+typedef struct ntlm_context
+{
+ /* Read Only --------------------------------------------------------- */
+ const char* server; /* Server to authenticate against */
+ const char* domain; /* NTLM domain to authenticate against */
+ 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) */
+
+ /* Require Locking --------------------------------------------------- */
+ hsh_t* pending; /* Pending connections */
+ hsh_t* established; /* Established connections */
+}
+ntlm_context_t;
+
+
+/* The default context settings */
+static const ntlm_context_t ntlm_defaults =
+{
+ NULL, NULL, NULL, DEFAULT_PENDING_MAX, DEFAULT_PENDING_TIMEOUT,
+ NULL, NULL
+};
+
+
+/* Mutexes for accessing non-thread-safe smblib */
+static pthread_mutex_t g_smblib_mutex;
+static pthread_mutexattr_t g_smblib_mutexattr;
+
+
+/* -------------------------------------------------------------------------------
+ * Internal Functions
+ */
+
+static ntlm_connection_t* makeconnection(ha_request_t* rq, ntlm_context_t* ctx)
+{
+ ntlm_connection_t* conn;
+
+ ASSERT(ctx);
+
+ conn = (ntlm_connection_t*)malloc(sizeof(ntlm_connection_t));
+ if(!conn)
+ {
+ ha_messagex(NULL, LOG_CRIT, "out of memory");
+ return NULL;
+ }
+
+ memset(conn, 0, sizeof(*conn));
+
+ /*
+ * Open a connection to to the domain controller. I don't think
+ * we can cache these connections or use them again as opening
+ * a connection here results in an nonce being generated.
+ */
+ conn->handle = ntlmssp_connect(ctx->server, ctx->backup,
+ ctx->domain, (char*)conn->nonce);
+ if(!conn->handle)
+ {
+ ha_messagex(rq, LOG_ERR, "couldn't connect to the domain server %s (backup: %s)",
+ ctx->server, ctx->backup ? ctx->backup : "none");
+ free(conn);
+ return NULL;
+ }
+
+ ha_messagex(rq, LOG_INFO, "established connection to server");
+ return conn;
+}
+
+static void freeconnection(ha_request_t* rq, ntlm_connection_t* conn)
+{
+ ASSERT(conn);
+
+ if(conn->handle)
+ {
+ ha_messagex(rq, LOG_DEBUG, "disconnected from server");
+ ntlmssp_disconnect(conn->handle);
+ conn->handle = NULL;
+ }
+
+ free(conn);
+}
+
+static void free_hash_object(void* arg, void* val)
+{
+ if(val)
+ {
+ ASSERT(val != NTLM_ESTABLISHED);
+ freeconnection(NULL, (ntlm_connection_t*)val);
+ }
+}
+
+static ntlm_connection_t* getpending(ntlm_context_t* ctx, const void* key)
+{
+ ntlm_connection_t* ret;
+
+ ASSERT(ctx && key);
+
+ ha_lock(NULL);
+
+ ret = (ntlm_connection_t*)hsh_rem(ctx->pending, key);
+
+ ha_unlock(NULL);
+
+ return ret;
+}
+
+static int putpending(ntlm_context_t* ctx, const void* key, ntlm_connection_t* conn)
+{
+ int r = 0;
+
+ ASSERT(ctx && key && conn);
+ ASSERT(conn->handle);
+
+ ha_lock(NULL);
+
+ if(!hsh_get(ctx->pending, key))
+ {
+ if(!hsh_set(ctx->pending, key, (void*)conn))
+ {
+ free_hash_object(NULL, conn);
+ ha_messagex(NULL, LOG_ERR, "out of memory");
+ r = -1;
+ }
+ }
+
+ ha_unlock(NULL);
+
+ return r;
+}
+
+int ntlm_auth_basic(ha_request_t* rq, ntlm_context_t* ctx, unsigned char* key,
+ const char* header)
+{
+ ntlm_connection_t* conn;
+ char* t;
+ basic_header_t basic;
+ const char* domain = NULL;
+ int found = 0;
+ int r;
+
+ ASSERT(ctx && key && header && rq);
+
+ /*
+ * We're doing basic authentication on the connection
+ * which invalidates any NTLM authentication we've started
+ * or done on this connection.
+ */
+ conn = getpending(ctx, key);
+ if(conn)
+ {
+ ha_messagex(rq, LOG_WARNING, "basic auth killed a pending ntlm auth in progress");
+ freeconnection(rq, conn);
+ }
+
+ if((r = basic_parse(header, rq->buf, &basic)) < 0)
+ return r;
+
+ /* Check and see if this connection is in the cache */
+ ha_lock(NULL);
+
+ if(hsh_get(ctx->established, basic.key) == NTLM_ESTABLISHED)
+ found = 1;
+
+ ha_unlock(NULL);
+
+ if(found)
+ ha_messagex(rq, LOG_NOTICE, "validated basic user against cache: %s", basic.user);
+
+ else
+ {
+ /* 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;
+ }
+
+ /* Make sure above did not fail */
+ if(basic.user && basic.user[0] && basic.password &&
+ domain && domain[0])
+ {
+ ha_messagex(rq, LOG_DEBUG, "checking user against server: %s", basic.user);
+
+ /* We need to lock to go into smblib */
+ ha_lock(&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(rq, LOG_NOTICE, "validated basic user against server: %s", basic.user);
+ }
+
+ if(found)
+ {
+ int r;
+ rq->resp_code = HA_SERVER_OK;
+ rq->resp_detail = basic.user;
+
+ ha_lock(NULL);
+
+ /* We put this connection into the successful connections */
+ r = hsh_set(ctx->established, basic.key, NTLM_ESTABLISHED);
+
+ ha_unlock(NULL);
+
+ if(!r)
+ {
+ ha_messagex(NULL, LOG_CRIT, "out of memory");
+ return HA_CRITERROR;
+ }
+
+ return HA_OK;
+ }
+
+ return HA_FALSE;
+}
+
+int ntlm_auth_ntlm(ha_request_t* rq, ntlm_context_t* ctx, void* key,
+ const char* header)
+{
+ ntlmssp_info_rec ntlmssp;
+ ntlm_connection_t* conn = NULL;
+ unsigned int flags = 0;
+ int ret = HA_FALSE;
+ size_t len = 0;
+ void* d;
+ int r;
+
+ ASSERT(ctx && key && header && rq);
+
+ /*
+ * Retrieve and remove the connection from the pending bag.
+ * We add it back again below if that's necessary.
+ */
+ conn = getpending(ctx, key);
+
+ /*
+ * We use the flags from an already established connection
+ * if we've been pending and stuff
+ */
+
+ if(conn && conn->flags)
+ flags = conn->flags;
+
+ /*
+ * First we figure out what kind of message the client
+ * is sending us.
+ */
+
+ d = ha_bufdec64(rq->buf, header, &len);
+
+ if(!d || len == 0)
+ RETURN(HA_FALSE);
+
+ r = ntlmssp_decode_msg(&ntlmssp, d, len, &flags);
+ if(r != 0)
+ {
+ ha_messagex(rq, LOG_WARNING, "decoding NTLMSSP message failed (error %d)", r);
+ rq->resp_code = HA_SERVER_BADREQ;
+ RETURN(HA_FALSE);
+ }
+
+
+ switch(ntlmssp.msg_type)
+ {
+
+ /* An initial NTLM request? */
+ case 1:
+ {
+ /* Win9x doesn't seem to send a domain or host */
+ int win9x = !ntlmssp.host[0] && !ntlmssp.domain[0];
+
+ /*
+ * If we already have a connection to the domain controller
+ * then we're in trouble. Basically this is the second
+ * type 1 message we've received over this connection.
+ *
+ * TODO: Eventually what we want to do here is wait for the
+ * other authentication request to complete, or something
+ * like that.
+ */
+ if(conn)
+ {
+ /*
+ * In this case we also add the connection back into the
+ * pending stack so that the correct request will complete
+ * properly when it comes through.
+ */
+ r = putpending(ctx, key, conn);
+ conn = NULL;
+
+ if(r < 0)
+ {
+ RETURN(HA_CRITERROR);
+ }
+ else
+ {
+ ha_messagex(rq, LOG_ERR, "received out of order NTLM request from client");
+ rq->resp_code = HA_SERVER_BADREQ;
+ RETURN(HA_FALSE);
+ }
+ }
+
+
+ /*
+ * Check how many connections we have to the domain controller
+ * and if too many then cut off here.
+ */
+ if(ctx->pending_max != -1)
+ {
+ ha_lock(NULL);
+
+ if(hsh_count(ctx->pending) >= ctx->pending_max)
+ hsh_bump(ctx->pending);
+
+ ha_unlock(NULL);
+ }
+
+
+ /*
+ * Open a connection to to the domain controller. I don't think
+ * we can cache these connections or use them again as opening
+ * a connection here results in an nonce being generated.
+ */
+ conn = makeconnection(rq, ctx);
+
+ if(!conn)
+ RETURN(HA_FAILED);
+
+ /* Save away any flags given us by ntlm_decode_msg */
+ conn->flags = flags;
+
+ /* Start building the header */
+ ha_bufcpy(rq->buf, HA_PREFIX_NTLM);
+
+ if(win9x)
+ {
+ struct ntlm_msg2_win9x msg_win9x;
+ ntlmssp_encode_msg2_win9x(conn->nonce, &msg_win9x, (char*)ctx->domain, flags);
+ ha_bufjoin(rq->buf);
+ ha_bufenc64(rq->buf, (unsigned char*)&msg_win9x, sizeof(msg_win9x));
+ }
+ else
+ {
+ struct ntlm_msg2 msg;
+ ntlmssp_encode_msg2(conn->nonce, &msg);
+ ha_bufjoin(rq->buf);
+ ha_bufenc64(rq->buf, (unsigned char*)&msg, sizeof(msg));
+ }
+
+ if(CHECK_RBUF(rq))
+ RETURN(HA_CRITERROR);
+
+
+ /* Cache this connection in our pending set ... */
+ r = putpending(ctx, key, conn);
+
+ /*
+ * By marking this as null, the cleanup code
+ * won't free the connection since it's been
+ * cached above.
+ */
+ conn = NULL;
+
+ if(r < 0)
+ {
+ RETURN(HA_CRITERROR);
+ }
+ else
+ {
+ ha_messagex(rq, LOG_DEBUG, "sending ntlm challenge");
+ ha_addheader(rq, "WWW-Authenticate", ha_bufdata(rq->buf));
+ rq->resp_code = HA_SERVER_DECLINE;
+ RETURN(HA_FALSE);
+ }
+ }
+
+ /* A response to a challenge */
+ case 3:
+ {
+ /*
+ * We need to have a connection at this point or this whole thing
+ * has come in in the wrong order. Actually it's a client error
+ * for stuff to come in wrong. But since some web servers also
+ * kill keep-alives and stuff, we forgive and just ask the client
+ * for the authentication info again.
+ */
+ if(!conn || !conn->handle)
+ {
+ ha_messagex(rq, LOG_WARNING, "received out of order NTLM response from client");
+ rq->resp_code = HA_SERVER_BADREQ;
+ RETURN(HA_FALSE);
+ }
+
+ if(!ntlmssp.user)
+ {
+ ha_messagex(rq, LOG_WARNING, "received NTLM response without user name");
+ rq->resp_code = HA_SERVER_BADREQ;
+ RETURN(HA_FALSE);
+ }
+
+ /* We have to lock while going into smblib */
+ ha_lock(&g_smblib_mutex);
+
+ /* Now authenticate them against the DC */
+fprintf(stderr, "AUTHENTICATING: %s / %s / %s / %s\n", (const char*)ntlmssp.user, (const char*)ntlmssp.nt,
+ (char*)ntlmssp.domain , (char*)ctx->domain);
+ r = ntlmssp_auth(conn->handle, (const char*)ntlmssp.user, (const char*)ntlmssp.nt, 1);
+
+ ha_unlock(&g_smblib_mutex);
+
+ /* The connection gets disconnected below */
+
+ if(r == NTV_LOGON_ERROR)
+ {
+ /*
+ * Note that we don't set a code here. This causes our
+ * caller to put in all the proper headers for us.
+ */
+ ha_messagex(rq, LOG_WARNING, "failed NTLM logon for user '%s'", ntlmssp.user);
+ RETURN(HA_FALSE);
+ }
+
+ /* A successful login ends here */
+ else
+ {
+ int r;
+ rq->resp_detail = (const char*)ntlmssp.user;
+ ha_messagex(rq, LOG_NOTICE, "validated ntlm user against server", ntlmssp.user);
+
+ ha_lock(NULL);
+
+ /* We put this connection into the successful connections */
+ r = hsh_set(ctx->established, key, NTLM_ESTABLISHED);
+
+ ha_unlock(NULL);
+
+ if(!r)
+ {
+ ha_messagex(NULL, LOG_CRIT, "out of memory");
+ RETURN(HA_CRITERROR);
+ }
+
+ RETURN(HA_OK);
+ }
+ }
+ break;
+
+ default:
+ ha_messagex(rq, LOG_WARNING, "received invalid NTLM message (type %d)", ntlmssp.msg_type);
+ rq->resp_code = HA_SERVER_BADREQ;
+ RETURN(HA_FALSE);
+ };
+
+
+finally:
+ if(CHECK_RBUF(rq))
+ ret = HA_CRITERROR;
+
+ if(conn)
+ freeconnection(rq, conn);
+
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------------
+ * Handler Functions
+ */
+
+int ntlm_config(ha_context_t* context, const char* name, const char* value)
+{
+ ntlm_context_t* ctx = (ntlm_context_t*)(context->ctx_data);
+
+ ASSERT(name && value && value[0]);
+
+ if(strcmp(name, "ntlmserver") == 0)
+ {
+ ctx->server = value;
+ return HA_OK;
+ }
+
+ else if(strcmp(name, "ntlmbackup") == 0)
+ {
+ ctx->backup = value;
+ return HA_OK;
+ }
+
+ else if(strcmp(name, "ntlmdomain") == 0)
+ {
+ ctx->domain = value;
+ return HA_OK;
+ }
+
+ else if(strcmp(name, "pendingmax") == 0)
+ {
+ return ha_confint(name, value, 1, 256, &(ctx->pending_max));
+ }
+
+ else if(strcmp(name, "pendingtimeout") == 0)
+ {
+ return ha_confint(name, value, 1, 86400, &(ctx->pending_timeout));
+ }
+
+ return HA_FALSE;
+}
+
+int ntlm_init(ha_context_t* context)
+{
+ /* Per context initialization */
+ if(context)
+ {
+ ntlm_context_t* ctx = (ntlm_context_t*)(context->ctx_data);
+ hsh_table_calls_t htc;
+
+ ASSERT(ctx);
+
+ /* Make sure there are some types of authentication we can do */
+ if(!(context->allowed_types & (HA_TYPE_BASIC | HA_TYPE_NTLM)))
+ {
+ ha_messagex(NULL, LOG_ERR, "NTLM module configured, but does not implement any "
+ "configured authentication type.");
+ return HA_FAILED;
+ }
+
+ /* Check for mandatory configuration */
+ if(!(ctx->server) || !(ctx->domain))
+ {
+ ha_messagex(NULL, LOG_ERR, "NTLM configuration incomplete. "
+ "Must have NTLMServer and NTLMDomain configured.");
+ return HA_FAILED;
+ }
+
+ ASSERT(!ctx->pending);
+ ASSERT(!ctx->established);
+
+ /* Initialize our tables */
+ if(!(ctx->pending = hsh_create(NTLM_HASH_KEY_LEN)) ||
+ !(ctx->established = hsh_create(NTLM_HASH_KEY_LEN)))
+ {
+ ha_messagex(NULL, LOG_CRIT, "out of memory");
+ return HA_CRITERROR;
+ }
+
+ htc.f_freeval = free_hash_object;
+ htc.arg = NULL;
+ hsh_set_table_calls(ctx->pending, &htc);
+
+ ha_messagex(NULL, LOG_INFO, "initialized ntlm handler");
+ }
+
+ /* Global Initialization */
+ else
+ {
+ /* Create the smblib mutex */
+ if(pthread_mutexattr_init(&g_smblib_mutexattr) != 0 ||
+ pthread_mutexattr_settype(&g_smblib_mutexattr, HA_MUTEX_TYPE) ||
+ pthread_mutex_init(&g_smblib_mutex, &g_smblib_mutexattr) != 0)
+ {
+ ha_messagex(NULL, LOG_CRIT, "threading problem. can't create mutex");
+ return HA_CRITERROR;
+ }
+ }
+
+ return HA_OK;
+}
+
+void ntlm_destroy(ha_context_t* context)
+{
+ /* Per context destroy */
+ if(context)
+ {
+ /* Note: We don't need to be thread safe here anymore */
+ ntlm_context_t* ctx = (ntlm_context_t*)(context->ctx_data);
+
+ if(ctx->pending)
+ hsh_free(ctx->pending);
+
+ if(ctx->established)
+ hsh_free(ctx->established);
+
+ ha_messagex(NULL, LOG_INFO, "uninitialized handler");
+ }
+
+ /* Global Destroy */
+ else
+ {
+ /* Close the mutex */
+ pthread_mutex_destroy(&g_smblib_mutex);
+ pthread_mutexattr_destroy(&g_smblib_mutexattr);
+ }
+}
+
+int ntlm_process(ha_request_t* rq)
+{
+ ntlm_context_t* ctx = (ntlm_context_t*)(rq->context->ctx_data);
+ void* ntlm_connection_t = NULL;
+ unsigned char key[NTLM_HASH_KEY_LEN];
+ const char* header = NULL;
+ time_t t = time(NULL);
+ int ret = 0, r;
+
+ ASSERT(rq);
+ ASSERT(rq->req_args[AUTH_ARG_CONN]);
+
+ rq->resp_code = -1;
+
+ /* Hash the unique key */
+ md5_string(key, rq->req_args[AUTH_ARG_CONN]);
+
+
+ ha_lock(NULL);
+
+ /*
+ * Purge out stale connection stuff. This includes
+ * authenticated connections which have expired as
+ * well as half open connections which expire.
+ */
+ r = hsh_purge(ctx->pending, t - ctx->pending_timeout);
+ r += hsh_purge(ctx->established, t - rq->context->cache_timeout);
+
+ ha_unlock(NULL);
+
+ if(r > 0)
+ ha_messagex(rq, LOG_DEBUG, "purged info from cache: %d", r);
+
+ /* Look for a NTLM header */
+ if(rq->context->allowed_types & HA_TYPE_NTLM)
+ {
+ header = ha_getheader(rq, "Authorization", HA_PREFIX_NTLM);
+ if(header)
+ {
+ /* Trim off for decoding */
+ header = trim_start(header);
+
+ ha_messagex(rq, LOG_DEBUG, "processing ntlm auth header");
+ ret = ntlm_auth_ntlm(rq, ctx, key, header);
+ if(ret < 0)
+ return ret;
+ }
+ }
+
+ /* If basic is enabled, and no NTLM */
+ if(!header && rq->context->allowed_types & HA_TYPE_BASIC)
+ {
+ /* Look for a Basic header */
+ header = ha_getheader(rq, "Authorization", HA_PREFIX_BASIC);
+ if(header)
+ {
+ /* Trim off for decoding */
+ header = trim_start(header);
+
+ ha_messagex(rq, LOG_DEBUG, "processing basic auth header");
+ ret = ntlm_auth_basic(rq, ctx, key, header);
+ if(ret < 0)
+ return ret;
+ }
+ }
+
+ /* The authorization header was not found */
+ else
+ {
+ ha_lock(NULL);
+
+ /*
+ * NTLM trusts a connection after it's been authenticated
+ * so just pass success for those. Note that we do this
+ * in the absence of a authorization header so that we
+ * allow connections to be re-authenticated.
+ */
+
+ if(hsh_get(ctx->established, key) == NTLM_ESTABLISHED)
+ {
+ hsh_touch(ctx->established, key);
+ rq->resp_code = HA_SERVER_OK;
+ }
+
+ ha_unlock(NULL);
+
+ if(rq->resp_code == HA_SERVER_OK)
+ ha_messagex(rq, LOG_NOTICE, "validated user against connection cache");
+
+ /* TODO: We need to be able to retrieve the user here somehow */
+ }
+
+
+ /* If nobody's set any other response then... */
+ if(rq->resp_code == -1)
+ {
+ /* If authentication failed tell the browser about it */
+ rq->resp_code = HA_SERVER_DECLINE;
+
+ if(rq->context->allowed_types & HA_TYPE_NTLM)
+ {
+ ha_addheader(rq, "WWW-Authenticate", HA_PREFIX_NTLM);
+ ha_messagex(rq, LOG_DEBUG, "sent ntlm auth request");
+ }
+
+ if(rq->context->allowed_types & HA_TYPE_BASIC)
+ {
+ ha_bufmcat(rq->buf, HA_PREFIX_BASIC, "realm=\"", rq->context->realm, "\"", NULL);
+
+ if(CHECK_RBUF(rq))
+ return HA_CRITERROR;
+
+ ha_addheader(rq, "WWW-Authenticate", ha_bufdata(rq->buf));
+ ha_messagex(rq, LOG_DEBUG, "sent basic auth request");
+ }
+ }
+
+ return ret;
+}
+
+
+
+/* -------------------------------------------------------------------------------
+ * Handler Definition
+ */
+
+ha_handler_t ntlm_handler =
+{
+ "NTLM", /* The type */
+ ntlm_init, /* Initialization function */
+ ntlm_destroy, /* Uninitialization routine */
+ ntlm_config, /* Config routine */
+ ntlm_process, /* Processing routine */
+ &ntlm_defaults, /* Default settings */
+ sizeof(ntlm_context_t)
+};
+