changeset 2377:8f5be0be3199 HEAD

NTLM authentication. Patch by Andrey Panin
author Timo Sirainen <tss@iki.fi>
date Wed, 28 Jul 2004 18:39:29 +0300
parents 05b8b6963b02
children 00a4d69a1d1c
files AUTHORS configure.in src/Makefile.am src/auth/Makefile.am src/auth/mech-ntlm.c src/auth/mech.c src/auth/passdb.c src/auth/passdb.h src/auth/password-scheme-ntlm.c src/auth/password-scheme.c src/auth/password-scheme.h src/lib-ntlm/.cvsignore src/lib-ntlm/Makefile.am src/lib-ntlm/hmac-md5.c src/lib-ntlm/hmac-md5.h src/lib-ntlm/ntlm-byteorder.h src/lib-ntlm/ntlm-des.c src/lib-ntlm/ntlm-des.h src/lib-ntlm/ntlm-encrypt.c src/lib-ntlm/ntlm-encrypt.h src/lib-ntlm/ntlm-flags.h src/lib-ntlm/ntlm-message.c src/lib-ntlm/ntlm-message.h src/lib-ntlm/ntlm-types.h src/lib-ntlm/ntlm.h src/lib/Makefile.am src/lib/md4.c src/lib/md4.h
diffstat 28 files changed, 2065 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Wed Jul 28 18:21:28 2004 +0300
+++ b/AUTHORS	Wed Jul 28 18:39:29 2004 +0300
@@ -6,7 +6,8 @@
 
 Matthew Reimer <mreimer@vpop.net> (src/auth/*-mysql.c)
 
-Andrey Panin <pazke@donpac.ru> (src/auth/mech-apop.c, src/auth/mech-login.c)
+Andrey Panin <pazke@donpac.ru> (src/auth/mech-apop.c, src/auth/mech-login.c,
+  src/lib-ntlm/*, src/auth/mech-ntlm.c)
 
 Joshua Goodall <joshua@roughtrade.net> (src/auth/password-scheme-cram-md5.c,
   src/util/dovecotpw.c)
--- a/configure.in	Wed Jul 28 18:21:28 2004 +0300
+++ b/configure.in	Wed Jul 28 18:39:29 2004 +0300
@@ -1293,6 +1293,7 @@
 src/lib-imap/Makefile
 src/lib-index/Makefile
 src/lib-mail/Makefile
+src/lib-ntlm/Makefile
 src/lib-settings/Makefile
 src/lib-storage/Makefile
 src/lib-storage/index/Makefile
--- a/src/Makefile.am	Wed Jul 28 18:21:28 2004 +0300
+++ b/src/Makefile.am	Wed Jul 28 18:39:29 2004 +0300
@@ -2,4 +2,4 @@
 POP3D = pop3-login pop3
 endif
 
-SUBDIRS = lib lib-settings lib-charset lib-mail lib-imap lib-index lib-storage lib-auth auth master login-common imap-login imap $(POP3D) util
+SUBDIRS = lib lib-ntlm lib-settings lib-charset lib-mail lib-imap lib-index lib-storage lib-auth auth master login-common imap-login imap $(POP3D) util
--- a/src/auth/Makefile.am	Wed Jul 28 18:21:28 2004 +0300
+++ b/src/auth/Makefile.am	Wed Jul 28 18:39:29 2004 +0300
@@ -5,12 +5,14 @@
 INCLUDES = \
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-settings \
+	-I$(top_srcdir)/src/lib-ntlm \
 	-DAUTH_MODULE_DIR=\""$(moduledir)/auth"\" \
 	-DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
 	$(AUTH_CFLAGS)
 
 dovecot_auth_LDADD = \
 	../lib-settings/libsettings.a \
+	../lib-ntlm/libntlm.a \
 	../lib/liblib.a \
 	$(AUTH_LIBS) \
 	$(RAND_LIBS) \
@@ -32,6 +34,7 @@
 	mech-login.c \
 	mech-cram-md5.c \
 	mech-digest-md5.c \
+	mech-ntlm.c \
 	mech-apop.c \
 	mycrypt.c \
 	passdb.c \
@@ -48,6 +51,7 @@
 	password-scheme.c \
 	password-scheme-md5crypt.c \
 	password-scheme-cram-md5.c \
+	password-scheme-ntlm.c \
 	userdb.c \
 	userdb-ldap.c \
 	userdb-passwd.c \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/mech-ntlm.c	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,218 @@
+/*
+ * NTLM and NTLMv2 authentication mechanism.
+ *
+ * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published 
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "common.h"
+#include "mech.h"
+#include "passdb.h"
+#include "str.h"
+#include "buffer.h"
+#include "hex-binary.h"
+#include "safe-memset.h"
+
+#include "ntlm.h"
+
+struct ntlm_auth_request {
+	struct auth_request auth_request;
+
+	pool_t pool;
+
+	/* requested: */
+	const unsigned char *challenge;
+
+	/* received: */
+	struct ntlmssp_response *response;
+};
+
+static void
+ntlm_credentials_callback(const char *credentials,
+			  struct auth_request *auth_request)
+{
+	struct ntlm_auth_request *auth =
+		(struct ntlm_auth_request *)auth_request;
+	const unsigned char *client_response;
+	unsigned char hash[NTLMSSP_HASH_SIZE];
+	unsigned int response_length;
+	buffer_t *hash_buffer;
+	int ret;
+
+	if (credentials == NULL) {
+		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		return;
+	}
+
+	hash_buffer = buffer_create_data(auth_request->pool,
+					 hash, sizeof(hash));
+	hex_to_binary(credentials, hash_buffer);
+
+	response_length = ntlmssp_buffer_length(auth->response, ntlm_response);
+	client_response = ntlmssp_buffer_data(auth->response, ntlm_response);
+
+	if (response_length > NTLMSSP_RESPONSE_SIZE) {
+		unsigned char ntlm_v2_response[NTLMSSP_V2_RESPONSE_SIZE];
+		const unsigned char *blob =
+			client_response + NTLMSSP_V2_RESPONSE_SIZE;
+
+		/*
+		 * Authentication target == NULL because we are acting
+		 * as a standalone server, not as NT domain member.
+		 */
+		ntlmssp_v2_response(auth_request->user, NULL,
+				    hash, auth->challenge, blob,
+				    response_length - NTLMSSP_V2_RESPONSE_SIZE,
+				    ntlm_v2_response);
+
+		ret = memcmp(ntlm_v2_response, client_response,
+			     NTLMSSP_V2_RESPONSE_SIZE) == 0;
+	} else {
+		unsigned char ntlm_response[NTLMSSP_RESPONSE_SIZE];
+
+		ntlmssp_v1_response(hash, auth->challenge, ntlm_response);
+
+		ret = memcmp(ntlm_response, client_response,
+			     NTLMSSP_RESPONSE_SIZE) == 0;
+	}
+
+	mech_auth_finish(auth_request, NULL, 0, ret);
+}
+
+static int
+mech_ntlm_auth_continue(struct auth_request *auth_request,
+			const unsigned char *data, size_t data_size,
+			mech_callback_t *callback)
+{
+	struct ntlm_auth_request *auth =
+		(struct ntlm_auth_request *)auth_request;
+	struct auth_client_request_reply reply;
+	const char *error;
+
+	auth_request->callback = callback;
+
+	if (!auth->challenge) {
+		const struct ntlmssp_request *request =
+			(struct ntlmssp_request *)data;
+		const struct ntlmssp_challenge *message;
+		size_t message_size;
+
+		if (!ntlmssp_check_request(request, data_size, &error)) {
+			if (verbose) {
+				i_info("ntlm(%s): invalid NTLM request, %s",
+				       get_log_prefix(auth_request),
+				       error);
+			}
+			mech_auth_finish(auth_request, NULL, 0, FALSE);
+			return TRUE;
+		}
+
+		message = ntlmssp_create_challenge(auth->pool, request,
+						   &message_size);
+		auth->challenge = message->challenge;
+
+		mech_init_auth_client_reply(&reply);
+		reply.id = auth_request->id;
+		reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+		reply.reply_idx = 0;
+		reply.data_size = message_size;
+		callback(&reply, message, auth_request->conn);
+	} else {
+		const struct ntlmssp_response *response =
+			(struct ntlmssp_response *)data;
+		char *username;
+
+		if (!ntlmssp_check_response(response, data_size, &error)) {
+			if (verbose) {
+				i_info("ntlm(%s): invalid NTLM response, %s",
+				       get_log_prefix(auth_request),
+				       error);
+			}
+			mech_auth_finish(auth_request, NULL, 0, FALSE);
+			return TRUE;
+		}
+
+		auth->response = p_malloc(auth->pool, data_size);
+		memcpy(auth->response, response, data_size);
+
+		username = p_strdup(auth_request->pool,
+				    ntlmssp_t_str(auth->response, user));
+
+		if (!mech_is_valid_username(username)) {
+			if (verbose) {
+				i_info("ntlm(%s): invalid username",
+				       get_log_prefix(auth_request));
+			}
+			mech_auth_finish(auth_request, NULL, 0, FALSE);
+			return TRUE;
+		}
+
+		auth_request->user = username;
+
+		passdb->lookup_credentials(auth_request,
+					   PASSDB_CREDENTIALS_NTLM,
+					   ntlm_credentials_callback);
+	}
+
+	return TRUE;
+}
+
+static int
+mech_ntlm_auth_initial(struct auth_request *auth_request,
+		       struct auth_client_request_new *request,
+		       const unsigned char *data __attr_unused__,
+		       mech_callback_t *callback)
+{
+	struct auth_client_request_reply reply;
+
+	mech_init_auth_client_reply(&reply);
+	reply.id = request->id;
+	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+	reply.reply_idx = 0;
+	reply.data_size = 0;
+	callback(&reply, "", auth_request->conn);
+
+	return TRUE;
+}
+
+static void
+mech_ntlm_auth_free(struct auth_request *auth_request)
+{
+	pool_unref(auth_request->pool);
+}
+
+static struct auth_request *mech_ntlm_auth_new(void)
+{
+	struct ntlm_auth_request *auth;
+	pool_t pool;
+
+	pool = pool_alloconly_create("ntlm_auth_request", 256);
+	auth = p_new(pool, struct ntlm_auth_request, 1);
+	auth->pool = pool;
+
+	auth->auth_request.refcount = 1;
+	auth->auth_request.pool = pool;
+	auth->auth_request.auth_initial = mech_ntlm_auth_initial;
+	auth->auth_request.auth_continue = mech_ntlm_auth_continue;
+	auth->auth_request.auth_free = mech_ntlm_auth_free;
+
+	return &auth->auth_request;
+}
+
+const struct mech_module mech_ntlm = {
+	"NTLM",
+
+	MEMBER(plaintext) FALSE,
+	MEMBER(advertise) TRUE,
+
+	MEMBER(passdb_need_plain) FALSE,
+	MEMBER(passdb_need_credentials) TRUE,
+
+	mech_ntlm_auth_new,
+};
--- a/src/auth/mech.c	Wed Jul 28 18:21:28 2004 +0300
+++ b/src/auth/mech.c	Wed Jul 28 18:39:29 2004 +0300
@@ -388,6 +388,7 @@
 extern struct mech_module mech_apop;
 extern struct mech_module mech_cram_md5;
 extern struct mech_module mech_digest_md5;
+extern struct mech_module mech_ntlm;
 extern struct mech_module mech_anonymous;
 
 void mech_init(void)
@@ -421,6 +422,8 @@
 			mech_register_module(&mech_cram_md5);
 		else if (strcasecmp(*mechanisms, "DIGEST-MD5") == 0)
 			mech_register_module(&mech_digest_md5);
+		else if (strcasecmp(*mechanisms, "NTLM") == 0)
+			mech_register_module(&mech_ntlm);
 		else if (strcasecmp(*mechanisms, "ANONYMOUS") == 0) {
 			if (anonymous_username == NULL) {
 				i_fatal("ANONYMOUS listed in mechanisms, "
@@ -481,5 +484,6 @@
 	mech_unregister_module(&mech_apop);
 	mech_unregister_module(&mech_cram_md5);
 	mech_unregister_module(&mech_digest_md5);
+	mech_unregister_module(&mech_ntlm);
 	mech_unregister_module(&mech_anonymous);
 }
--- a/src/auth/passdb.c	Wed Jul 28 18:21:28 2004 +0300
+++ b/src/auth/passdb.c	Wed Jul 28 18:39:29 2004 +0300
@@ -28,6 +28,8 @@
 		return "HMAC-MD5";
 	case PASSDB_CREDENTIALS_DIGEST_MD5:
 		return "DIGEST-MD5";
+	case PASSDB_CREDENTIALS_NTLM:
+		return "NTLM";
 	}
 
 	return "??";
--- a/src/auth/passdb.h	Wed Jul 28 18:21:28 2004 +0300
+++ b/src/auth/passdb.h	Wed Jul 28 18:39:29 2004 +0300
@@ -12,7 +12,8 @@
 	PASSDB_CREDENTIALS_PLAINTEXT,
 	PASSDB_CREDENTIALS_CRYPT,
 	PASSDB_CREDENTIALS_CRAM_MD5,
-	PASSDB_CREDENTIALS_DIGEST_MD5
+	PASSDB_CREDENTIALS_DIGEST_MD5,
+	PASSDB_CREDENTIALS_NTLM
 };
 
 enum passdb_result {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/password-scheme-ntlm.c	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,15 @@
+
+#include "lib.h"
+#include "hex-binary.h"
+#include "password-scheme.h"
+
+#include "ntlm.h"
+
+const char *password_generate_ntlm(const char *plaintext)
+{
+	unsigned char hash[16];
+
+	ntlm_v1_hash(plaintext, hash);
+
+	return binary_to_hex_ucase(hash, sizeof(hash));
+}
--- a/src/auth/password-scheme.c	Wed Jul 28 18:21:28 2004 +0300
+++ b/src/auth/password-scheme.c	Wed Jul 28 18:39:29 2004 +0300
@@ -400,6 +400,18 @@
 	return memcmp(md5_digest, data, 16) == 0;
 }
 
+static int ntlm_verify(const char *plaintext, const char *password,
+		       const char *user __attr_unused__)
+{
+	return strcmp(password, password_generate_ntlm(plaintext)) == 0;
+}
+
+static const char *ntlm_generate(const char *plaintext,
+				 const char *user __attr_unused__)
+{
+	return password_generate_ntlm(plaintext);
+}
+
 static const struct password_scheme default_schemes[] = {
 	{ "CRYPT", crypt_verify, crypt_generate },
 	{ "MD5", md5_verify, md5_generate },
@@ -413,6 +425,7 @@
 	{ "DIGEST-MD5", digest_md5_verify, digest_md5_generate },
 	{ "PLAIN-MD5", plain_md5_verify, plain_md5_generate },
 	{ "LDAP-MD5", ldap_md5_verify, ldap_md5_generate },
+	{ "NTLM", ntlm_verify, ntlm_generate },
 	{ NULL, NULL, NULL }
 };
 
--- a/src/auth/password-scheme.h	Wed Jul 28 18:21:28 2004 +0300
+++ b/src/auth/password-scheme.h	Wed Jul 28 18:39:29 2004 +0300
@@ -30,5 +30,6 @@
 /* INTERNAL: */
 const char *password_generate_md5_crypt(const char *pw, const char *salt);
 const char *password_generate_cram_md5(const char *pw);
+const char *password_generate_ntlm(const char *pw);
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/.cvsignore	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,8 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/Makefile.am	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,20 @@
+noinst_LIBRARIES = libntlm.a
+
+INCLUDES = \
+	-I$(top_srcdir)/src/lib
+
+libntlm_a_SOURCES = \
+	hmac-md5.c \
+	ntlm-des.c \
+	ntlm-encrypt.c \
+	ntlm-message.c
+
+noinst_HEADERS = \
+	hmac-md5.h \
+	ntlm.h \
+	ntlm-types.h \
+	ntlm-flags.h \
+	ntlm-byteorder.h \
+	ntlm-des.h \
+	ntlm-encrypt.h \
+	ntlm-message.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/hmac-md5.c	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,48 @@
+/*
+ * HMAC-MD5 (RFC-2104) implementation.
+ *
+ * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published 
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "lib.h"
+#include "hmac-md5.h"
+
+void hmac_md5_init(struct hmac_md5_context *ctx,
+		   const unsigned char * key, size_t key_len)
+{
+	int i;
+	unsigned char md5key[16];
+
+	if (key_len > 64) {
+		md5_get_digest(key, key_len, md5key);
+		key = md5key;
+		key_len = 16;
+	}
+
+	memcpy(ctx->k_ipad, key, key_len);
+	memset(ctx->k_ipad + key_len, 0, 64 - key_len);
+	memcpy(ctx->k_opad, ctx->k_ipad, 64);
+
+	for (i = 0; i < 64; i++) {
+		ctx->k_ipad[i] ^= 0x36;
+		ctx->k_opad[i] ^= 0x5c;
+	}
+
+	md5_init(&ctx->ctx);
+	md5_update(&ctx->ctx, ctx->k_ipad, 64);  
+}
+
+void hmac_md5_final(struct hmac_md5_context *ctx, unsigned char *digest)
+{
+	md5_final(&ctx->ctx, digest);
+
+	md5_init(&ctx->ctx);
+	md5_update(&ctx->ctx, ctx->k_opad, 64);   
+	md5_update(&ctx->ctx, digest, 16); 
+	md5_final(&ctx->ctx, digest);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/hmac-md5.h	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,21 @@
+#ifndef __HMAC_MD5_H__
+#define __HMAC_MD5_H__
+
+#include "md5.h"
+
+struct hmac_md5_context {
+	struct md5_context ctx;
+	unsigned char k_ipad[64];
+	unsigned char k_opad[64];
+};
+
+void hmac_md5_init(struct hmac_md5_context *ctx, const unsigned char* key, size_t key_len);
+void hmac_md5_final(struct hmac_md5_context *ctx, unsigned char *digest);
+
+static inline void
+hmac_md5_update(struct hmac_md5_context *ctx, const void * data, size_t size)
+{
+	md5_update(&ctx->ctx, data, size);
+}
+
+#endif /* __HMAC_MD5_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/ntlm-byteorder.h	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,111 @@
+/*
+ * Little-endian data access functions.
+ *
+ * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published 
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __NTLM_BYTEORDER_H__
+#define __NTLM_BYTEORDER_H__
+
+#if defined(__i386__) || defined(__vax__)
+
+static inline uint16_t read_le16(const void *addr)
+{
+	return *((uint16_t *) addr);
+}
+
+static inline uint32_t read_le32(const void *addr)
+{
+	return *((uint32_t *) addr);
+}
+
+static inline uint64_t read_le64(const void *addr)
+{
+	return *((uint64_t *) addr);
+}
+
+static inline void write_le16(void *addr, const uint16_t value)
+{
+	*((uint16_t *) addr) = value;
+}
+
+static inline void write_le32(void *addr, const uint32_t value)
+{
+	*((uint32_t *) addr) = value;
+}
+
+static inline void write_le64(void *addr, const uint64_t value)
+{
+	*((uint64_t *) addr) = value;
+}
+
+#else
+
+/*
+ * Dumb and slow, but byteorder and alignment independent code.
+ */
+
+#define readb(addr, pos, type) ((type)(*(((uint8_t *) (addr)) + (pos))))
+
+static inline uint16_t read_le16(const void *addr)
+{
+	return readb(addr, 0, uint16_t) | (readb(addr, 1, uint16_t) << 8);
+}
+
+static inline uint32_t read_le32(const void *addr)
+{
+	return   readb(addr, 0, uint32_t) |
+		(readb(addr, 1, uint32_t) << 8) |
+		(readb(addr, 2, uint32_t) << 16) |
+		(readb(addr, 3, uint32_t) << 24);
+}
+
+static inline uint64_t read_le64(const void *addr)
+{
+	return   readb(addr, 0, uint64_t) |
+		(readb(addr, 1, uint64_t) << 8) |
+		(readb(addr, 2, uint64_t) << 16) |
+		(readb(addr, 3, uint64_t) << 24) |
+		(readb(addr, 4, uint64_t) << 32) |
+		(readb(addr, 5, uint64_t) << 40) |
+		(readb(addr, 6, uint64_t) << 48) |
+		(readb(addr, 7, uint64_t) << 56);
+}
+
+#define writeb(addr, pos, value) \
+	*(((uint8_t *)(addr)) + (pos)) = (uint8_t) (value)
+
+static inline void write_le16(void *addr, const uint16_t value)
+{
+	writeb(addr, 0, value & 0xff);
+	writeb(addr, 1, (value >> 8) & 0xff);
+}
+
+static inline void write_le32(void *addr, const uint32_t value)
+{
+	writeb(addr, 0, value & 0xff);
+	writeb(addr, 1, (value >> 8) & 0xff);
+	writeb(addr, 2, (value >> 16) & 0xff);
+	writeb(addr, 3, (value >> 24) & 0xff);
+}
+
+static inline void write_le64(void *addr, const uint64_t value)
+{
+	writeb(addr, 0, value & 0xff);
+	writeb(addr, 1, (value >> 8) & 0xff);
+	writeb(addr, 2, (value >> 16) & 0xff);
+	writeb(addr, 3, (value >> 24) & 0xff);
+	writeb(addr, 4, (value >> 32) & 0xff);
+	writeb(addr, 5, (value >> 40) & 0xff);
+	writeb(addr, 6, (value >> 48) & 0xff);
+	writeb(addr, 7, (value >> 56) & 0xff);
+}
+
+#endif
+
+#endif	/* __NTLM_BYTEORDER_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/ntlm-des.c	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,604 @@
+/*
+ * Implements DES encryption, but not decryption.
+ * DES is used to create LM password hashes and both LM and NTLM Responses.
+ *
+ * Copyright (C) 2003, 2004 by Christopher R. Hertel <crh@ubiqx.mn.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Notes:
+ *
+ *  This implementation was created by studying many existing examples
+ *  found in Open Source, in the public domain, and in various documentation.
+ *  The SMB protocol makes minimal use of the DES function, so this is a
+ *  minimal implementation.  That which is not required has been removed.
+ *
+ *  The SMB protocol uses the DES algorithm as a hash function, not an
+ *  encryption function.  The auth_DEShash() implemented here is a one-way
+ *  function.  The reverse is not implemented in this module.  Also, there
+ *  is no attempt at making this either fast or efficient.  There is no
+ *  need, as the auth_DEShash() function is used for generating the LM
+ *  Response from a 7-byte key and an 8-byte challenge.  It is not intended
+ *  for use in encrypting large blocks of data or data streams.
+ *
+ *  As stated above, this implementation is based on studying existing work
+ *  in the public domain or under Open Source (specifically LGPL) license.
+ *  The code, however, is written from scratch.  Obviously, I make no claim
+ *  with regard to those earlier works (except to claim that I am grateful
+ *  to the previous implementors whose work I studied).  See the list of
+ *  references below for resources I used.
+ *
+ *  References:
+ *    I read through the libmcrypt code to see how they put the pieces
+ *    together.  See: http://mcrypt.hellug.gr/
+ *    Libmcrypt is available under the terms of the LGPL.
+ *
+ *    The libmcrypt implementation includes the following credits:
+ *      written 12 Dec 1986 by Phil Karn, KA9Q; large sections adapted
+ *      from the 1977 public-domain program by Jim Gillogly
+ *      Modified for additional speed - 6 December 1988 Phil Karn
+ *      Modified for parameterized key schedules - Jan 1991 Phil Karn
+ *      modified in order to use the libmcrypt API by Nikos Mavroyanopoulos
+ *      All modifications are placed under the license of libmcrypt.
+ *
+ *    See also Phil Karn's privacy and security page:
+ *      http://www.ka9q.net/privacy.html
+ *
+ *    I relied heavily upon:
+ *      Applied Cryptography, Second Edition:
+ *        Protocols, Algorithms, and Source Code in C
+ *      by Bruce Schneier. ISBN 0-471-11709-9, John Wiley & Sons, Inc., 1996
+ *    Particularly Chapter 12.
+ *
+ *    Here's one more DES resource, which I found quite helpful (aside from
+ *    the Clinton jokes):
+ *      http://www.aci.net/kalliste/des.htm
+ *
+ *    Finally, the use of DES in SMB is covered in:
+ *      Implementing CIFS - the Common Internet File System
+ *      by your truly.  ISBN 0-13-047116-X, Prentice Hall PTR., August 2003
+ *    Section 15.3, in particular.
+ *    (Online at: http://ubiqx.org/cifs/SMB.html#SMB.8.3)
+ */
+
+#include "ntlm-des.h"
+
+/*
+ * Initial permutation map.
+ * In the first step of DES, the bits of the initial plaintext are rearranged 
+ * according to the map given below.  This map and those like it are read by
+ * the permute() function (below) which uses the maps as a guide when moving
+ * bits from one place to another.
+ *
+ * Note that the values here are all one less than those shown in Schneier.
+ * That's because C likes to start counting from 0, not 1.
+ *
+ * According to Schneier (Ch12, pg 271), the purpose of the initial
+ * permutation was to make it easier to load plaintext and ciphertext into
+ * a DES ecryption chip.  I have no idea why that would be the case.
+ */
+static const unsigned char InitialPermuteMap[64] = {
+	57, 49, 41, 33, 25, 17, 9, 1,
+	59, 51, 43, 35, 27, 19, 11, 3,
+	61, 53, 45, 37, 29, 21, 13, 5,
+	63, 55, 47, 39, 31, 23, 15, 7,
+	56, 48, 40, 32, 24, 16, 8, 0,
+	58, 50, 42, 34, 26, 18, 10, 2,
+	60, 52, 44, 36, 28, 20, 12, 4,
+	62, 54, 46, 38, 30, 22, 14, 6
+};
+
+/*
+ * Key permutation map.
+ * Like the input data and encryption result, the key is permuted before
+ * the algorithm really gets going.  The original algorithm called for an
+ * eight-byte key in which each byte contained a parity bit.  During the
+ * key permutiation, the parity bits were discarded.  The DES algorithm,
+ * as used with SMB, does not make use of the parity bits.  Instead, SMB
+ * passes 7-byte keys to DES.  For DES implementations that expect parity,
+ * the parity bits must be added.  In this case, however, we're just going
+ * to start with a 7-byte (56 bit) key.  KeyPermuteMap, below, is adjusted
+ * accordingly and, of course, each entry in the map is reduced by 1 with
+ * respect to the documented values because C likes to start counting from
+ * 0, not 1.
+ */
+static const unsigned char KeyPermuteMap[56] = {
+	49, 42, 35, 28, 21, 14,  7,  0,
+	50, 43, 36, 29, 22, 15,  8,  1,
+	51, 44, 37, 30, 23, 16,  9,  2,
+	52, 45, 38, 31, 55, 48, 41, 34,
+	27, 20, 13,  6, 54, 47, 40, 33,
+	26, 19, 12,  5, 53, 46, 39, 32,
+	25, 18, 11,  4, 24, 17, 10,  3
+};
+
+/*
+ * Key rotation table.
+ * At the start of each round of encryption, the key is split and each
+ * 28-bit half is rotated left.  The number of bits of rotation per round
+ * is given in the table below.
+ */
+static const unsigned char KeyRotation[16] = {
+	1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+};
+
+/*
+ * Key compression table.
+ * This table is used to select 48 of the 56 bits of the key.
+ * The left and right halves of the source text are each 32 bits,
+ * but they are expanded to 48 bits and the results are XOR'd
+ * against the compressed (48-bit) key.
+ */
+static const unsigned char KeyCompression[48] = {
+	13, 16, 10, 23,  0,  4,  2, 27,
+	14,  5, 20,  9, 22, 18, 11,  3,
+	25,  7, 15,  6, 26, 19, 12,  1,
+	40, 51, 30, 36, 46, 54, 29, 39,
+	50, 44, 32, 47, 43, 48, 38, 55,
+	33, 52, 45, 41, 49, 35, 28, 31
+};
+
+/*
+ * Data expansion table.
+ * This table is used after the data block (64-bits) has been split
+ * into two 32-bit (4-byte) halves (generally denoted L and R).
+ * Each 32-bit half is "expanded", using this table, to a 48 bit
+ * data block, which is then XOR'd with the 48 bit subkey for the
+ * round.
+ */
+static const unsigned char DataExpansion[48] = {
+	31,  0,  1,  2,  3,  4,  3,  4,
+	 5,  6,  7,  8,  7,  8,  9, 10,
+	11, 12, 11, 12, 13, 14, 15, 16,
+	15, 16, 17, 18, 19, 20, 19, 20,
+	21, 22, 23, 24, 23, 24, 25, 26,
+	27, 28, 27, 28, 29, 30, 31,  0
+};
+
+/*
+ * The (in)famous S-boxes.
+ * These are used to perform substitutions.
+ * Six bits worth of input will return four bits of output.
+ * The four bit values are stored in these tables.  Each table has
+ * 64 entries...and 6 bits provides a number between 0 and 63.
+ * There are eight S-boxes, one per 6 bits of a 48-bit value.
+ * Thus, 48 bits are reduced to 32 bits.  Obviously, this step
+ * follows the DataExpansion step.
+ *
+ * Note that the literature generally shows this as 8 arrays each
+ * with four rows and 16 colums.  There is a complex formula for
+ * mapping the 6 bit input values to the correct row and column.
+ * I've pre-computed that mapping, and the tables below provide
+ * direct 6-bit input to 4-bit output.  See pp 274-274 in Schneier.
+ */
+static const unsigned char sbox[8][64] = {
+	{	/* S0 */
+		14,  0,  4, 15, 13,  7,  1,  4,  2, 14, 15,  2, 11, 13,  8,  1,
+		 3, 10, 10,  6,  6, 12, 12, 11,  5,  9,  9,  5,  0,  3,  7,  8,
+		 4, 15,  1, 12, 14,  8,  8,  2, 13,  4,  6,  9,  2,  1, 11,  7,
+		15,  5, 12, 11,  9,  3,  7, 14,  3, 10, 10,  0,  5,  6,  0, 13
+	},
+	{	/* S1 */
+		15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14,
+		 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5,
+		 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2,
+		 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9
+	},
+	{	/* S2 */
+		10, 13,  0,  7,  9,  0, 14,  9,  6,  3,  3,  4, 15,  6,  5, 10,
+		 1,  2, 13,  8, 12,  5,  7, 14, 11, 12,  4, 11,  2, 15,  8,  1,
+		13,  1,  6, 10,  4, 13,  9,  0,  8,  6, 15,  9,  3,  8,  0,  7,
+		11,  4,  1, 15,  2, 14, 12,  3,  5, 11, 10,  5, 14,  2,  7, 12
+	},
+	{	/* S3 */
+		 7, 13, 13,  8, 14, 11,  3,  5,  0,  6,  6, 15,  9,  0, 10,  3,
+		 1,  4,  2,  7,  8,  2,  5, 12, 11,  1, 12, 10,  4, 14, 15,  9,
+		10,  3,  6, 15,  9,  0,  0,  6, 12, 10, 11,  1,  7, 13, 13,  8,
+		15,  9,  1,  4,  3,  5, 14, 11,  5, 12,  2,  7,  8,  2,  4, 14
+	},
+	{	/* S4 */
+		 2, 14, 12, 11,  4,  2,  1, 12,  7,  4, 10,  7, 11, 13,  6,  1,
+		 8,  5,  5,  0,  3, 15, 15, 10, 13,  3,  0,  9, 14,  8,  9,  6,
+		 4, 11,  2,  8,  1, 12, 11,  7, 10,  1, 13, 14,  7,  2,  8, 13,
+		15,  6,  9, 15, 12,  0,  5,  9,  6, 10,  3,  4,  0,  5, 14,  3
+	},
+	{	/* S5 */
+		12, 10,  1, 15, 10,  4, 15,  2,  9,  7,  2, 12,  6,  9,  8,  5,
+		 0,  6, 13,  1,  3, 13,  4, 14, 14,  0,  7, 11,  5,  3, 11,  8,
+		 9,  4, 14,  3, 15,  2,  5, 12,  2,  9,  8,  5, 12, 15,  3, 10,
+		 7, 11,  0, 14,  4,  1, 10,  7,  1,  6, 13,  0, 11,  8,  6, 13
+	},
+	{	/* S6 */
+		 4, 13, 11,  0,  2, 11, 14,  7, 15,  4,  0,  9,  8,  1, 13, 10,
+		 3, 14, 12,  3,  9,  5,  7, 12,  5,  2, 10, 15,  6,  8,  1,  6,
+		 1,  6,  4, 11, 11, 13, 13,  8, 12,  1,  3,  4,  7, 10, 14,  7,
+		10,  9, 15,  5,  6,  0,  8, 15,  0, 14,  5,  2,  9,  3,  2, 12
+	},
+	{	/* S7 */
+		13,  1,  2, 15,  8, 13,  4,  8,  6, 10, 15,  3, 11,  7,  1,  4,
+		10, 12,  9,  5,  3,  6, 14, 11,  5,  0,  0, 14, 12,  9,  7,  2,
+		 7,  2, 11,  1,  4, 14,  1,  7,  9,  4, 12, 10, 14,  8,  2, 13,
+		 0, 15,  6, 12, 10,  9, 13,  0, 15,  3,  3,  5,  5,  6,  8, 11
+	}
+};
+
+/*
+ * P-Box permutation.
+ * This permutation is applied to the result of the S-Box Substitutions.
+ * It's a straight-forward re-arrangement of the bits.
+ */
+static const unsigned char pbox[32] = {
+	15,  6, 19, 20, 28, 11, 27, 16,
+	 0, 14, 22, 25,  4, 17, 30,  9,
+	 1,  7, 23, 13, 31, 26,  2,  8,
+	18, 12, 29,  5, 21, 10,  3, 24
+};
+
+/*
+ * Final permutation map.
+ * This is supposed to be the inverse of the Initial Permutation,
+ * but there's been a bit of fiddling done.
+ * As always, the values given are one less than those in the literature
+ * (because C starts counting from 0, not 1).  In addition, the penultimate
+ * step in DES is to swap the left and right hand sides of the ciphertext.
+ * The inverse of the Initial Permutation is then applied to produce the
+ * final result.
+ * To save a step, the map below does the left/right swap as well as the
+ * inverse permutation.
+ */
+static const unsigned char FinalPermuteMap[64] = {
+	7, 39, 15, 47, 23, 55, 31, 63,
+	6, 38, 14, 46, 22, 54, 30, 62,
+	5, 37, 13, 45, 21, 53, 29, 61,
+	4, 36, 12, 44, 20, 52, 28, 60,
+	3, 35, 11, 43, 19, 51, 27, 59,
+	2, 34, 10, 42, 18, 50, 26, 58,
+	1, 33,  9, 41, 17, 49, 25, 57,
+	0, 32,  8, 40, 16, 48, 24, 56
+};
+
+/*
+ * Macros:
+ *
+ *  CLRBIT( STR, IDX )
+ *    Input:  STR - (uchar *) pointer to an array of 8-bit bytes.
+ *            IDX - (int) bitwise index of a bit within the STR array
+ *                  that is to be cleared (that is, given a value of 0).
+ *    Notes:  This macro clears a bit within an array of bits (which is
+ *            built within an array of bytes).
+ *          - The macro converts to an assignment of the form A &= B.
+ *          - The string of bytes is viewed as an array of bits, read from
+ *            highest order bit first.  The highest order bit of a byte
+ *            would, therefore, be bit 0 (within that byte).
+ *
+ *  SETBIT( STR, IDX )
+ *    Input:  STR - (uchar *) pointer to an array of 8-bit bytes.
+ *            IDX - (int) bitwise index of a bit within the STR array
+ *                  that is to be set (that is, given a value of 1).
+ *    Notes:  This macro sets a bit within an array of bits (which is
+ *            built within an array of bytes).
+ *          - The macro converts to an assignment of the form A |= B.
+ *          - The string of bytes is viewed as an array of bits, read from
+ *            highest order bit first.  The highest order bit of a byte
+ *            would, therefore, be bit 0 (within that byte).
+ *
+ *  GETBIT( STR, IDX )
+ *    Input:  STR - (uchar *) pointer to an array of 8-bit bytes.
+ *            IDX - (int) bit-wise index of a bit within the STR array
+ *                  that is to be read.
+ *    Output: True (1) if the indexed bit was set, else false (0).
+ */
+#define CLRBIT(STR, IDX) ((STR)[(IDX)/8] &= ~(0x01 << (7 - ((IDX)%8))))
+
+#define SETBIT( STR, IDX ) ( (STR)[(IDX)/8] |= (0x01 << (7 - ((IDX)%8))) )
+
+#define GETBIT( STR, IDX ) (( ((STR)[(IDX)/8]) >> (7 - ((IDX)%8)) ) & 0x01)
+
+/*
+ * Performs a DES permutation, which re-arranges the bits in an array of
+ * bytes.
+ *
+ *  Input:  dst     - Destination into which to put the re-arranged bits.
+ *          src     - Source from which to read the bits.
+ *          map     - Permutation map.
+ *          mapsize - Number of bytes represented by the <map>.  This also
+ *                    represents the number of bytes to be copied to <dst>.
+ *
+ *  Output: none.
+ *
+ *  Notes:  <src> and <dst> must not point to the same location.
+ *
+ *        - No checks are done to ensure that there is enough room
+ *          in <dst>, or that the bit numbers in <map> do not exceed
+ *          the bits available in <src>.  A good reason to make this
+ *          function static (private).
+ *
+ *        - The <mapsize> value is in bytes.  All permutations in DES
+ *          use tables that are a multiple of 8 bits, so there is no
+ *          need to handle partial bytes.  (Yes, I know that there
+ *          are some machines out there that still use bytes of a size
+ *          other than 8 bits.  For our purposes we'll stick with 8-bit
+ *          bytes.)
+ */
+static void
+permute(unsigned char *dst, const unsigned char *src,
+	const unsigned char * map, const int mapsize)
+{
+	int bitcount;
+	int i;
+
+	/* Clear all bits in the destination. */
+	for (i = 0; i < mapsize; i++)
+		dst[i] = 0;
+
+	/* Set destination bit if the mapped source bit it set. */
+	bitcount = mapsize * 8;
+	for (i = 0; i < bitcount; i++) {
+		if (GETBIT(src, map[i]))
+			SETBIT(dst, i);
+	}
+}
+
+/*
+ * Split the 56-bit key in half & left rotate each half by <numbits> bits.
+ *
+ *  Input:  key     - The 56-bit key to be split-rotated.
+ *          numbits - The number of bits by which to rotate the key.
+ *
+ *  Output: none.
+ *
+ *  Notes:  There are probably several better ways to implement this.
+ */
+static void
+keyshift(unsigned char *key, const int numbits)
+{
+	int i;
+	unsigned char keep = key[0];	/* Copy the highest order bits of the key. */
+
+	/* Repeat the shift process <numbits> times. */
+	for (i = 0; i < numbits; i++) {
+		int j;
+
+		/* Shift the entire thing, byte by byte.
+		 */
+		for (j = 0; j < 7; j++) {
+			if (j && (key[j] & 0x80))	/* If the top bit of this byte is set. */
+				key[j - 1] |= 0x01;	/* ...shift it to last byte's low bit. */
+			key[j] <<= 1;	/* Then left-shift the whole byte.     */
+		}
+
+		/* Now move the high-order bits of each 28-bit half-key to their
+		 * correct locations.
+		 * Bit 27 is the lowest order bit of the first half-key.
+		 * Before the shift, it was the highest order bit of the 2nd half-key.
+		 */
+		if (GETBIT(key, 27)) {	/* If bit 27 is set... */
+			CLRBIT(key, 27);	/* ...clear bit 27. */
+			SETBIT(key, 55);	/* ...set lowest order bit of 2nd half-key. */
+		}
+
+		/* We kept the highest order bit of the first half-key in <keep>.
+		 * If it's set, copy it to bit 27.
+		 */
+		if (keep & 0x80)
+			SETBIT(key, 27);
+
+		/* Rotate the <keep> byte too, in case <numbits> is 2 and there's
+		 * a second round coming.
+		 */
+		keep <<= 1;
+	}
+}
+
+/*
+ * Perform S-Box substitutions.
+ *
+ *  Input:  dst - Destination byte array into which the S-Box substituted
+ *                bitmap will be written.
+ *          src - Source byte array.
+ *
+ *  Output: none.
+ *
+ *  Notes:  It's really not possible (for me, anyway) to understand how
+ *          this works without reading one or more detailed explanations.
+ *          Quick overview, though:
+ *
+ *          After the DataExpansion step (in which a 32-bit bit array is
+ *          expanded to a 48-bit bit array) the expanded data block is
+ *          XOR'd with 48-bits worth of key.  That 48 bits then needs to
+ *          be condensed back into 32 bits.
+ *
+ *          The S-Box substitution handles the data reduction by breaking
+ *          the 48-bit value into eight 6-bit values.  For each of these
+ *          6-bit values there is a table (an S-Box table).  The table
+ *          contains 64 possible values.  Conveniently, a 6-bit integer
+ *          can represent a value between 0 and 63.
+ *
+ *          So, if you think of the 48-bit bit array as an array of 6-bit
+ *          integers, you use S-Box table 0 with the 0th 6-bit value.
+ *          Table 1 is used with the 6-bit value #1, and so on until #7.
+ *          Within each table, the correct substitution is found based
+ *          simply on the value of the 6-bit integer.
+ *
+ *          Well, the original algorithm (and most documentation) don't
+ *          make it so simple.  There's a complex formula for mapping
+ *          the 6-bit values to the correct substitution.  Fortunately,
+ *          those lookups can be precomputed (and have been for this
+ *          implementation).  See pp 274-274 in Schneier.
+ *
+ *          Oh, and the substitute values are all 4-bit values, so each
+ *          6-bits gets reduced to 4-bits resulting in a 32-bit bit array.
+ */
+static void
+s_box(unsigned char *dst, const unsigned char *src)
+{
+	int i;
+
+	/* Clear the destination array. */
+	for (i = 0; i < 4; i++)
+		dst[i] = 0;
+
+	/* For each set of six input bits... */
+	for (i = 0; i < 8; i++) {
+		int j;
+		int Snum;
+		int bitnum;
+
+		/* Extract the 6-bit integer from the source.
+		 * This will be the lookup key within the sbox[i] array.
+		 */
+		for (Snum = j = 0, bitnum = (i * 6); j < 6; j++, bitnum++) {
+			Snum <<= 1;
+			Snum |= GETBIT(src, bitnum);
+		}
+
+		/* Find the correct value in the correct sbox[]
+		 * and copy it into the destination.
+		 * Left shift the nibble four bytes for even values of <i>.
+		 */
+		if (0 == (i % 2))
+			dst[i / 2] |= ((sbox[i][Snum]) << 4);
+		else
+			dst[i / 2] |= sbox[i][Snum];
+	}
+}
+
+/*
+ * Perform an XOR operation on two byte arrays.
+ *
+ *  Input:  dst   - Destination array to which the result will be written.
+ *          a     - The first string of bytes.
+ *          b     - The second string of bytes.
+ *          count - Number of bytes to XOR against one another.
+ *
+ *  Output: none.
+ *
+ *  Notes:  This function operates on whole byte chunks.  There's no need
+ *          to XOR partial bytes so no need to write code to handle it.
+ *
+ *        - This function essentially implements dst = a ^ b; for byte
+ *          arrays.
+ *
+ *        - <dst> may safely point to the same location as <a> or <b>.
+ */
+static void xor(unsigned char *dst, const unsigned char *a, 
+		const unsigned char *b,	const int count)
+{
+	int i;
+	for (i = 0; i < count; i++)
+		dst[i] = a[i] ^ b[i];
+}
+
+/*
+ * DES encryption of the input data using the input key.
+ *
+ *  Input:  dst - Destination buffer.  It *must* be at least eight bytes
+ *                in length, to receive the encrypted result.
+ *          key - Encryption key.  Exactly seven bytes will be used.
+ *                If your key is shorter, ensure that you pad it to seven
+ *                bytes.
+ *          src - Source data to be encrypted.  Exactly eight bytes will
+ *                be used.  If your source data is shorter, ensure that
+ *                you pad it to eight bytes.
+ *
+ *  Output: A pointer to the encrpyted data (same as <dst>).
+ *
+ *  Notes:  In SMB, the DES function is used as a hashing function rather
+ *          than an encryption/decryption tool.  When used for generating
+ *          the LM hash the <src> input is the known value "KGS!@#$%" and
+ *          the key is derived from the password entered by the user.
+ *          When used to generate the LM or NTLM response, the <key> is
+ *          derived from the LM or NTLM hash, and the challenge is used
+ *          as the <src> input.
+ *          See: http://ubiqx.org/cifs/SMB.html#SMB.8.3
+ *
+ *        - This function is called "DEShash" rather than just "DES"
+ *          because it is only used for creating LM hashes and the
+ *          LM/NTLM responses.  For all practical purposes, however, it
+ *          is a full DES encryption implementation.
+ *
+ *        - This DES implementation does not need to be fast, nor is a
+ *          DES decryption function needed.  The goal is to keep the
+ *          code small, simple, and well documented.
+ *
+ *        - The input values are copied and refiddled within the module
+ *          and the result is not written to <dst> until the very last
+ *          step, so it's okay if <dst> points to the same memory as
+ *          <key> or <src>.
+ */
+unsigned char *
+deshash(unsigned char *dst, const unsigned char *key,
+	const unsigned char *src)
+{
+	int i;			/* Loop counter. */
+	unsigned char K[7];	/* Holds the key, as we manipulate it. */
+	unsigned char D[8];	/* The data block, as we manipulate it. */
+
+	/* Create the permutations of the key and the source. */
+	permute(K, key, KeyPermuteMap, 7);
+	permute(D, src, InitialPermuteMap, 8);
+
+	/* DES encryption proceeds in 16 rounds.
+	 * The stuff inside the loop is known in the literature as "function f".
+	 */
+	for (i = 0; i < 16; i++) {
+		int j;
+		unsigned char *L = D;		/* The left 4 bytes (half) of the data block. */
+		unsigned char *R = &(D[4]);	/* The right half of the ciphertext block. */
+		unsigned char Rexp[6];		/* Expanded right half. */
+		unsigned char Rn[4];		/* New value of R, as we manipulate it. */
+		unsigned char SubK[6];		/* The 48-bit subkey. */
+
+		/* Generate the subkey for this round. */
+		keyshift(K, KeyRotation[i]);
+		permute(SubK, K, KeyCompression, 6);
+
+		/* Expand the right half (R) of the data block to 48 bytes,
+		 * then XOR the result with the Subkey for this round.
+		 */
+		permute(Rexp, R, DataExpansion, 6);
+		xor(Rexp, Rexp, SubK, 6);
+
+		/* S-Box substitutions, P-Box permutation, and final XOR.
+		 * The S-Box substitutions return a 32-bit value, which is then
+		 * run through the 32-bit to 32-bit P-Box permutation.  The P-Box
+		 * result is then XOR'd with the left-hand half of the key.
+		 * (Rexp is used as a temporary variable between the P-Box & XOR).
+		 */
+		s_box(Rn, Rexp);
+		permute(Rexp, Rn, pbox, 4);
+		xor(Rn, L, Rexp, 4);
+
+		/* The previous R becomes the new L,
+		 * and Rn is moved into R ready for the next round.
+		 */
+		for (j = 0; j < 4; j++) {
+			L[j] = R[j];
+			R[j] = Rn[j];
+		}
+	}
+
+	/* The encryption is complete.
+	 * Now reverse-permute the ciphertext to produce the final result.
+	 * We actually combine two steps here.  The penultimate step is to
+	 * swap the positions of L and R in the result of the 16 rounds,
+	 * after which the reverse of the Initial Permutation is applied.
+	 * To save a step, the FinalPermuteMap applies both the L/R swap
+	 * and the inverse of the Initial Permutation.
+	 */
+	permute(dst, D, FinalPermuteMap, 8);
+	return dst;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/ntlm-des.h	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,7 @@
+#ifndef __NTLM_DES_H__
+#define __NTLM_DES_H__
+
+unsigned char * deshash(unsigned char *dst, const unsigned char *key,
+			const unsigned char *src);
+
+#endif	/* __NTLM_DES_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/ntlm-encrypt.c	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,114 @@
+/*
+ * NTLM and NTLMv2 hash generation.
+ *
+ * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published 
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <ctype.h>
+
+#include "lib.h"
+#include "buffer.h"
+#include "compat.h"
+#include "safe-memset.h"
+#include "md4.h"
+#include "hmac-md5.h"
+#include "ntlm.h"
+#include "ntlm-des.h"
+
+static unsigned char *
+t_unicode_str(const char *src, int ucase, size_t *size)
+{
+	buffer_t *wstr;
+
+	wstr = buffer_create_dynamic(unsafe_data_stack_pool, 32, (size_t)-1);
+	for ( ; *src; src++) {
+		buffer_append_c(wstr, ucase ? i_toupper(*src) : *src);
+		buffer_append_c(wstr, '\0');
+	}
+
+	*size = buffer_get_used_size(wstr);
+	return buffer_free_without_data(wstr);
+}
+
+static void
+ntlmssp_des_encrypt_triad(const unsigned char *hash,
+		 	  const unsigned char *challenge,
+			  unsigned char *response)
+{
+	deshash(response, hash, challenge);
+	deshash(response + 8, hash + 7, challenge);
+	deshash(response + 16, hash + 14, challenge);
+}
+
+const unsigned char *
+ntlm_v1_hash(const char *passwd, unsigned char hash[NTLMSSP_HASH_SIZE])
+{
+	size_t len;
+	void *wpwd = t_unicode_str(passwd, 0, &len);
+
+	md4_get_digest(wpwd, len, hash);
+
+	safe_memset(wpwd, 0, len);
+
+	return hash;
+}
+
+static void
+hmac_md5_ucs2le_string_ucase(struct hmac_md5_context *ctx, const char *str)
+{
+	size_t len;
+	unsigned char *wstr = t_unicode_str(str, 1, &len);
+
+	hmac_md5_update(ctx, wstr, len);
+}
+
+static void
+ntlm_v2_hash(const char *user, const char *target,
+	     const unsigned char *hash_v1,
+	     unsigned char hash[NTLMSSP_V2_HASH_SIZE])
+{
+	struct hmac_md5_context ctx;
+
+	hmac_md5_init(&ctx, hash_v1, NTLMSSP_HASH_SIZE);
+	hmac_md5_ucs2le_string_ucase(&ctx, user);
+	if (target)
+		hmac_md5_ucs2le_string_ucase(&ctx, target);
+	hmac_md5_final(&ctx, hash);
+}
+
+void
+ntlmssp_v1_response(const unsigned char *hash,
+		    const unsigned char *challenge,
+		    unsigned char response[NTLMSSP_RESPONSE_SIZE])
+{
+	unsigned char des_hash[NTLMSSP_DES_KEY_LENGTH * 3];
+
+	memcpy(des_hash, hash, NTLMSSP_HASH_SIZE);
+	memset(des_hash + NTLMSSP_HASH_SIZE, 0,
+	       sizeof(des_hash) - NTLMSSP_HASH_SIZE);
+
+	ntlmssp_des_encrypt_triad(des_hash, challenge, response);
+}
+
+void
+ntlmssp_v2_response(const char *user, const char *target,
+		    const unsigned char *hash_v1,
+		    const unsigned char *challenge,
+		    const unsigned char *blob, size_t blob_size,
+		    unsigned char response[NTLMSSP_V2_RESPONSE_SIZE])
+{
+	struct hmac_md5_context ctx;
+	unsigned char hash[NTLMSSP_V2_HASH_SIZE];
+
+	ntlm_v2_hash(user, target, hash_v1, hash);
+
+	hmac_md5_init(&ctx, hash, NTLMSSP_V2_HASH_SIZE);
+	hmac_md5_update(&ctx, challenge, NTLMSSP_CHALLENGE_SIZE);
+	hmac_md5_update(&ctx, blob, blob_size);
+	hmac_md5_final(&ctx, response);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/ntlm-encrypt.h	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,17 @@
+#ifndef __NTLM_ENCRYPT__
+#define __NTLM_ENCRYPT__
+
+const unsigned char *
+ntlm_v1_hash(const char *passwd, unsigned char hash[NTLMSSP_HASH_SIZE]);
+
+void ntlmssp_v1_response(const unsigned char *hash,
+			 const unsigned char *challenge,
+			 unsigned char response[NTLMSSP_RESPONSE_SIZE]);
+
+void ntlmssp_v2_response(const char *user, const char *target,
+			 const unsigned char *hash_v1,
+			 const unsigned char *challenge,
+			 const unsigned char *blob, size_t blob_size,
+			 unsigned char response[NTLMSSP_V2_RESPONSE_SIZE]);
+
+#endif	/* __NTLM_ENCRYPT__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/ntlm-flags.h	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,140 @@
+/*
+ * NTLM message flags.
+ *
+ * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#ifndef __NTLM_FLAGS_H__
+#define __NTLM_FLAGS_H__
+
+/*
+ * Indicates that Unicode strings are supported for use in security
+ * buffer data. 
+ */
+#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 
+
+/*
+ * Indicates that OEM strings are supported for use in security buffer data.
+ */
+#define NTLMSSP_NEGOTIATE_OEM 0x00000002 
+
+/*
+ * Requests that the server's authentication realm be included in the
+ * Type 2 message. 
+ */
+#define NTLMSSP_REQUEST_TARGET 0x00000004 
+
+/*
+ * Specifies that authenticated communication between the client and server
+ * should carry a digital signature (message integrity). 
+ */
+#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 
+
+/*
+ * Specifies that authenticated communication between the client and server
+ * should be encrypted (message confidentiality).
+ */
+#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 
+
+/*
+ * Indicates that datagram authentication is being used. 
+ */
+#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040 
+
+/*
+ * Indicates that the LAN Manager session key should be
+ * used for signing and sealing authenticated communications.
+ */
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 
+
+/*
+ * Indicates that NTLM authentication is being used. 
+ */
+#define NTLMSSP_NEGOTIATE_NTLM 0x00000200 
+
+/*
+ * Sent by the client in the Type 1 message to indicate that the name of the
+ * domain in which the client workstation has membership is included in the
+ * message. This is used by the server to determine whether the client is
+ * eligible for local authentication. 
+ */
+#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000 
+
+/*
+ * Sent by the client in the Type 1 message to indicate that the client
+ * workstation's name is included in the message. This is used by the server
+ * to determine whether the client is eligible for local authentication.
+ */
+#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000 
+
+/*
+ * Sent by the server to indicate that the server and client are on the same
+ * machine. Implies that the client may use the established local credentials
+ * for authentication instead of calculating a response to the challenge.
+ */
+#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000 
+
+/*
+ * Indicates that authenticated communication between the client and server
+ * should be signed with a "dummy" signature. 
+ */
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 
+
+/*
+ * Sent by the server in the Type 2 message to indicate that the target
+ * authentication realm is a domain.
+ */
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 
+
+/*
+ * Sent by the server in the Type 2 message to indicate that the target
+ * authentication realm is a server. 
+ */
+#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 
+
+/*
+ * Sent by the server in the Type 2 message to indicate that the target
+ * authentication realm is a share. Presumably, this is for share-level
+ * authentication. Usage is unclear. 
+ */
+#define NTLMSSP_TARGET_TYPE_SHARE 0x00040000 
+
+/*
+ * Indicates that the NTLM2 signing and sealing scheme should be used for
+ * protecting authenticated communications. Note that this refers to a
+ * particular session security scheme, and is not related to the use of
+ * NTLMv2 authentication.
+ */ 
+#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000 
+
+/*
+ * Sent by the server in the Type 2 message to indicate that it is including
+ * a Target Information block in the message. The Target Information block
+ * is used in the calculation of the NTLMv2 response.
+ */
+#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 
+
+/*
+ * Indicates that 128-bit encryption is supported. 
+ */
+#define NTLMSSP_NEGOTIATE_128 0x20000000 
+
+/*
+ * Indicates that the client will provide an encrypted master session key in
+ * the "Session Key" field of the Type 3 message. This is used in signing and
+ * sealing, and is RC4-encrypted using the previous session key as the
+ * encryption key.
+ */
+#define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000 
+
+/*
+ * Indicates that 56-bit encryption is supported.
+ */
+#define NTLMSSP_NEGOTIATE_56 0x80000000 
+
+#endif	/* __NTLM_FLAGS_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/ntlm-message.c	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,240 @@
+/*
+ * NTLM message handling.
+ *
+ * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published 
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "lib.h"
+#include "str.h"
+#include "buffer.h"
+#include "hostpid.h"
+#include "randgen.h"
+
+#include "ntlm.h"
+#include "ntlm-message.h"
+
+const char * __ntlmssp_t_str(const void *message, struct ntlmssp_buffer *buffer)
+{
+	unsigned int len = read_le16(&buffer->length) / sizeof(ucs2le_t);
+	string_t *str = t_str_new(len / 2);
+	const char *p = ((char *) message) + read_le32(&buffer->offset);
+
+	while (len-- > 0) {
+		str_append_c(str, *p & 0x7f);
+		p += sizeof(ucs2le_t);
+	}
+
+	return str_c(str);
+}
+
+static unsigned int append_string(buffer_t *buf, const char *str, int ucase)
+{
+	unsigned int length = 0;
+
+	for ( ; *str; str++) {
+		buffer_append_c(buf, ucase ? toupper(*str) : *str);
+		buffer_append_c(buf, 0);
+		length += sizeof(ucs2le_t);
+	}
+
+	return length;
+}
+
+static void ntlmssp_append_string(buffer_t *buf, size_t buffer_offset,
+				  const char *str)
+{
+	struct ntlmssp_buffer buffer;
+	unsigned int length;
+
+	write_le32(&buffer.offset, buffer_get_used_size(buf));
+
+	length = append_string(buf, str, 0);
+
+	write_le16(&buffer.length, length);
+	write_le16(&buffer.space, length);
+	buffer_write(buf, buffer_offset, &buffer, sizeof(buffer));
+}
+
+static void ntlmssp_append_target_info(buffer_t *buf, size_t buffer_offset, ...)
+{
+	struct ntlmssp_v2_target_info info;
+	struct ntlmssp_buffer buffer;
+	va_list args;
+	unsigned int length, total_length = 0;
+	int type;
+
+	write_le32(&buffer.offset, buffer_get_used_size(buf));
+
+	va_start(args, buffer_offset);
+
+	do {
+		type = va_arg(args, int);
+		const char *data;
+
+		memset(&info, 0, sizeof(info));
+		write_le16(&info.type, type);
+
+		switch (type) {
+			case NTPLMSSP_V2_TARGET_END:
+				buffer_append(buf, &info, sizeof(info));
+				length = sizeof(info);
+				break;
+			case NTPLMSSP_V2_TARGET_SERVER:
+			case NTPLMSSP_V2_TARGET_DOMAIN:
+			case NTPLMSSP_V2_TARGET_FQDN:
+			case NTPLMSSP_V2_TARGET_DNS:
+				data = va_arg(args, char *);
+				write_le16(&info.length,
+					   strlen(data) * sizeof(ucs2le_t));
+				buffer_append(buf, &info, sizeof(info));
+				length = append_string(buf, data, 0);
+				break;
+			default:
+				i_panic("Invalid NTLM target info block type "
+					"%u", type);
+		}
+
+		total_length += length;
+	
+	} while (type != NTPLMSSP_V2_TARGET_END);
+
+	va_end(args);
+
+	write_le16(&buffer.length, total_length);
+	write_le16(&buffer.space, total_length);
+	buffer_write(buf, buffer_offset, &buffer, sizeof(buffer));
+}
+
+static inline uint32_t ntlmssp_flags(uint32_t client_flags)
+{
+	uint32_t flags = NTLMSSP_NEGOTIATE_UNICODE |
+			 NTLMSSP_NEGOTIATE_NTLM |
+			 NTLMSSP_NEGOTIATE_TARGET_INFO;
+
+	if (client_flags & NTLMSSP_REQUEST_TARGET)
+		flags |= NTLMSSP_REQUEST_TARGET | NTLMSSP_TARGET_TYPE_SERVER;
+
+	return flags;
+}
+
+const struct ntlmssp_challenge *
+ntlmssp_create_challenge(pool_t pool, const struct ntlmssp_request *request,
+			 size_t *size)
+{
+	buffer_t *buf;
+	uint32_t flags = ntlmssp_flags(read_le32(&request->flags));
+	struct ntlmssp_challenge c;
+
+	buf = buffer_create_dynamic(pool, sizeof(struct ntlmssp_challenge),
+				    (size_t)-1);
+
+	memset(&c, 0, sizeof(c));
+	write_le64(&c.magic, NTLMSSP_MAGIC);
+	write_le32(&c.type, NTLMSSP_MSG_TYPE2);
+	write_le32(&c.flags, flags);
+	random_fill(c.challenge, sizeof(c.challenge));
+
+	buffer_write(buf, 0, &c, sizeof(c));
+
+	if (flags & NTLMSSP_TARGET_TYPE_SERVER)
+		ntlmssp_append_string(buf,
+			offsetof(struct ntlmssp_challenge, target_name),
+			my_hostname);
+
+	ntlmssp_append_target_info(buf, offsetof(struct ntlmssp_challenge,
+						 target_info),
+				   NTPLMSSP_V2_TARGET_FQDN, my_hostname,
+				   NTPLMSSP_V2_TARGET_END);
+
+	*size = buffer_get_used_size(buf);
+	return buffer_free_without_data(buf);
+}
+
+static int ntlmssp_check_buffer(const struct ntlmssp_buffer *buffer,
+				size_t data_size, const char **error)
+{
+	uint32_t offset = read_le32(&buffer->offset);
+
+	if (offset >= data_size) {
+		*error = "buffer offset out of bounds";
+		return 0;
+	}
+
+	if (offset + read_le16(&buffer->space) > data_size) {
+		*error = "buffer end out of bounds";
+		return 0;
+	}
+
+	return 1;
+}
+
+int ntlmssp_check_request(const struct ntlmssp_request *request,
+			  size_t data_size, const char **error)
+{
+	uint32_t flags;
+
+	if (data_size < sizeof(struct ntlmssp_request)) {
+		*error = "request too short";
+		return 0;
+	}
+
+	if (read_le64(&request->magic) != NTLMSSP_MAGIC) {
+		*error = "signature mismatch";
+		return 0;
+	}
+
+	if (read_le32(&request->type) != NTLMSSP_MSG_TYPE1) {
+		*error = "message type mismatch";
+		return 0;
+	}
+
+	flags = read_le32(&request->flags);
+
+	if ((flags & NTLMSSP_NEGOTIATE_UNICODE) == 0) {
+		*error = "client doesn't advertise Unicode support";
+		return 0;
+	}
+
+	if ((flags & NTLMSSP_NEGOTIATE_NTLM) == 0) {
+		*error = "client doesn't advertise NTLM support";
+		return 0;
+	}
+
+	return 1;
+}
+
+int ntlmssp_check_response(const struct ntlmssp_response *response,
+			   size_t data_size, const char **error)
+{
+	if (data_size < sizeof(struct ntlmssp_response)) {
+		*error = "response too short";
+		return 0;
+	}
+
+	if (read_le64(&response->magic) != NTLMSSP_MAGIC) {
+		*error = "signature mismatch";
+		return 0;
+	}
+
+	if (read_le32(&response->type) != NTLMSSP_MSG_TYPE3) {
+		*error = "message type mismatch";
+		return 0;
+	}
+
+	if (!ntlmssp_check_buffer(&response->lm_response, data_size, error) ||
+	    !ntlmssp_check_buffer(&response->ntlm_response, data_size, error) ||
+	    !ntlmssp_check_buffer(&response->domain, data_size, error) ||
+	    !ntlmssp_check_buffer(&response->user, data_size, error) ||
+	    !ntlmssp_check_buffer(&response->workstation, data_size, error))
+		return 0;
+
+	return 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/ntlm-message.h	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,13 @@
+#ifndef __NTLM_MESSAGE_H__
+#define __NTLM_MESSAGE_H__
+
+const struct ntlmssp_challenge *
+ntlmssp_create_challenge(pool_t pool, const struct ntlmssp_request *request,
+			 size_t *size);
+
+int ntlmssp_check_request(const struct ntlmssp_request *request,
+			  size_t data_size, const char **error);
+int ntlmssp_check_response(const struct ntlmssp_response *response,
+			   size_t data_size, const char **error);
+
+#endif	/* __NTLM_MESSAGE_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/ntlm-types.h	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,130 @@
+/*
+ * NTLM data structures.
+ *
+ * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published 
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __NTLM_TYPES_H__
+#define __NTLM_TYPES_H__
+
+#define NTLMSSP_MAGIC			0x005053534d4c544eULL
+
+#define	NTLMSSP_MSG_TYPE1		1
+#define	NTLMSSP_MSG_TYPE2		2
+#define	NTLMSSP_MSG_TYPE3		3
+
+#define NTLMSSP_DES_KEY_LENGTH		7
+
+#define NTLMSSP_CHALLENGE_SIZE		8
+
+#define NTLMSSP_HASH_SIZE		16
+#define NTLMSSP_RESPONSE_SIZE		24
+
+#define NTLMSSP_V2_HASH_SIZE		16
+#define NTLMSSP_V2_RESPONSE_SIZE	16
+
+
+typedef uint16_t ucs2le_t;
+
+struct ntlmssp_buffer {
+	uint16_t length;	/* length of the buffer */
+	uint16_t space;		/* space allocated space for buffer */
+	uint32_t offset;	/* data offset from the start of the message */
+};
+
+typedef struct ntlmssp_buffer ntlmssp_buffer_t;
+
+
+/*
+ * 
+ */
+struct ntlmssp_message {
+	uint64_t magic;			/* NTLMSSP\0 */
+	uint32_t type;			/* Should be 1 */
+};
+
+/*
+ * Type 1 message, client sends it to start NTLM authentication sequence.
+ */
+struct ntlmssp_request {
+	uint64_t magic;			/* NTLMSSP\0 */
+	uint32_t type;			/* Should be 1 */
+	uint32_t flags;			/* Flags */
+	ntlmssp_buffer_t domain;	/* Domain name (optional) */
+	ntlmssp_buffer_t workstation;	/* Workstation name (optional) */
+	/* Start of the data block */
+};
+
+/*
+ * The Type 2 message is sent by the server to the client in response to
+ * the client's Type 1 message. It serves to complete the negotiation of
+ * options with the client, and also provides a challenge to the client.
+ */
+struct ntlmssp_challenge {
+	uint64_t magic;			/* NTLMSSP\0 */
+	uint32_t type;			/* Should be 2 */
+	ntlmssp_buffer_t target_name;	/* Name of authentication target */
+	uint32_t flags;			/* Flags */
+	uint8_t challenge[NTLMSSP_CHALLENGE_SIZE];	/* Server challenge */
+	uint32_t context[2];		/* Local authentication context handle */
+	ntlmssp_buffer_t target_info;	/* Target information block (for NTLMv2) */
+	/* Start of the data block */
+};
+
+/*
+ * The Type 3 message is the final step in authentication. This message
+ * contains the client's responses to the Type 2 challenge, which demonstrate
+ * that the client has knowledge of the account password without sending the
+ * password directly. The Type 3 message also indicates the domain and username
+ * of the authenticating account, as well as the client workstation name.
+ */
+struct ntlmssp_response {
+	uint64_t magic;			/* NTLMSSP\0 */
+	uint32_t type;			/* Should be 3 */
+	ntlmssp_buffer_t lm_response;	/* LM/LMv2 recponse */
+	ntlmssp_buffer_t ntlm_response;	/* NTLM/NTLMv2 recponse */
+	ntlmssp_buffer_t domain;	/* Domain name */
+	ntlmssp_buffer_t user;		/* User name */
+	ntlmssp_buffer_t workstation;	/* Workstation name */
+	ntlmssp_buffer_t session_key;	/* Session key (optional */
+	uint32_t flags;			/* Flags (optional) */
+	/* Start of the data block */
+};
+
+/*
+ * NTLMv2 Target Information Block item.
+ */
+struct ntlmssp_v2_target_info {
+	uint16_t type;			/* Data type (see below) */
+	uint16_t length;		/* Length of content field */
+	/* Content (always in ucs2-le) */
+};
+
+/*
+ * NTLMv2 Target Information Block item data type.
+ */
+enum {
+	NTPLMSSP_V2_TARGET_END = 0,	/* End of list  */
+	NTPLMSSP_V2_TARGET_SERVER,	/* NetBIOS server name */ 
+	NTPLMSSP_V2_TARGET_DOMAIN,	/* NT Domain NetBIOS name */
+	NTPLMSSP_V2_TARGET_FQDN,	/* Fully qualified host name */
+	NTPLMSSP_V2_TARGET_DNS,		/* DNS domain name */
+};
+
+/*
+ * NTLMv2 Authentication data blob.
+ */
+struct ntlmssp_v2_blob {
+	uint32_t magic;			/* Should be 0x01010000 */
+	uint32_t reserved;		/* Always 0 */
+	uint64_t timestamp;		/* Timestamp */
+	uint32_t unknown;		/* Unknown something */
+	/* Target Information Block */
+};
+
+#endif	/* __NTLM_TYPES_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-ntlm/ntlm.h	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,36 @@
+#ifndef __NTLM_H__
+#define __NTLM_H__
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "ntlm-types.h"
+#include "ntlm-flags.h"
+#include "ntlm-byteorder.h"
+#include "ntlm-encrypt.h"
+#include "ntlm-message.h"
+
+#define ntlmssp_buffer_data(message, buffer) \
+	__ntlmssp_buffer_data((message), &message->buffer)
+
+static inline const void *
+__ntlmssp_buffer_data(void * message, struct ntlmssp_buffer *buffer)
+{
+	return ((char *) message) + read_le32(&buffer->offset);
+}
+
+#define ntlmssp_buffer_length(message, buffer) \
+	__ntlmssp_buffer_length(&message->buffer)
+
+static inline unsigned int __ntlmssp_buffer_length(struct ntlmssp_buffer *buffer)
+{
+	return read_le16(&buffer->length);
+}
+
+#define ntlmssp_t_str(message, buffer) \
+	__ntlmssp_t_str((message), &message->buffer)
+
+const char * __ntlmssp_t_str(const void *message,
+			     struct ntlmssp_buffer *buffer);
+
+#endif
--- a/src/lib/Makefile.am	Wed Jul 28 18:21:28 2004 +0300
+++ b/src/lib/Makefile.am	Wed Jul 28 18:39:29 2004 +0300
@@ -32,6 +32,7 @@
 	ioloop-select.c \
 	lib.c \
 	lib-signals.c \
+	md4.c \
 	md5.c \
 	mempool-alloconly.c \
 	mempool-datastack.c \
@@ -92,6 +93,7 @@
 	lib.h \
 	lib-signals.h \
 	macros.h \
+	md4.h \
 	md5.h \
 	mempool.h \
 	mkdir-parents.h \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/md4.c	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,266 @@
+/*
+ * MD4 (RFC-1320) message digest.
+ * Modified from MD5 code by Andrey Panin <pazke@donpac.ru>
+ *
+ * Written by Solar Designer <solar@openwall.com> in 2001, and placed in
+ * the public domain.  There's absolutely no warranty.
+ *
+ * This differs from Colin Plumb's older public domain implementation in
+ * that no 32-bit integer data type is required, there's no compile-time
+ * endianness configuration, and the function prototypes match OpenSSL's.
+ * The primary goals are portability and ease of use.
+ *
+ * This implementation is meant to be fast, but not as fast as possible.
+ * Some known optimizations are not included to reduce source code size
+ * and avoid compile-time configuration.
+ */
+
+#include "lib.h"
+#include "safe-memset.h"
+#include "md4.h"
+
+/*
+ * The basic MD4 functions.
+ */
+#define F(x, y, z)	((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z)	(((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z)	((x) ^ (y) ^ (z))
+
+/*
+ * The MD4 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, s) \
+	(a) += f((b), (c), (d)) + (x);	 \
+	(a) = ((a) << (s)) | ((a) >> (32 - (s)))
+
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures which tolerate unaligned
+ * memory accesses is just an optimization.  Nothing will break if it
+ * doesn't work.
+ */
+#if defined(__i386__) || defined(__vax__)
+#define SET(n) \
+	(*(const uint_fast32_t *)&ptr[(n) * 4])
+#define GET(n) \
+	SET(n)
+#else
+#define SET(n) \
+	(ctx->block[(n)] = \
+	(uint_fast32_t)ptr[(n) * 4] | \
+	((uint_fast32_t)ptr[(n) * 4 + 1] << 8) | \
+	((uint_fast32_t)ptr[(n) * 4 + 2] << 16) | \
+	((uint_fast32_t)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+	(ctx->block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update
+ * the bit counters.  There're no alignment requirements.
+ */
+static const void *body(struct md4_context *ctx, const void *data, size_t size)
+{
+	const unsigned char *ptr;
+	uint_fast32_t a, b, c, d;
+	uint_fast32_t saved_a, saved_b, saved_c, saved_d;
+
+	ptr = data;
+
+	a = ctx->a;
+	b = ctx->b;
+	c = ctx->c;
+	d = ctx->d;
+
+	do {
+		saved_a = a;
+		saved_b = b;
+		saved_c = c;
+		saved_d = d;
+
+/* Round 1 */
+		STEP(F, a, b, c, d, SET( 0),  3);
+		STEP(F, d, a, b, c, SET( 1),  7);
+		STEP(F, c, d, a, b, SET( 2), 11);
+		STEP(F, b, c, d, a, SET( 3), 19);
+
+		STEP(F, a, b, c, d, SET( 4),  3);
+		STEP(F, d, a, b, c, SET( 5),  7);
+		STEP(F, c, d, a, b, SET( 6), 11);
+		STEP(F, b, c, d, a, SET( 7), 19);
+
+		STEP(F, a, b, c, d, SET( 8),  3);
+		STEP(F, d, a, b, c, SET( 9),  7);
+		STEP(F, c, d, a, b, SET(10), 11);
+		STEP(F, b, c, d, a, SET(11), 19);
+
+		STEP(F, a, b, c, d, SET(12),  3);
+		STEP(F, d, a, b, c, SET(13),  7);
+		STEP(F, c, d, a, b, SET(14), 11);
+		STEP(F, b, c, d, a, SET(15), 19);
+/* Round 2 */
+		STEP(G, a, b, c, d, GET( 0) + 0x5A827999,  3);
+		STEP(G, d, a, b, c, GET( 4) + 0x5A827999,  5);
+		STEP(G, c, d, a, b, GET( 8) + 0x5A827999,  9);
+		STEP(G, b, c, d, a, GET(12) + 0x5A827999, 13);
+
+		STEP(G, a, b, c, d, GET( 1) + 0x5A827999,  3);
+		STEP(G, d, a, b, c, GET( 5) + 0x5A827999,  5);
+		STEP(G, c, d, a, b, GET( 9) + 0x5A827999,  9);
+		STEP(G, b, c, d, a, GET(13) + 0x5A827999, 13);
+
+		STEP(G, a, b, c, d, GET( 2) + 0x5A827999,  3);
+		STEP(G, d, a, b, c, GET( 6) + 0x5A827999,  5);
+		STEP(G, c, d, a, b, GET(10) + 0x5A827999,  9);
+		STEP(G, b, c, d, a, GET(14) + 0x5A827999, 13);
+
+		STEP(G, a, b, c, d, GET( 3) + 0x5A827999,  3);
+		STEP(G, d, a, b, c, GET( 7) + 0x5A827999,  5);
+		STEP(G, c, d, a, b, GET(11) + 0x5A827999,  9);
+		STEP(G, b, c, d, a, GET(15) + 0x5A827999, 13);
+/* Round 3 */
+		STEP(H, a, b, c, d, GET( 0) + 0x6ED9EBA1,  3);
+		STEP(H, d, a, b, c, GET( 8) + 0x6ED9EBA1,  9);
+		STEP(H, c, d, a, b, GET( 4) + 0x6ED9EBA1, 11);
+		STEP(H, b, c, d, a, GET(12) + 0x6ED9EBA1, 15);
+
+		STEP(H, a, b, c, d, GET( 2) + 0x6ED9EBA1,  3);
+		STEP(H, d, a, b, c, GET(10) + 0x6ED9EBA1,  9);
+		STEP(H, c, d, a, b, GET( 6) + 0x6ED9EBA1, 11);
+		STEP(H, b, c, d, a, GET(14) + 0x6ED9EBA1, 15);
+
+		STEP(H, a, b, c, d, GET( 1) + 0x6ED9EBA1,  3);
+		STEP(H, d, a, b, c, GET( 9) + 0x6ED9EBA1,  9);
+		STEP(H, c, d, a, b, GET( 5) + 0x6ED9EBA1, 11);
+		STEP(H, b, c, d, a, GET(13) + 0x6ED9EBA1, 15);
+
+		STEP(H, a, b, c, d, GET( 3) + 0x6ED9EBA1,  3);
+		STEP(H, d, a, b, c, GET(11) + 0x6ED9EBA1,  9);
+		STEP(H, c, d, a, b, GET( 7) + 0x6ED9EBA1, 11);
+		STEP(H, b, c, d, a, GET(15) + 0x6ED9EBA1, 15);
+
+		a += saved_a;
+		b += saved_b;
+		c += saved_c;
+		d += saved_d;
+
+		ptr += 64;
+	} while (size -= 64);
+
+	ctx->a = a;
+	ctx->b = b;
+	ctx->c = c;
+	ctx->d = d;
+
+	return ptr;
+}
+
+void md4_init(struct md4_context *ctx)
+{
+	ctx->a = 0x67452301;
+	ctx->b = 0xefcdab89;
+	ctx->c = 0x98badcfe;
+	ctx->d = 0x10325476;
+
+	ctx->lo = 0;
+	ctx->hi = 0;
+}
+
+void md4_update(struct md4_context *ctx, const void *data, size_t size)
+{
+	/* @UNSAFE */
+	uint_fast32_t saved_lo;
+	unsigned long used, free;
+
+	saved_lo = ctx->lo;
+	if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+		ctx->hi++;
+	ctx->hi += size >> 29;
+
+	used = saved_lo & 0x3f;
+
+	if (used) {
+		free = 64 - used;
+
+		if (size < free) {
+			memcpy(&ctx->buffer[used], data, size);
+			return;
+		}
+
+		memcpy(&ctx->buffer[used], data, free);
+		data = (const unsigned char *) data + free;
+		size -= free;
+		body(ctx, ctx->buffer, 64);
+	}
+
+	if (size >= 64) {
+		data = body(ctx, data, size & ~(unsigned long)0x3f);
+		size &= 0x3f;
+	}
+
+	memcpy(ctx->buffer, data, size);
+}
+
+void md4_final(struct md4_context *ctx, unsigned char result[16])
+{
+	/* @UNSAFE */
+	unsigned long used, free;
+
+	used = ctx->lo & 0x3f;
+
+	ctx->buffer[used++] = 0x80;
+
+	free = 64 - used;
+
+	if (free < 8) {
+		memset(&ctx->buffer[used], 0, free);
+		body(ctx, ctx->buffer, 64);
+		used = 0;
+		free = 64;
+	}
+
+	memset(&ctx->buffer[used], 0, free - 8);
+
+	ctx->lo <<= 3;
+	ctx->buffer[56] = ctx->lo;
+	ctx->buffer[57] = ctx->lo >> 8;
+	ctx->buffer[58] = ctx->lo >> 16;
+	ctx->buffer[59] = ctx->lo >> 24;
+	ctx->buffer[60] = ctx->hi;
+	ctx->buffer[61] = ctx->hi >> 8;
+	ctx->buffer[62] = ctx->hi >> 16;
+	ctx->buffer[63] = ctx->hi >> 24;
+
+	body(ctx, ctx->buffer, 64);
+
+	result[0] = ctx->a;
+	result[1] = ctx->a >> 8;
+	result[2] = ctx->a >> 16;
+	result[3] = ctx->a >> 24;
+	result[4] = ctx->b;
+	result[5] = ctx->b >> 8;
+	result[6] = ctx->b >> 16;
+	result[7] = ctx->b >> 24;
+	result[8] = ctx->c;
+	result[9] = ctx->c >> 8;
+	result[10] = ctx->c >> 16;
+	result[11] = ctx->c >> 24;
+	result[12] = ctx->d;
+	result[13] = ctx->d >> 8;
+	result[14] = ctx->d >> 16;
+	result[15] = ctx->d >> 24;
+
+	safe_memset(ctx, 0, sizeof(*ctx));
+}
+
+void md4_get_digest(const void *data, size_t size, unsigned char result[16])
+{
+	struct md4_context ctx;
+
+	md4_init(&ctx);
+	md4_update(&ctx, data, size);
+	md4_final(&ctx, result);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/md4.h	Wed Jul 28 18:39:29 2004 +0300
@@ -0,0 +1,25 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security,
+ * Inc. MD4 Message-Digest Algorithm.
+ *
+ * Written by Solar Designer <solar@openwall.com> in 2001, and placed in
+ * the public domain.  See md4.c for more information.
+ */
+
+#ifndef __MD4_H
+#define __MD4_H
+
+struct md4_context {
+	uint_fast32_t lo, hi;
+	uint_fast32_t a, b, c, d;
+	unsigned char buffer[64];
+	uint_fast32_t block[16];
+};
+
+void md4_init(struct md4_context *ctx);
+void md4_update(struct md4_context *ctx, const void *data, size_t size);
+void md4_final(struct md4_context *ctx, unsigned char result[16]);
+
+void md4_get_digest(const void *data, size_t size, unsigned char result[16]);
+
+#endif