diff options
-rw-r--r-- | daemon/ntlm.c | 825 | ||||
-rw-r--r-- | daemon/ntlmssp.c | 444 | ||||
-rw-r--r-- | daemon/ntlmssp.h | 138 |
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__ */ |