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)
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
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
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) \
mech-login.c \
mech-cram-md5.c \
mech-digest-md5.c \
+ mech-ntlm.c \
mech-apop.c \
mycrypt.c \
passdb.c \
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
+/*
+ * 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,
+};
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)
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, "
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);
}
return "HMAC-MD5";
case PASSDB_CREDENTIALS_DIGEST_MD5:
return "DIGEST-MD5";
+ case PASSDB_CREDENTIALS_NTLM:
+ return "NTLM";
}
return "??";
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
+
+#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));
+}
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 },
{ "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 }
};
/* 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
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
--- /dev/null
+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
+/*
+ * 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
+#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
+/*
+ * 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
+/*
+ * 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
+#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
+/*
+ * 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
+#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
+/*
+ * 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
+/*
+ * 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
+#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
+/*
+ * 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
+#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
ioloop-select.c \
lib.c \
lib-signals.c \
+ md4.c \
md5.c \
mempool-alloconly.c \
mempool-datastack.c \
lib.h \
lib-signals.h \
macros.h \
+ md4.h \
md5.h \
mempool.h \
mkdir-parents.h \
--- /dev/null
+/*
+ * 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
+/*
+ * 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