From bb59442e8131ab45ab36900c05c02757eca05feb Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 25 Aug 2004 00:48:14 +0000 Subject: Removed NTLM support from the main branch. --- daemon/ntlm.c | 824 ---------------------------------------------------------- 1 file changed, 824 deletions(-) delete mode 100644 daemon/ntlm.c (limited to 'daemon/ntlm.c') 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 - * - */ - -#include "usuals.h" -#include "httpauthd.h" -#include "hash.h" -#include "defaults.h" -#include "md5.h" -#include "basic.h" -#include "stringx.h" - -#include - -/* 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) -}; - -- cgit v1.2.3