From fc2f0dd93013d0037e2dd442eb0b71174ad8412d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 8 Nov 2017 19:08:54 +0000 Subject: [ntlm] Add support for NTLM authentication mechanism Signed-off-by: Michael Brown --- src/crypto/ntlm.c | 334 +++++++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/ntlm.h | 199 +++++++++++++++++++++++++++ src/tests/ntlm_test.c | 312 ++++++++++++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 5 files changed, 847 insertions(+) create mode 100644 src/crypto/ntlm.c create mode 100644 src/include/ipxe/ntlm.h create mode 100644 src/tests/ntlm_test.c (limited to 'src') diff --git a/src/crypto/ntlm.c b/src/crypto/ntlm.c new file mode 100644 index 00000000..870af213 --- /dev/null +++ b/src/crypto/ntlm.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * NT LAN Manager (NTLM) authentication + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** Negotiate message + * + * This message content is fixed since there is no need to specify the + * calling workstation name or domain name, and the set of flags is + * mandated by the MS-NLMP specification. + */ +const struct ntlm_negotiate ntlm_negotiate = { + .header = { + .magic = NTLM_MAGIC, + .type = cpu_to_le32 ( NTLM_NEGOTIATE ), + }, + .flags = cpu_to_le32 ( NTLM_NEGOTIATE_EXTENDED_SESSIONSECURITY | + NTLM_NEGOTIATE_ALWAYS_SIGN | + NTLM_NEGOTIATE_NTLM | + NTLM_REQUEST_TARGET | + NTLM_NEGOTIATE_UNICODE ), +}; + +/** + * Parse NTLM Challenge + * + * @v challenge Challenge message + * @v len Length of Challenge message + * @v info Challenge information to fill in + * @ret rc Return status code + */ +int ntlm_challenge ( struct ntlm_challenge *challenge, size_t len, + struct ntlm_challenge_info *info ) { + size_t offset; + + DBGC ( challenge, "NTLM challenge message:\n" ); + DBGC_HDA ( challenge, 0, challenge, len ); + + /* Sanity checks */ + if ( len < sizeof ( *challenge ) ) { + DBGC ( challenge, "NTLM underlength challenge (%zd bytes)\n", + len ); + return -EINVAL; + } + + /* Extract nonce */ + info->nonce = &challenge->nonce; + DBGC ( challenge, "NTLM challenge nonce:\n" ); + DBGC_HDA ( challenge, 0, info->nonce, sizeof ( *info->nonce ) ); + + /* Extract target information */ + info->len = le16_to_cpu ( challenge->info.len ); + offset = le32_to_cpu ( challenge->info.offset ); + if ( ( offset > len ) || + ( info->len > ( len - offset ) ) ) { + DBGC ( challenge, "NTLM target information outside " + "challenge\n" ); + DBGC_HDA ( challenge, 0, challenge, len ); + return -EINVAL; + } + info->target = ( ( ( void * ) challenge ) + offset ); + DBGC ( challenge, "NTLM challenge target information:\n" ); + DBGC_HDA ( challenge, 0, info->target, info->len ); + + return 0; +} + +/** + * Calculate NTLM verification key + * + * @v domain Domain name (or NULL) + * @v username User name (or NULL) + * @v password Password (or NULL) + * @v key Key to fill in + * + * This is the NTOWFv2() function as defined in MS-NLMP. + */ +void ntlm_key ( const char *domain, const char *username, + const char *password, struct ntlm_key *key ) { + struct digest_algorithm *md4 = &md4_algorithm; + struct digest_algorithm *md5 = &md5_algorithm; + union { + uint8_t md4[MD4_CTX_SIZE]; + uint8_t md5[MD5_CTX_SIZE]; + } ctx; + uint8_t digest[MD4_DIGEST_SIZE]; + size_t digest_len; + uint8_t c; + uint16_t wc; + + /* Use empty usernames/passwords if not specified */ + if ( ! domain ) + domain = ""; + if ( ! username ) + username = ""; + if ( ! password ) + password = ""; + + /* Construct MD4 digest of (Unicode) password */ + digest_init ( md4, ctx.md4 ); + while ( ( c = *(password++) ) ) { + wc = cpu_to_le16 ( c ); + digest_update ( md4, ctx.md4, &wc, sizeof ( wc ) ); + } + digest_final ( md4, ctx.md4, digest ); + + /* Construct HMAC-MD5 of (Unicode) upper-case username */ + digest_len = sizeof ( digest ); + hmac_init ( md5, ctx.md5, digest, &digest_len ); + while ( ( c = *(username++) ) ) { + wc = cpu_to_le16 ( toupper ( c ) ); + hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) ); + } + while ( ( c = *(domain++) ) ) { + wc = cpu_to_le16 ( c ); + hmac_update ( md5, ctx.md5, &wc, sizeof ( wc ) ); + } + hmac_final ( md5, ctx.md5, digest, &digest_len, key->raw ); + DBGC ( key, "NTLM key:\n" ); + DBGC_HDA ( key, 0, key, sizeof ( *key ) ); +} + +/** + * Construct NTLM responses + * + * @v info Challenge information + * @v key Verification key + * @v nonce Nonce, or NULL to use a random nonce + * @v lm LAN Manager response to fill in + * @v nt NT response to fill in + */ +void ntlm_response ( struct ntlm_challenge_info *info, struct ntlm_key *key, + struct ntlm_nonce *nonce, struct ntlm_lm_response *lm, + struct ntlm_nt_response *nt ) { + struct digest_algorithm *md5 = &md5_algorithm; + struct ntlm_nonce tmp_nonce; + uint8_t ctx[MD5_CTX_SIZE]; + size_t key_len = sizeof ( *key ); + unsigned int i; + + /* Generate random nonce, if needed */ + if ( ! nonce ) { + for ( i = 0 ; i < sizeof ( tmp_nonce ) ; i++ ) + tmp_nonce.raw[i] = random(); + nonce = &tmp_nonce; + } + + /* Construct LAN Manager response */ + memcpy ( &lm->nonce, nonce, sizeof ( lm->nonce ) ); + hmac_init ( md5, ctx, key->raw, &key_len ); + hmac_update ( md5, ctx, info->nonce, sizeof ( *info->nonce ) ); + hmac_update ( md5, ctx, &lm->nonce, sizeof ( lm->nonce ) ); + hmac_final ( md5, ctx, key->raw, &key_len, lm->digest ); + DBGC ( key, "NTLM LAN Manager response:\n" ); + DBGC_HDA ( key, 0, lm, sizeof ( *lm ) ); + + /* Construct NT response */ + memset ( nt, 0, sizeof ( *nt ) ); + nt->version = NTLM_VERSION_NTLMV2; + nt->high = NTLM_VERSION_NTLMV2; + memcpy ( &nt->nonce, nonce, sizeof ( nt->nonce ) ); + hmac_init ( md5, ctx, key->raw, &key_len ); + hmac_update ( md5, ctx, info->nonce, sizeof ( *info->nonce ) ); + hmac_update ( md5, ctx, &nt->version, + ( sizeof ( *nt ) - + offsetof ( typeof ( *nt ), version ) ) ); + hmac_update ( md5, ctx, info->target, info->len ); + hmac_update ( md5, ctx, &nt->zero, sizeof ( nt->zero ) ); + hmac_final ( md5, ctx, key->raw, &key_len, nt->digest ); + DBGC ( key, "NTLM NT response prefix:\n" ); + DBGC_HDA ( key, 0, nt, sizeof ( *nt ) ); +} + +/** + * Append data to NTLM message + * + * @v header Message header, or NULL to only calculate next payload + * @v data Data descriptor + * @v payload Data payload + * @v len Length of data + * @ret payload Next data payload + */ +static void * ntlm_append ( struct ntlm_header *header, struct ntlm_data *data, + void *payload, size_t len ) { + + /* Populate data descriptor */ + if ( header ) { + data->offset = cpu_to_le32 ( payload - ( ( void * ) header ) ); + data->len = data->max_len = cpu_to_le16 ( len ); + } + + return ( payload + len ); +} + +/** + * Append Unicode string data to NTLM message + * + * @v header Message header, or NULL to only calculate next payload + * @v data Data descriptor + * @v payload Data payload + * @v string String to append, or NULL + * @ret payload Next data payload + */ +static void * ntlm_append_string ( struct ntlm_header *header, + struct ntlm_data *data, void *payload, + const char *string ) { + uint16_t *tmp = payload; + uint8_t c; + + /* Convert string to Unicode */ + for ( tmp = payload ; ( string && ( c = *(string++) ) ) ; tmp++ ) { + if ( header ) + *tmp = cpu_to_le16 ( c ); + } + + /* Append string data */ + return ntlm_append ( header, data, payload, + ( ( ( void * ) tmp ) - payload ) ); +} + +/** + * Construct NTLM Authenticate message + * + * @v info Challenge information + * @v domain Domain name, or NULL + * @v username User name, or NULL + * @v workstation Workstation name, or NULL + * @v lm LAN Manager response + * @v nt NT response + * @v auth Message to fill in, or NULL to only calculate length + * @ret len Length of message + */ +size_t ntlm_authenticate ( struct ntlm_challenge_info *info, const char *domain, + const char *username, const char *workstation, + struct ntlm_lm_response *lm, + struct ntlm_nt_response *nt, + struct ntlm_authenticate *auth ) { + void *tmp; + size_t nt_len; + size_t len; + + /* Construct response header */ + if ( auth ) { + memset ( auth, 0, sizeof ( *auth ) ); + memcpy ( auth->header.magic, ntlm_negotiate.header.magic, + sizeof ( auth->header.magic ) ); + auth->header.type = cpu_to_le32 ( NTLM_AUTHENTICATE ); + auth->flags = ntlm_negotiate.flags; + } + tmp = ( ( ( void * ) auth ) + sizeof ( *auth ) ); + + /* Construct LAN Manager response */ + if ( auth ) + memcpy ( tmp, lm, sizeof ( *lm ) ); + tmp = ntlm_append ( &auth->header, &auth->lm, tmp, sizeof ( *lm ) ); + + /* Construct NT response */ + nt_len = ( sizeof ( *nt ) + info->len + sizeof ( nt->zero ) ); + if ( auth ) { + memcpy ( tmp, nt, sizeof ( *nt ) ); + memcpy ( ( tmp + sizeof ( *nt ) ), info->target, info->len ); + memset ( ( tmp + sizeof ( *nt ) + info->len ), 0, + sizeof ( nt->zero ) ); + } + tmp = ntlm_append ( &auth->header, &auth->nt, tmp, nt_len ); + + /* Populate domain, user, and workstation names */ + tmp = ntlm_append_string ( &auth->header, &auth->domain, tmp, domain ); + tmp = ntlm_append_string ( &auth->header, &auth->user, tmp, username ); + tmp = ntlm_append_string ( &auth->header, &auth->workstation, tmp, + workstation ); + + /* Calculate length */ + len = ( tmp - ( ( void * ) auth ) ); + if ( auth ) { + DBGC ( auth, "NTLM authenticate message:\n" ); + DBGC_HDA ( auth, 0, auth, len ); + } + + return len; +} + +/** + * Calculate NTLM Authenticate message length + * + * @v info Challenge information + * @v domain Domain name, or NULL + * @v username User name, or NULL + * @v workstation Workstation name, or NULL + * @ret len Length of Authenticate message + */ +size_t ntlm_authenticate_len ( struct ntlm_challenge_info *info, + const char *domain, const char *username, + const char *workstation ) { + + return ntlm_authenticate ( info, domain, username, workstation, + NULL, NULL, NULL ); +} diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 2dc182dd..e0d0dbc8 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -370,6 +370,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_entropy ( ERRFILE_OTHER | 0x004e0000 ) #define ERRFILE_cert_cmd ( ERRFILE_OTHER | 0x004f0000 ) #define ERRFILE_acpi_settings ( ERRFILE_OTHER | 0x00500000 ) +#define ERRFILE_ntlm ( ERRFILE_OTHER | 0x00510000 ) /** @} */ diff --git a/src/include/ipxe/ntlm.h b/src/include/ipxe/ntlm.h new file mode 100644 index 00000000..b0436c9a --- /dev/null +++ b/src/include/ipxe/ntlm.h @@ -0,0 +1,199 @@ +#ifndef _IPXE_NTLM_H +#define _IPXE_NTLM_H + +/** @file + * + * NT LAN Manager (NTLM) authentication + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** A message header */ +struct ntlm_header { + /** Magic signature */ + uint8_t magic[8]; + /** Message type */ + uint32_t type; +} __attribute__ (( packed )); + +/** Magic signature */ +#define NTLM_MAGIC { 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' } + +/** Message types */ +enum ntlm_type { + /** Negotiate message type */ + NTLM_NEGOTIATE = 0x00000001UL, + /** Challenge message type */ + NTLM_CHALLENGE = 0x00000002UL, + /** Authenticate message */ + NTLM_AUTHENTICATE = 0x00000003UL, +}; + +/** Negotiation flags */ +enum ntlm_flags { + /** Negotiate key exchange */ + NTLM_NEGOTIATE_KEY_EXCH = 0x20000000UL, + /** Negotiate extended security */ + NTLM_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000UL, + /** Negotiate always sign */ + NTLM_NEGOTIATE_ALWAYS_SIGN = 0x00008000UL, + /** Negotiate NTLM key */ + NTLM_NEGOTIATE_NTLM = 0x00000200UL, + /** Request target name and information */ + NTLM_REQUEST_TARGET = 0x00000004UL, + /** Negotiate Unicode character encoding */ + NTLM_NEGOTIATE_UNICODE = 0x00000001UL, +}; + +/** A version descriptor */ +struct ntlm_version { + /** Product major version */ + uint8_t major; + /** Product minor version */ + uint8_t minor; + /** Product build number */ + uint16_t build; + /** Reserved */ + uint8_t reserved[3]; + /** NTLMSSP revision */ + uint8_t revision; +} __attribute__ (( packed )); + +/** A nonce */ +struct ntlm_nonce { + /** Raw bytes */ + uint8_t raw[8]; +} __attribute__ (( packed )); + +/** A variable-length data descriptor */ +struct ntlm_data { + /** Length (in bytes) */ + uint16_t len; + /** Maximum length (in bytes) + * + * Should always be set equal to the length; this field is + * entirely superfluous. + */ + uint16_t max_len; + /** Offset from start of message header */ + uint32_t offset; +} __attribute__ (( packed )); + +/** A Negotiate message */ +struct ntlm_negotiate { + /** Message header */ + struct ntlm_header header; + /** Negotiation flags */ + uint32_t flags; + /** Domain name */ + struct ntlm_data domain; + /** Workstation name */ + struct ntlm_data workstation; +} __attribute__ (( packed )); + +/** A Challenge message */ +struct ntlm_challenge { + /** Message header */ + struct ntlm_header header; + /** Target name */ + struct ntlm_data name; + /** Negotiation flags */ + uint32_t flags; + /** Server nonce */ + struct ntlm_nonce nonce; + /** Reserved */ + uint8_t reserved[8]; + /** Target information */ + struct ntlm_data info; +} __attribute__ (( packed )); + +/** An Authenticate message */ +struct ntlm_authenticate { + /** Message header */ + struct ntlm_header header; + /** LAN Manager response */ + struct ntlm_data lm; + /** NT response */ + struct ntlm_data nt; + /** Domain name */ + struct ntlm_data domain; + /** User name */ + struct ntlm_data user; + /** Workstation name */ + struct ntlm_data workstation; + /** Session key */ + struct ntlm_data session; + /** Negotiation flags */ + uint32_t flags; +} __attribute__ (( packed )); + +/** A LAN Manager response */ +struct ntlm_lm_response { + /** HMAC-MD5 digest */ + uint8_t digest[MD5_DIGEST_SIZE]; + /** Client nonce */ + struct ntlm_nonce nonce; +} __attribute__ (( packed )); + +/** An NT response */ +struct ntlm_nt_response { + /** HMAC-MD5 digest */ + uint8_t digest[MD5_DIGEST_SIZE]; + /** Response version */ + uint8_t version; + /** Highest response version */ + uint8_t high; + /** Reserved */ + uint8_t reserved_a[6]; + /** Current time */ + uint64_t time; + /** Client nonce */ + struct ntlm_nonce nonce; + /** Must be zero */ + uint32_t zero; +} __attribute__ (( packed )); + +/** NTLM version */ +#define NTLM_VERSION_NTLMV2 0x01 + +/** NTLM challenge information */ +struct ntlm_challenge_info { + /** Server nonce */ + struct ntlm_nonce *nonce; + /** Target information */ + void *target; + /** Length of target information */ + size_t len; +}; + +/** An NTLM verification key */ +struct ntlm_key { + /** Raw bytes */ + uint8_t raw[MD5_DIGEST_SIZE]; +}; + +extern const struct ntlm_negotiate ntlm_negotiate; +extern int ntlm_challenge ( struct ntlm_challenge *challenge, size_t len, + struct ntlm_challenge_info *info ); +extern void ntlm_key ( const char *domain, const char *username, + const char *password, struct ntlm_key *key ); +extern void ntlm_response ( struct ntlm_challenge_info *info, + struct ntlm_key *key, struct ntlm_nonce *nonce, + struct ntlm_lm_response *lm, + struct ntlm_nt_response *nt ); +extern size_t ntlm_authenticate ( struct ntlm_challenge_info *info, + const char *domain, const char *username, + const char *workstation, + struct ntlm_lm_response *lm, + struct ntlm_nt_response *nt, + struct ntlm_authenticate *auth ); +extern size_t ntlm_authenticate_len ( struct ntlm_challenge_info *info, + const char *domain, const char *username, + const char *workstation ); + +#endif /* _IPXE_NTLM_H */ diff --git a/src/tests/ntlm_test.c b/src/tests/ntlm_test.c new file mode 100644 index 00000000..65a8b8c6 --- /dev/null +++ b/src/tests/ntlm_test.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * NTLM authentication self-tests + * + * The test vectors are taken from the MS-NLMP specification document. + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include +#include + +/** A key generation test */ +struct ntlm_key_test { + /** Domain name (or NULL) */ + const char *domain; + /** User name (or NULL) */ + const char *username; + /** Password (or NULL) */ + const char *password; + /** Expected key */ + struct ntlm_key expected; +}; + +/** An authentication test */ +struct ntlm_authenticate_test { + /** Domain name (or NULL) */ + const char *domain; + /** User name (or NULL) */ + const char *username; + /** Password (or NULL) */ + const char *password; + /** Workstation (or NULL) */ + const char *workstation; + /** Nonce */ + struct ntlm_nonce nonce; + /** Challenge message */ + struct ntlm_challenge *challenge; + /** Length of Challenge message */ + size_t challenge_len; + /** Expected Authenticate message */ + struct ntlm_authenticate *expected; + /** Expected length of Authenticate message */ + size_t expected_len; +}; + +/** Define inline message data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define a key generation digest test */ +#define KEY_TEST( name, DOMAIN, USERNAME, PASSWORD, EXPECTED ) \ + static struct ntlm_key_test name = { \ + .domain = DOMAIN, \ + .username = USERNAME, \ + .password = PASSWORD, \ + .expected = { \ + .raw = EXPECTED, \ + }, \ + }; + +/** Define an authentication test */ +#define AUTHENTICATE_TEST( name, DOMAIN, USERNAME, PASSWORD, \ + WORKSTATION, NONCE, CHALLENGE, EXPECTED ) \ + static const uint8_t name ## _challenge[] = CHALLENGE; \ + static const uint8_t name ## _expected[] = EXPECTED; \ + static struct ntlm_authenticate_test name = { \ + .domain = DOMAIN, \ + .username = USERNAME, \ + .password = PASSWORD, \ + .workstation = WORKSTATION, \ + .nonce = { \ + .raw = NONCE, \ + }, \ + .challenge = ( ( void * ) name ## _challenge ), \ + .challenge_len = sizeof ( name ## _challenge ), \ + .expected = ( ( void * ) name ## _expected ), \ + .expected_len = sizeof ( name ## _expected ), \ + }; + +/** NTOWFv2() test from MS-NLMP specification */ +KEY_TEST ( msnlmp_ntowfv2, "Domain", "User", "Password", + DATA ( 0x0c, 0x86, 0x8a, 0x40, 0x3b, 0xfd, 0x7a, 0x93, 0xa3, 0x00, + 0x1e, 0xf2, 0x2e, 0xf0, 0x2e, 0x3f ) ); + +/** Authentication test from MS-NLMP specification */ +AUTHENTICATE_TEST ( msnlmp_authenticate, + "Domain", "User", "Password", "COMPUTER", + DATA ( 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa ), + DATA ( 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x33, 0x82, 0x8a, 0xe2, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, + 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x24, 0x00, 0x44, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x70, 0x17, 0x00, 0x00, 0x00, 0x0f, 0x53, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x02, 0x00, + 0x0c, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x00, 0x00 ), + DATA ( 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x54, 0x00, 0x84, 0x00, 0x00, 0x00, 0x0c, 0x00, + 0x0c, 0x00, 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x5c, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0xd8, 0x00, 0x00, 0x00, + 0x35, 0x82, 0x88, 0xe2, 0x05, 0x01, 0x28, 0x0a, 0x00, 0x00, + 0x00, 0x0f, 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x50, 0x00, + 0x55, 0x00, 0x54, 0x00, 0x45, 0x00, 0x52, 0x00, 0x86, 0xc3, + 0x50, 0x97, 0xac, 0x9c, 0xec, 0x10, 0x25, 0x54, 0x76, 0x4a, + 0x57, 0xcc, 0xcc, 0x19, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0x68, 0xcd, 0x0a, 0xb8, 0x51, 0xe5, 0x1c, 0x96, + 0xaa, 0xbc, 0x92, 0x7b, 0xeb, 0xef, 0x6a, 0x1c, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00, + 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, + 0x6e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x65, 0x00, + 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0xda, 0xd2, 0x54, + 0x4f, 0xc9, 0x79, 0x90, 0x94, 0xce, 0x1c, 0xe9, 0x0b, 0xc9, + 0xd0, 0x3e ) ); + +/** + * Report key generation test result + * + * @v test Key generation test + * @v file Test code file + * @v line Test code line + */ +static void ntlm_key_okx ( struct ntlm_key_test *test, + const char *file, unsigned int line ) { + struct ntlm_key key; + + ntlm_key ( test->domain, test->username, test->password, &key ); + okx ( memcmp ( &key, &test->expected, sizeof ( key ) ) == 0, + file, line ); +} +#define ntlm_key_ok( test ) \ + ntlm_key_okx ( test, __FILE__, __LINE__ ) + +/** + * Report NTLM variable-length data test result + * + * @v msg Message header + * @v msg_len Length of message + * @v data Variable-length data descriptor + * @v expected Expected message header + * @v expected_data Expected variable-length data descriptor + * @v field Field name + * @v file Test code file + * @v line Test code line + */ +static void ntlm_data_okx ( struct ntlm_header *msg, size_t msg_len, + struct ntlm_data *data, + struct ntlm_header *expected, + struct ntlm_data *expected_data, + const char *field, const char *file, + unsigned int line ) { + size_t offset; + size_t len; + void *raw; + void *expected_raw; + + /* Verify data lies within message */ + okx ( data->len == data->max_len, file, line ); + offset = le32_to_cpu ( data->offset ); + len = le16_to_cpu ( data->len ); + okx ( offset <= msg_len, file, line ); + okx ( len <= ( msg_len - offset ), file, line ); + + /* Verify content matches expected content */ + raw = ( ( ( void * ) msg ) + offset ); + expected_raw = ( ( ( void * ) expected ) + + le32_to_cpu ( expected_data->offset ) ); + DBGC ( msg, "NTLM %s expected:\n", field ); + DBGC_HDA ( msg, 0, expected_raw, le16_to_cpu ( expected_data->len ) ); + DBGC ( msg, "NTLM %s actual:\n", field ); + DBGC_HDA ( msg, 0, raw, len ); + okx ( data->len == expected_data->len, file, line ); + okx ( memcmp ( raw, expected_raw, len ) == 0, file, line ); +} +#define ntlm_data_ok( msg, msg_len, data, expected, expected_data ) \ + ntlm_data_okx ( msg, msg_len, data, expected, expected_data, \ + __FILE__, __LINE__ ) + +/** + * Report NTLM authentication test result + * + * @v test Authentication test + * @v file Test code file + * @v line Test code line + */ +static void ntlm_authenticate_okx ( struct ntlm_authenticate_test *test, + const char *file, unsigned int line ) { + struct ntlm_authenticate *expected = test->expected; + struct ntlm_challenge_info info; + struct ntlm_authenticate *auth; + struct ntlm_key key; + struct ntlm_lm_response lm; + struct ntlm_nt_response nt; + size_t len; + + /* Parse Challenge message */ + okx ( ntlm_challenge ( test->challenge, test->challenge_len, + &info ) == 0, file, line ); + + /* Generate key */ + ntlm_key ( test->domain, test->username, test->password, &key ); + + /* Generate responses */ + ntlm_response ( &info, &key, &test->nonce, &lm, &nt ); + + /* Allocate buffer for Authenticate message */ + len = ntlm_authenticate_len ( &info, test->domain, test->username, + test->workstation ); + okx ( len >= sizeof ( *auth ), file, line ); + auth = malloc ( len ); + okx ( auth != NULL, file, line ); + + /* Construct Authenticate message */ + okx ( ntlm_authenticate ( &info, test->domain, test->username, + test->workstation, &lm, &nt, auth ) == len, + file, line ); + + /* Verify header */ + okx ( memcmp ( &auth->header, &expected->header, + sizeof ( auth->header ) ) == 0, file, line ); + + /* Verify LAN Manager response */ + ntlm_data_okx ( &auth->header, len, &auth->lm, &expected->header, + &expected->lm, "LM", file, line ); + + /* Verify NT response */ + ntlm_data_okx ( &auth->header, len, &auth->nt, &expected->header, + &expected->nt, "NT", file, line ); + + /* Verify domain name */ + ntlm_data_okx ( &auth->header, len, &auth->domain, &expected->header, + &expected->domain, "domain", file, line ); + + /* Verify user name */ + ntlm_data_okx ( &auth->header, len, &auth->user, &expected->header, + &expected->user, "user", file, line ); + + /* Verify workstation name */ + ntlm_data_okx ( &auth->header, len, &auth->workstation, + &expected->header, &expected->workstation, + "workstation",file, line ); + + /* Verify session key */ + if ( auth->flags & NTLM_NEGOTIATE_KEY_EXCH ) { + ntlm_data_okx ( &auth->header, len, &auth->session, + &expected->header, &expected->session, + "session", file, line ); + } + + /* Free Authenticate message */ + free ( auth ); +} +#define ntlm_authenticate_ok( test ) \ + ntlm_authenticate_okx ( test, __FILE__, __LINE__ ) + +/** + * Perform NTLM self-test + * + */ +static void ntlm_test_exec ( void ) { + + /* Verify key generation */ + ntlm_key_ok ( &msnlmp_ntowfv2 ); + + /* Verify authentication response */ + ntlm_authenticate_ok ( &msnlmp_authenticate ); +} + +/** NTLM self-test */ +struct self_test ntlm_test __self_test = { + .name = "ntlm", + .exec = ntlm_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index a0c4ff5d..2e812d6f 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -72,3 +72,4 @@ REQUIRE_OBJECT ( iobuf_test ); REQUIRE_OBJECT ( bitops_test ); REQUIRE_OBJECT ( der_test ); REQUIRE_OBJECT ( pem_test ); +REQUIRE_OBJECT ( ntlm_test ); -- cgit v1.2.3-55-g7522