summaryrefslogtreecommitdiff
path: root/daemon/ntlm.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/ntlm.c')
-rw-r--r--daemon/ntlm.c824
1 files changed, 0 insertions, 824 deletions
diff --git a/daemon/ntlm.c b/daemon/ntlm.c
deleted file mode 100644
index 56190cf..0000000
--- a/daemon/ntlm.c
+++ /dev/null
@@ -1,824 +0,0 @@
-/*
- * 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;
- 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, 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, 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);
-
- /*
- * TODO: Our callers need to be able to keep alive
- * connections that have authentication going on.
- */
-
- /* 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 */
- r = ntlmssp_auth(conn->handle, ntlmssp.user, ntlmssp.nt, 1,
- ntlmssp.domain[0] ? (char*)ntlmssp.domain : (char*)ctx->domain);
-
- 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 = 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, 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)
-};
-