summaryrefslogtreecommitdiff
path: root/daemon
diff options
context:
space:
mode:
Diffstat (limited to 'daemon')
-rw-r--r--daemon/ntlm.c825
-rw-r--r--daemon/ntlmssp.c444
-rw-r--r--daemon/ntlmssp.h138
3 files changed, 1407 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)
+};
+
diff --git a/daemon/ntlmssp.c b/daemon/ntlmssp.c
new file mode 100644
index 0000000..ecc5df9
--- /dev/null
+++ b/daemon/ntlmssp.c
@@ -0,0 +1,444 @@
+/*
+ * $Id: ntlmssp.c,v 1.1 2004/04/21 17:37:06 nate Exp $
+ *
+ */
+
+#include <sys/socket.h>
+#include <sys/vfs.h>
+
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "ntlmssp.h"
+#include "smbval/smblib-priv.h"
+
+/* -----------------------------------------------------------------------------
+ * What new hell is this? From mod_ntlm2
+ */
+
+#include "smbval/byteorder.h"
+#include "smbval/std-defines.h"
+#include "smbval/std-includes.h"
+#include "smbval/smblib-common.h"
+#include "smbval/smblib-priv.h"
+#include "smbval/rfcnb-common.h"
+#include "smbval/rfcnb-error.h"
+#include "smbval/rfcnb-priv.h"
+#include "smbval/rfcnb-util.h"
+#include "smbval/rfcnb-io.h"
+#include "smbval/rfcnb.h"
+
+#include "smbval/rfcnb-io.inc.c"
+#include "smbval/rfcnb-util.inc.c"
+#include "smbval/session.inc.c"
+#include "smbval/smbdes.inc.c"
+#include "smbval/smbencrypt.inc.c"
+#include "smbval/smblib-util.inc.c"
+#include "smbval/smblib.inc.c"
+
+/* -------------------------------------------------------------------------- */
+
+#define little_endian_word(x) x[0] + (((unsigned)x[1]) << 8)
+/* fhz 02-02-09: typecasting is needed for a generic use */
+#define set_little_endian_word(x,y) (*((char *)x))=(y&0xff);*(((char*)x)+1)=((y>>8)&0xff)
+
+static int
+ntlm_msg_type(unsigned char *raw_msg, unsigned msglen)
+{
+ struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg;
+
+ if (msglen < 9)
+ return -1;
+ if (strncmp((char*)msg->protocol, "NTLMSSP", 8))
+ return -1;
+ return msg->type;
+}
+
+static int
+ntlm_extract_mem(unsigned char *dst,
+ unsigned char *src, unsigned srclen,
+ unsigned char *off, unsigned char *len,
+ unsigned max)
+{
+ unsigned o = little_endian_word(off);
+ unsigned l = little_endian_word(len);
+ if (l > max)
+ return -1;
+ if (o >= srclen)
+ return -1;
+ if (o + l > srclen)
+ return -1;
+ src += o;
+ while (l-- > 0)
+ *dst++ = *src++;
+ return 0;
+}
+
+static int
+ntlm_extract_string(unsigned char *dst,
+ unsigned char *src, unsigned srclen,
+ unsigned char *off, unsigned char *len,
+ unsigned max)
+{
+ unsigned o = little_endian_word(off);
+ unsigned l = little_endian_word(len);
+ if (l > max)
+ return -1;
+ if (o >= srclen)
+ return -1;
+ if (o + l > srclen)
+ return -1;
+ src += o;
+ while (l-- > 0) {
+ /* +csz 2003/02/20 - En algunos casos vienen \0 entremedio */
+ if ( *src != '\0' ) {
+ *dst = *src;
+ dst++;
+ }
+ src++;
+ }
+ *dst = 0;
+ return 0;
+}
+
+static int
+ntlm_put_in_unicode(unsigned char *dst,
+ unsigned char *src, unsigned srclen, unsigned max)
+{
+ unsigned l = srclen*2;
+ if (l > max)
+ l=max; /* fhz: bad very bad */
+ while (l > 0) {
+ /* ASCII to unicode*/
+ *dst++ = *src++;
+ *dst++=0;
+ l -=2;
+ }
+ return 0;
+
+
+
+}
+
+static int
+ntlm_extract_unicode(unsigned char *dst,
+ unsigned char *src, unsigned srclen,
+ unsigned char *off, unsigned char *len,
+ unsigned max)
+{
+ unsigned o = little_endian_word(off);
+ unsigned l = little_endian_word(len) / 2; /* Unicode! */
+ if (l > max)
+ return -1;
+ if (o >= srclen)
+ return -1;
+ if (o + l > srclen)
+ return -1;
+ src += o;
+ while (l > 0) {
+ /* Unicode to ASCII */
+ *dst++ = *src;
+ src += 2;
+ l -= 2;
+ }
+ *dst = 0;
+ return 0;
+}
+
+static int
+ntlm_msg1_getntlmssp_flags(unsigned char *raw_msg,
+ unsigned char *ntlmssp_flags)
+{
+ struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg;
+ *ntlmssp_flags=little_endian_word(msg->flags);
+ return 0;
+}
+
+static int
+ntlm_msg1_gethostname(unsigned char *raw_msg,
+ unsigned msglen, unsigned char *hostname)
+{
+ struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg;
+ if (ntlm_extract_string(hostname, (unsigned char*) msg, msglen,
+ msg->host_off, msg->host_len, MAX_HOSTLEN))
+ return 1;
+ return 0;
+}
+
+static int
+ntlm_msg1_getdomainname(unsigned char *raw_msg,
+ unsigned msglen, unsigned char *domainname)
+{
+ struct ntlm_msg1 *msg = (struct ntlm_msg1 *) raw_msg;
+ if (ntlm_extract_string(domainname, (unsigned char*) msg,
+ msglen, msg->dom_off, msg->dom_len, MAX_DOMLEN))
+ return 2;
+ return 0;
+}
+
+static int
+ntlm_msg3_getlm(unsigned char *raw_msg, unsigned msglen,
+ unsigned char *lm)
+{
+ struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg;
+ if (ntlm_extract_mem(lm, (unsigned char*) msg, msglen, msg->lm_off,
+ msg->lm_len, RESP_LEN))
+ return 4;
+ return 0;
+}
+
+static int
+ntlm_msg3_getnt(unsigned char *raw_msg, unsigned msglen,
+ unsigned char *nt)
+{
+ struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg;
+ if (ntlm_extract_mem(nt, (unsigned char*) msg, msglen, msg->nt_off,
+ msg->nt_len, RESP_LEN))
+ /* Win9x: we can't extract nt ... so we use lm... */
+ if (ntlm_extract_mem(nt, (unsigned char*) msg, msglen, msg->lm_off,
+ msg->lm_len, RESP_LEN))
+ return 8;
+ return 0;
+}
+
+static int
+ntlm_msg3_getusername(unsigned char *raw_msg,
+ unsigned msglen, unsigned char *username,
+ unsigned ntlmssp_flags)
+{
+ struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg;
+ int c;
+ if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ if (ntlm_extract_unicode(username, (unsigned char*)msg, msglen,
+ msg->user_off, msg->user_len, MAX_USERLEN))
+ return 16;
+ }
+ else { /* ascii */
+ if (ntlm_extract_string(username, (unsigned char*)msg, msglen,
+ msg->user_off, msg->user_len, MAX_USERLEN))
+ return 16;
+ else {
+ /* Win9x client leave username in uppercase...fix it: */
+ while (*username!=(unsigned char)NULL) {
+ c=tolower((int)*username);
+ *username=(unsigned char)c;
+ username++;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+ntlm_msg3_gethostname(unsigned char *raw_msg, unsigned msglen,
+ unsigned char *hostname,unsigned ntlmssp_flags)
+{
+ struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg;
+ if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ if (ntlm_extract_unicode(hostname, (unsigned char*) msg, msglen,
+ msg->host_off, msg->host_len, MAX_HOSTLEN))
+ return 0; /* this one FAILS, but since the value is not used,
+ * we just pretend it was ok. */
+ }
+ else { /* ascii */
+ if (ntlm_extract_string(hostname, (unsigned char*) msg, msglen,
+ msg->host_off, msg->host_len, MAX_HOSTLEN))
+ return 0; /* this one FAILS, but since the value is not used,
+ * we just pretend it was ok. */
+ }
+ return 0;
+}
+
+static int
+ntlm_msg3_getdomainname(unsigned char *raw_msg,
+ unsigned msglen, unsigned char *domainname,
+ unsigned ntlmssp_flags)
+{
+ struct ntlm_msg3 *msg = (struct ntlm_msg3 *) raw_msg;
+ if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ if (ntlm_extract_unicode(domainname, (unsigned char*) msg, msglen,
+ msg->dom_off, msg->dom_len, MAX_DOMLEN))
+ return 64;
+ }
+ else { /* asii */
+ if (ntlm_extract_string(domainname, (unsigned char*) msg, msglen,
+ msg->dom_off, msg->dom_len, MAX_DOMLEN))
+ return 64;
+ }
+ return 0;
+}
+
+int
+ntlmssp_decode_msg(struct ntlmssp_info *info,
+ unsigned char *raw_msg, unsigned msglen, unsigned *ntlmssp_flags)
+{
+ switch (info->msg_type = ntlm_msg_type(raw_msg, msglen)) {
+ case 1:
+ return ntlm_msg1_getntlmssp_flags(raw_msg,(unsigned char*)ntlmssp_flags)
+ + ntlm_msg1_gethostname(raw_msg, msglen, info->host)
+ + ntlm_msg1_getdomainname(raw_msg, msglen, info->domain);
+ case 3:
+ return ntlm_msg3_getlm(raw_msg, msglen, info->lm)
+ + ntlm_msg3_getnt(raw_msg, msglen, info->nt)
+ + ntlm_msg3_getusername(raw_msg, msglen, info->user,*ntlmssp_flags)
+ + ntlm_msg3_gethostname(raw_msg, msglen, info->host,*ntlmssp_flags)
+ + ntlm_msg3_getdomainname(raw_msg, msglen, info->domain,*ntlmssp_flags);
+ }
+ return -1;
+}
+
+int
+ntlmssp_encode_msg2(unsigned char *nonce, struct ntlm_msg2 *msg)
+{
+ memset(msg, 0, sizeof(struct ntlm_msg2));
+ strcpy((char*)msg->protocol, "NTLMSSP");
+ msg->type = 0x02;
+ set_little_endian_word(msg->msg_len, sizeof(struct ntlm_msg2));
+ set_little_endian_word(msg->flags, 0x8201);
+ memcpy(msg->nonce, nonce, sizeof(msg->nonce));
+ return 0;
+}
+
+int
+ntlmssp_encode_msg2_win9x(unsigned char *nonce, struct ntlm_msg2_win9x *msg,char *domainname,unsigned ntlmssp_flags)
+{
+ unsigned int size,len,flags;
+
+ memset(msg, 0, sizeof(struct ntlm_msg2_win9x));
+ strcpy((char*)msg->protocol, "NTLMSSP");
+ msg->type = 0x02;
+ if (ntlmssp_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ /* unicode case */
+
+ len=strlen(domainname);
+ ntlm_put_in_unicode((unsigned char*)msg->dom, (unsigned char*)domainname,
+ len, MAX_DOMLEN);
+ len=len*2;
+ if (len>MAX_DOMLEN)
+ len=MAX_DOMLEN; /* fhz: bad very bad */
+ flags=NTLM_NTLMSSP_NEG_FLAGS | NTLMSSP_NEGOTIATE_UNICODE;
+ } else {
+ /* ascii case */
+ len=strlen(domainname);
+ if (len>MAX_DOMLEN)
+ len=MAX_DOMLEN; /* fhz: bad very bad */
+ strncpy((char*)msg->dom, domainname,len);
+ flags=NTLM_NTLMSSP_NEG_FLAGS;
+ }
+ size=NTLM_MSG2_WIN9X_FIXED_SIZE+len;
+ set_little_endian_word(msg->dom_off, NTLM_MSG2_WIN9X_FIXED_SIZE);
+ set_little_endian_word(msg->dom_len1,len);
+ set_little_endian_word(msg->dom_len2,len);
+ set_little_endian_word(msg->msg_len,size);
+ set_little_endian_word(msg->flags,flags);
+ if (ntlmssp_flags & NTLMSSP_REQUEST_TARGET)
+ set_little_endian_word(msg->zero2, 0x01); /* == set NTLMSSP_TARGET_TYPE_DOMAIN */
+
+ memcpy(msg->nonce, nonce, sizeof(msg->nonce));
+ return size;
+}
+
+
+int
+ntlmssp_validuser(const char *USERNAME, const char *PASSWORD, const char *SERVER,
+ const char *BACKUP, const char *DOMAIN)
+{
+ char *SMB_Prots[] =
+ {"PC NETWORK PROGRAM 1.0",
+ "MICROSOFT NETWORKS 1.03",
+ "MICROSOFT NETWORKS 3.0",
+ "LANMAN1.0",
+ "LM1.2X002",
+ "Samba",
+ "NT LM 0.12",
+ "NT LANMAN 1.0",
+ NULL};
+ SMB_Handle_Type con;
+
+ SMB_Init();
+ con = SMB_Connect_Server(NULL, (char*)SERVER, (char*)DOMAIN);
+ if (con == NULL) { /* Error ... */
+ con = SMB_Connect_Server(NULL, (char*)BACKUP, (char*)DOMAIN);
+ if (con == NULL) {
+ return (NTV_SERVER_ERROR);
+ }
+ }
+ if (SMB_Negotiate(con, SMB_Prots) < 0) { /* An error */
+ SMB_Discon(con, 0);
+ return (NTV_PROTOCOL_ERROR);
+ }
+ /* Test for a server in share level mode do not authenticate against
+ * it */
+ if (con->Security == 0) {
+ SMB_Discon(con, 0);
+ return (NTV_PROTOCOL_ERROR);
+ }
+ if (SMB_Logon_Server(con, (char*)USERNAME, (char*)PASSWORD, 0) < 0) {
+ SMB_Discon(con, 0);
+ return (NTV_LOGON_ERROR);
+ }
+ SMB_Discon(con, 0);
+ return (NTV_NO_ERROR);
+}
+
+void* ntlmssp_connect(const char *SERVER, const char *BACKUP, const char *DOMAIN,
+ char *nonce)
+{
+ char *SMB_Prots[] =
+ {"PC NETWORK PROGRAM 1.0",
+ "MICROSOFT NETWORKS 1.03",
+ "MICROSOFT NETWORKS 3.0",
+ "LANMAN1.0",
+ "LM1.2X002",
+ "Samba",
+ "NT LM 0.12",
+ "NT LANMAN 1.0",
+ NULL};
+ SMB_Handle_Type con;
+
+ SMB_Init();
+ con = SMB_Connect_Server(NULL, (char*)SERVER, (char*)DOMAIN);
+ if (con == NULL) { /* Error ... */
+ con = SMB_Connect_Server(NULL, (char*)BACKUP, (char*)DOMAIN);
+ if (con == NULL) {
+ return (NULL);
+ }
+ }
+ if (SMB_Negotiate(con, SMB_Prots) < 0) { /* An error */
+ SMB_Discon(con, 0);
+ return (NULL);
+ }
+ /* Test for a server in share level mode do not authenticate
+ * against it */
+ if (con->Security == 0) {
+ SMB_Discon(con, 0);
+ return (NULL);
+ }
+ memcpy(nonce, con->Encrypt_Key, 8);
+
+ return con;
+}
+
+int
+ntlmssp_auth(void *handle, const char *USERNAME, const char *PASSWORD, int flag)
+{
+ SMB_Handle_Type con = handle;
+
+ if (SMB_Logon_Server(con, (char*)USERNAME, (char*)PASSWORD, flag) < 0) {
+ return (NTV_LOGON_ERROR);
+ }
+ return NTV_NO_ERROR;
+}
+
+void
+ntlmssp_disconnect(void *handle)
+{
+ SMB_Handle_Type con = handle;
+ SMB_Discon(con, 0);
+}
+
diff --git a/daemon/ntlmssp.h b/daemon/ntlmssp.h
new file mode 100644
index 0000000..2d7f52a
--- /dev/null
+++ b/daemon/ntlmssp.h
@@ -0,0 +1,138 @@
+
+#ifndef __NTLMSSP_H__
+#define __NTLMSSP_H__
+
+#define MAX_HOSTLEN 32
+#define MAX_DOMLEN 32
+#define MAX_USERLEN 32
+#define RESP_LEN 24
+#define NONCE_LEN 8
+
+/* fhz, 01-10-15 : borrowed from samba code */
+/* NTLMSSP negotiation flags */
+#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
+#define NTLMSSP_NEGOTIATE_OEM 0x00000002
+#define NTLMSSP_REQUEST_TARGET 0x00000004
+#define NTLMSSP_NEGOTIATE_SIGN 0x00000010
+#define NTLMSSP_NEGOTIATE_SEAL 0x00000020
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
+#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
+#define NTLMSSP_NEGOTIATE_00001000 0x00001000
+#define NTLMSSP_NEGOTIATE_00002000 0x00002000
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000
+#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000
+#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
+#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000
+#define NTLMSSP_NEGOTIATE_128 0x20000000
+#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000
+
+#define SMBD_NTLMSSP_NEG_FLAGS 0x000082b1
+#define NTLM_NTLMSSP_NEG_FLAGS 0x00008206
+/* 8201 8207 */
+
+#define LEN_NTLMSSP_FLAGS 4
+#define OFFSET_MSG1_NTLMSSP_FLAGS 12
+
+struct ntlm_msg1 {
+ unsigned char protocol[8];
+ unsigned char type; /* 1 */
+ unsigned char zero1[3];
+ unsigned char flags[2];
+ unsigned char zero2[2];
+
+ unsigned char dom_len[4];
+ unsigned char dom_off[4];
+
+ unsigned char host_len[4];
+ unsigned char host_off[4];
+
+#if 0
+ unsigned char data[0];
+#endif
+} __attribute__((packed));
+
+struct ntlm_msg2 {
+ unsigned char protocol[8];
+ unsigned char type; /* 2 */
+ unsigned char zero1[7];
+ unsigned char msg_len[4];
+ unsigned char flags[2];
+ unsigned char zero2[2];
+
+ unsigned char nonce[8];
+ unsigned char zero3[8];
+} __attribute__((packed));
+
+struct ntlm_msg3 {
+ unsigned char protocol[8];
+ unsigned char type; /* 3 */
+ unsigned char zero1[3];
+
+ unsigned char lm_len[4];
+ unsigned char lm_off[4];
+
+ unsigned char nt_len[4];
+ unsigned char nt_off[4];
+
+ unsigned char dom_len[4];
+ unsigned char dom_off[4];
+
+ unsigned char user_len[4];
+ unsigned char user_off[4];
+
+ unsigned char host_len[4];
+ unsigned char host_off[4];
+
+ unsigned char msg_len[4]; /* Win9x: data begins here! */
+
+#if 0
+ unsigned char data[0];
+#endif
+} __attribute__((packed));
+
+struct ntlm_msg2_win9x {
+ unsigned char protocol[8];
+ unsigned char type; /* 2 */
+ unsigned char zero1[3];
+ unsigned char dom_len1[2];
+ unsigned char dom_len2[2];
+ unsigned char dom_off[4];
+ unsigned char flags[2];
+ unsigned char zero2[2];
+
+ unsigned char nonce[8];
+ unsigned char zero3[8];
+ unsigned char zero4[4];
+ unsigned char msg_len[4];
+ unsigned char dom[MAX_DOMLEN];
+} __attribute__((packed));
+
+/* size without dom[] : */
+#define NTLM_MSG2_WIN9X_FIXED_SIZE (sizeof(struct ntlm_msg2_win9x)-MAX_DOMLEN)
+
+
+typedef struct ntlmssp_info {
+ int msg_type;
+ unsigned char user[MAX_USERLEN + 1];
+ unsigned char host[MAX_HOSTLEN + 1];
+ unsigned char domain[MAX_DOMLEN + 1];
+ unsigned char lm[RESP_LEN];
+ unsigned char nt[RESP_LEN];
+} ntlmssp_info_rec;
+
+int ntlmssp_decode_msg(struct ntlmssp_info *info, unsigned char *raw_msg, unsigned msglen, unsigned *ntlmssp_flags);
+int ntlmssp_encode_msg2(unsigned char *nonce, struct ntlm_msg2 *msg);
+int ntlmssp_encode_msg2_win9x(unsigned char *nonce, struct ntlm_msg2_win9x *msg,char *domainname,unsigned ntlmssp_flags);
+
+#define NTV_NO_ERROR 0
+#define NTV_SERVER_ERROR 1
+#define NTV_PROTOCOL_ERROR 2
+#define NTV_LOGON_ERROR 3
+
+int ntlmssp_validuser(const char* username, const char* password, const char* server, const char* backup, const char* domain);
+void* ntlmssp_connect(const char* server, const char* backup, const char* domain, char* nonce);
+int ntlmssp_auth(void* handle, const char* user, const char* password, int flag);
+void ntlmssp_disconnect(void* handle);
+
+#endif /* __NTLMSSP_H__ */