[dovecot-cvs] dovecot/src/lib-ntlm .cvsignore, NONE, 1.1 Makefile.am, NONE, 1.1 hmac-md5.c, NONE, 1.1 hmac-md5.h, NONE, 1.1 ntlm-byteorder.h, NONE, 1.1 ntlm-des.c, NONE, 1.1 ntlm-des.h, NONE, 1.1 ntlm-encrypt.c, NONE, 1.1 ntlm-encrypt.h, NONE, 1.1 ntlm-flags.h, NONE, 1.1 ntlm-message.c, NONE, 1.1 ntlm-message.h, NONE, 1.1 ntlm-types.h, NONE, 1.1 ntlm.h, NONE, 1.1

cras at dovecot.org cras at dovecot.org
Wed Jul 28 18:39:32 EEST 2004


Update of /home/cvs/dovecot/src/lib-ntlm
In directory talvi:/tmp/cvs-serv28916/src/lib-ntlm

Added Files:
	.cvsignore Makefile.am hmac-md5.c hmac-md5.h ntlm-byteorder.h 
	ntlm-des.c ntlm-des.h ntlm-encrypt.c ntlm-encrypt.h 
	ntlm-flags.h ntlm-message.c ntlm-message.h ntlm-types.h ntlm.h 
Log Message:
NTLM authentication. Patch by Andrey Panin



--- NEW FILE: .cvsignore ---
*.la
*.lo
*.o
.deps
.libs
Makefile
Makefile.in
so_locations

--- NEW FILE: Makefile.am ---
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

--- NEW FILE: hmac-md5.c ---
/*
 * HMAC-MD5 (RFC-2104) implementation.
 *
 * Copyright (c) 2004 Andrey Panin <pazke at 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);
}

--- NEW FILE: hmac-md5.h ---
#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__ */

--- NEW FILE: ntlm-byteorder.h ---
/*
 * Little-endian data access functions.
 *
 * Copyright (c) 2004 Andrey Panin <pazke at 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__ */

--- NEW FILE: ntlm-des.c ---
/*
 * 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 at 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;
}

--- NEW FILE: ntlm-des.h ---
#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__ */

--- NEW FILE: ntlm-encrypt.c ---
/*
 * NTLM and NTLMv2 hash generation.
 *
 * Copyright (c) 2004 Andrey Panin <pazke at 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);
}

--- NEW FILE: ntlm-encrypt.h ---
#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__ */

--- NEW FILE: ntlm-flags.h ---
/*
 * NTLM message flags.
 *
 * Copyright (c) 2004 Andrey Panin <pazke at 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__ */

--- NEW FILE: ntlm-message.c ---
/*
 * NTLM message handling.
 *
 * Copyright (c) 2004 Andrey Panin <pazke at 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;
}

--- NEW FILE: ntlm-message.h ---
#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__ */

--- NEW FILE: ntlm-types.h ---
/*
 * NTLM data structures.
 *
 * Copyright (c) 2004 Andrey Panin <pazke at 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__ */

--- NEW FILE: ntlm.h ---
#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



More information about the dovecot-cvs mailing list