Mercurial > dovecot > core-2.2
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