From 98b174b8a17f79fd698609179d328ea9eb5f629b Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 12 Apr 2004 14:19:48 +0000 Subject: r177: Split ntlm_auth --diagnostics into a seperate file, so as not to clutter the main ntlm_auth program. It quite possibly should belong in smbtorture, but relies on the winbind client for now. Andrew Bartlett --- source/Makefile.in | 10 +- source/utils/ntlm_auth.c | 629 ++--------------------------------- source/utils/ntlm_auth.h | 27 ++ source/utils/ntlm_auth_diagnostics.c | 600 +++++++++++++++++++++++++++++++++ 4 files changed, 665 insertions(+), 601 deletions(-) create mode 100644 source/utils/ntlm_auth.h create mode 100644 source/utils/ntlm_auth_diagnostics.c diff --git a/source/Makefile.in b/source/Makefile.in index 51b32b44311..de7ee10bb40 100644 --- a/source/Makefile.in +++ b/source/Makefile.in @@ -653,7 +653,8 @@ TDBBACKUP_OBJ = tdb/tdbbackup.o tdb/tdbback.o lib/snprintf.o $(TDBBASE_OBJ) TDBDUMP_OBJ = tdb/tdbdump.o $(TDBBASE_OBJ) -NTLM_AUTH_OBJ = utils/ntlm_auth.o $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ) \ +NTLM_AUTH_OBJ1 = utils/ntlm_auth.o utils/ntlm_auth_diagnostics.o +NTLM_AUTH_OBJ = ${NTLM_AUTH_OBJ1} $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ) \ libsmb/asn1.o libsmb/spnego.o libsmb/clikrb5.o libads/kerberos.o \ libads/kerberos_verify.o $(SECRETS_OBJ) lib/server_mutex.o \ libads/authdata.o rpc_parse/parse_prs.o rpc_parse/parse_misc.o \ @@ -1365,7 +1366,7 @@ clean: delheaders python_clean # afterwards. proto_exists: include/proto.h include/wrepld_proto.h include/build_env.h \ nsswitch/winbindd_proto.h web/swat_proto.h \ - client/client_proto.h utils/net_proto.h smbd/build_options.c + client/client_proto.h utils/net_proto.h utils/ntlm_auth_proto.h smbd/build_options.c delheaders: @echo Removing prototype headers @@ -1413,6 +1414,11 @@ utils/net_proto.h: -h _NET_PROTO_H_ $(builddir)/utils/net_proto.h \ $(NET_OBJ1) +utils/ntlm_auth_proto.h: + @cd $(srcdir) && $(SHELL) $(MKPROTO_SH) $(AWK) \ + -h _NTLM_AUTH_PROTO_H_ $(builddir)/utils/ntlm_auth_proto.h \ + $(NTLM_AUTH_OBJ1) + # "make headers" or "make proto" calls a subshell because we need to # make sure these commands are executed in sequence even for a # parallel make. diff --git a/source/utils/ntlm_auth.c b/source/utils/ntlm_auth.c index 45a919c5841..45e37dc37ff 100644 --- a/source/utils/ntlm_auth.c +++ b/source/utils/ntlm_auth.c @@ -23,6 +23,7 @@ */ #include "includes.h" +#include "../utils/ntlm_auth.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND @@ -39,14 +40,6 @@ enum stdio_helper_mode { NUM_HELPER_MODES }; -enum ntlm_break { - BREAK_NONE, - BREAK_LM, - BREAK_NT, - NO_LM, - NO_NT -}; - typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode, char *buf, int length); @@ -81,10 +74,10 @@ static const struct { extern int winbindd_fd; -static const char *opt_username; -static const char *opt_domain; -static const char *opt_workstation; -static const char *opt_password; +const char *opt_username; +const char *opt_domain; +const char *opt_workstation; +const char *opt_password; static DATA_BLOB opt_challenge; static DATA_BLOB opt_lm_response; static DATA_BLOB opt_nt_response; @@ -124,7 +117,7 @@ static char winbind_separator(void) return sep; } -static const char *get_winbind_domain(void) +const char *get_winbind_domain(void) { struct winbindd_response response; @@ -149,7 +142,7 @@ static const char *get_winbind_domain(void) } -static const char *get_winbind_netbios_name(void) +const char *get_winbind_netbios_name(void) { struct winbindd_response response; @@ -175,6 +168,18 @@ static const char *get_winbind_netbios_name(void) } +DATA_BLOB get_challenge(void) +{ + static DATA_BLOB chal; + if (opt_challenge.length) + return opt_challenge; + + chal = data_blob(NULL, 8); + + generate_random_buffer(chal.data, chal.length, False); + return chal; +} + /* Copy of parse_domain_user from winbindd_util.c. Parse a string of the form DOMAIN/user into a domain and a user */ @@ -287,17 +292,17 @@ static BOOL check_plaintext_auth(const char *user, const char *pass, /* authenticate a user with an encrypted username/password */ -static NTSTATUS contact_winbind_auth_crap(const char *username, - const char *domain, - const char *workstation, - const DATA_BLOB *challenge, - const DATA_BLOB *lm_response, - const DATA_BLOB *nt_response, - uint32 flags, - uint8 lm_key[8], - uint8 user_session_key[16], - char **error_string, - char **unix_name) +NTSTATUS contact_winbind_auth_crap(const char *username, + const char *domain, + const char *workstation, + const DATA_BLOB *challenge, + const DATA_BLOB *lm_response, + const DATA_BLOB *nt_response, + uint32 flags, + uint8 lm_key[8], + uint8 user_session_key[16], + char **error_string, + char **unix_name) { NTSTATUS nt_status; NSS_STATUS result; @@ -1490,580 +1495,6 @@ static BOOL check_auth_crap(void) return True; } -/* - Authenticate a user with a challenge/response, checking session key - and valid authentication types -*/ - -static DATA_BLOB get_challenge(void) -{ - static DATA_BLOB chal; - if (opt_challenge.length) - return opt_challenge; - - chal = data_blob(NULL, 8); - - generate_random_buffer(chal.data, chal.length, False); - return chal; -} - -/* - * Test the normal 'LM and NTLM' combination - */ - -static BOOL test_lm_ntlm_broken(enum ntlm_break break_which) -{ - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB lm_response = data_blob(NULL, 24); - DATA_BLOB nt_response = data_blob(NULL, 24); - DATA_BLOB session_key = data_blob(NULL, 16); - - uchar lm_key[8]; - uchar user_session_key[16]; - uchar lm_hash[16]; - uchar nt_hash[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; - - ZERO_STRUCT(lm_key); - ZERO_STRUCT(user_session_key); - - flags |= WBFLAG_PAM_LMKEY; - flags |= WBFLAG_PAM_USER_SESSION_KEY; - - SMBencrypt(opt_password,chall.data,lm_response.data); - E_deshash(opt_password, lm_hash); - - SMBNTencrypt(opt_password,chall.data,nt_response.data); - - E_md4hash(opt_password, nt_hash); - SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); - - switch (break_which) { - case BREAK_NONE: - break; - case BREAK_LM: - lm_response.data[0]++; - break; - case BREAK_NT: - nt_response.data[0]++; - break; - case NO_LM: - data_blob_free(&lm_response); - break; - case NO_NT: - data_blob_free(&nt_response); - break; - } - - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - &lm_response, - &nt_response, - flags, - lm_key, - user_session_key, - &error_string, NULL); - - data_blob_free(&lm_response); - - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return break_which == BREAK_NT; - } - - if (memcmp(lm_hash, lm_key, - sizeof(lm_key)) != 0) { - DEBUG(1, ("LM Key does not match expectations!\n")); - DEBUG(1, ("lm_key:\n")); - dump_data(1, (const char *)lm_key, 8); - DEBUG(1, ("expected:\n")); - dump_data(1, (const char *)lm_hash, 8); - pass = False; - } - - if (break_which == NO_NT) { - if (memcmp(lm_hash, user_session_key, - 8) != 0) { - DEBUG(1, ("NT Session Key does not match expectations (should be LM hash)!\n")); - DEBUG(1, ("user_session_key:\n")); - dump_data(1, (const char *)user_session_key, sizeof(user_session_key)); - DEBUG(1, ("expected:\n")); - dump_data(1, (const char *)lm_hash, sizeof(lm_hash)); - pass = False; - } - } else { - if (memcmp(session_key.data, user_session_key, - sizeof(user_session_key)) != 0) { - DEBUG(1, ("NT Session Key does not match expectations!\n")); - DEBUG(1, ("user_session_key:\n")); - dump_data(1, (const char *)user_session_key, 16); - DEBUG(1, ("expected:\n")); - dump_data(1, (const char *)session_key.data, session_key.length); - pass = False; - } - } - return pass; -} - -/* - * Test LM authentication, no NT response supplied - */ - -static BOOL test_lm(void) -{ - - return test_lm_ntlm_broken(NO_NT); -} - -/* - * Test the NTLM response only, no LM. - */ - -static BOOL test_ntlm(void) -{ - return test_lm_ntlm_broken(NO_LM); -} - -/* - * Test the NTLM response only, but in the LM field. - */ - -static BOOL test_ntlm_in_lm(void) -{ - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB nt_response = data_blob(NULL, 24); - - uchar lm_key[8]; - uchar lm_hash[16]; - uchar user_session_key[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; - - ZERO_STRUCT(user_session_key); - - flags |= WBFLAG_PAM_LMKEY; - flags |= WBFLAG_PAM_USER_SESSION_KEY; - - SMBNTencrypt(opt_password,chall.data,nt_response.data); - - E_deshash(opt_password, lm_hash); - - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - &nt_response, - NULL, - flags, - lm_key, - user_session_key, - &error_string, NULL); - - data_blob_free(&nt_response); - - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return False; - } - - if (memcmp(lm_hash, lm_key, - sizeof(lm_key)) != 0) { - DEBUG(1, ("LM Key does not match expectations!\n")); - DEBUG(1, ("lm_key:\n")); - dump_data(1, (const char *)lm_key, 8); - DEBUG(1, ("expected:\n")); - dump_data(1, (const char *)lm_hash, 8); - pass = False; - } - if (memcmp(lm_hash, user_session_key, 8) != 0) { - DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n")); - DEBUG(1, ("user_session_key:\n")); - dump_data(1, (const char *)user_session_key, 16); - DEBUG(1, ("expected:\n")); - dump_data(1, (const char *)lm_hash, 8); - pass = False; - } - return pass; -} - -/* - * Test the NTLM response only, but in the both the NT and LM fields. - */ - -static BOOL test_ntlm_in_both(void) -{ - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB nt_response = data_blob(NULL, 24); - DATA_BLOB session_key = data_blob(NULL, 16); - - char lm_key[8]; - char lm_hash[16]; - char user_session_key[16]; - char nt_hash[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; - - ZERO_STRUCT(lm_key); - ZERO_STRUCT(user_session_key); - - flags |= WBFLAG_PAM_LMKEY; - flags |= WBFLAG_PAM_USER_SESSION_KEY; - - SMBNTencrypt(opt_password,chall.data,nt_response.data); - E_md4hash(opt_password, (unsigned char *)nt_hash); - SMBsesskeygen_ntv1((const unsigned char *)nt_hash, NULL, session_key.data); - - E_deshash(opt_password, (unsigned char *)lm_hash); - - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - &nt_response, - &nt_response, - flags, - (unsigned char *)lm_key, - (unsigned char *)user_session_key, - &error_string, NULL); - - data_blob_free(&nt_response); - - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return False; - } - - if (memcmp(lm_hash, lm_key, - sizeof(lm_key)) != 0) { - DEBUG(1, ("LM Key does not match expectations!\n")); - DEBUG(1, ("lm_key:\n")); - dump_data(1, lm_key, 8); - DEBUG(1, ("expected:\n")); - dump_data(1, lm_hash, 8); - pass = False; - } - if (memcmp(session_key.data, user_session_key, - sizeof(user_session_key)) != 0) { - DEBUG(1, ("NT Session Key does not match expectations!\n")); - DEBUG(1, ("user_session_key:\n")); - dump_data(1, user_session_key, 16); - DEBUG(1, ("expected:\n")); - dump_data(1, (const char *)session_key.data, session_key.length); - pass = False; - } - - - return pass; -} - -/* - * Test the NTLMv2 and LMv2 responses - */ - -static BOOL test_lmv2_ntlmv2_broken(enum ntlm_break break_which) -{ - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB ntlmv2_response = data_blob(NULL, 0); - DATA_BLOB lmv2_response = data_blob(NULL, 0); - DATA_BLOB ntlmv2_session_key = data_blob(NULL, 0); - DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain()); - - uchar user_session_key[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; - - ZERO_STRUCT(user_session_key); - - flags |= WBFLAG_PAM_USER_SESSION_KEY; - - if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall, - &names_blob, - &lmv2_response, &ntlmv2_response, - &ntlmv2_session_key)) { - data_blob_free(&names_blob); - return False; - } - data_blob_free(&names_blob); - - switch (break_which) { - case BREAK_NONE: - break; - case BREAK_LM: - lmv2_response.data[0]++; - break; - case BREAK_NT: - ntlmv2_response.data[0]++; - break; - case NO_LM: - data_blob_free(&lmv2_response); - break; - case NO_NT: - data_blob_free(&ntlmv2_response); - break; - } - - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - &lmv2_response, - &ntlmv2_response, - flags, - NULL, - user_session_key, - &error_string, NULL); - - data_blob_free(&lmv2_response); - data_blob_free(&ntlmv2_response); - - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return break_which == BREAK_NT; - } - - if (break_which != NO_NT && break_which != BREAK_NT && memcmp(ntlmv2_session_key.data, user_session_key, - sizeof(user_session_key)) != 0) { - DEBUG(1, ("USER (NTLMv2) Session Key does not match expectations!\n")); - DEBUG(1, ("user_session_key:\n")); - dump_data(1, (const char *)user_session_key, 16); - DEBUG(1, ("expected:\n")); - dump_data(1, (const char *)ntlmv2_session_key.data, ntlmv2_session_key.length); - pass = False; - } - return pass; -} - -/* - * Test the NTLMv2 and LMv2 responses - */ - -static BOOL test_lmv2_ntlmv2(void) -{ - return test_lmv2_ntlmv2_broken(BREAK_NONE); -} - -/* - * Test the LMv2 response only - */ - -static BOOL test_lmv2(void) -{ - return test_lmv2_ntlmv2_broken(NO_NT); -} - -/* - * Test the NTLMv2 response only - */ - -static BOOL test_ntlmv2(void) -{ - return test_lmv2_ntlmv2_broken(NO_LM); -} - -static BOOL test_lm_ntlm(void) -{ - return test_lm_ntlm_broken(BREAK_NONE); -} - -static BOOL test_ntlm_lm_broken(void) -{ - return test_lm_ntlm_broken(BREAK_LM); -} - -static BOOL test_ntlm_ntlm_broken(void) -{ - return test_lm_ntlm_broken(BREAK_NT); -} - -static BOOL test_ntlmv2_lmv2_broken(void) -{ - return test_lmv2_ntlmv2_broken(BREAK_LM); -} - -static BOOL test_ntlmv2_ntlmv2_broken(void) -{ - return test_lmv2_ntlmv2_broken(BREAK_NT); -} - -static BOOL test_plaintext(enum ntlm_break break_which) -{ - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB nt_response = data_blob(NULL, 0); - DATA_BLOB lm_response = data_blob(NULL, 0); - char *password; - - uchar user_session_key[16]; - uchar lm_key[16]; - static const uchar zeros[8]; - DATA_BLOB chall = data_blob(zeros, sizeof(zeros)); - char *error_string; - - ZERO_STRUCT(user_session_key); - - flags |= WBFLAG_PAM_LMKEY; - flags |= WBFLAG_PAM_USER_SESSION_KEY; - - if ((push_ucs2_allocate((smb_ucs2_t **)&nt_response.data, opt_password)) == -1) { - DEBUG(0, ("push_ucs2_allocate failed!\n")); - exit(1); - } - - nt_response.length = strlen_w(((void *)nt_response.data))*sizeof(smb_ucs2_t); - - password = strdup_upper(opt_password); - - if ((convert_string_allocate(NULL, CH_UNIX, - CH_DOS, password, - strlen(password)+1, - (void**)&lm_response.data,True)) == -1) { - DEBUG(0, ("push_ascii_allocate failed!\n")); - exit(1); - } - - SAFE_FREE(password); - - lm_response.length = strlen(lm_response.data); - - switch (break_which) { - case BREAK_NONE: - break; - case BREAK_LM: - lm_response.data[0]++; - break; - case BREAK_NT: - nt_response.data[0]++; - break; - case NO_LM: - SAFE_FREE(lm_response.data); - lm_response.length = 0; - break; - case NO_NT: - SAFE_FREE(nt_response.data); - nt_response.length = 0; - break; - } - - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - &lm_response, - &nt_response, - flags, - lm_key, - user_session_key, - &error_string, NULL); - - SAFE_FREE(nt_response.data); - SAFE_FREE(lm_response.data); - data_blob_free(&chall); - - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return break_which == BREAK_NT; - } - - return break_which != BREAK_NT; -} - -static BOOL test_plaintext_none_broken(void) { - return test_plaintext(BREAK_NONE); -} - -static BOOL test_plaintext_lm_broken(void) { - return test_plaintext(BREAK_LM); -} - -static BOOL test_plaintext_nt_broken(void) { - return test_plaintext(BREAK_NT); -} - -static BOOL test_plaintext_nt_only(void) { - return test_plaintext(NO_LM); -} - -static BOOL test_plaintext_lm_only(void) { - return test_plaintext(NO_NT); -} - -/* - Tests: - - - LM only - - NT and LM - - NT - - NT in LM field - - NT in both fields - - NTLMv2 - - NTLMv2 and LMv2 - - LMv2 - - plaintext tests (in challenge-response feilds) - - check we get the correct session key in each case - check what values we get for the LM session key - -*/ - -static const struct ntlm_tests { - BOOL (*fn)(void); - const char *name; -} test_table[] = { - {test_lm, "LM"}, - {test_lm_ntlm, "LM and NTLM"}, - {test_ntlm, "NTLM"}, - {test_ntlm_in_lm, "NTLM in LM"}, - {test_ntlm_in_both, "NTLM in both"}, - {test_ntlmv2, "NTLMv2"}, - {test_lmv2_ntlmv2, "NTLMv2 and LMv2"}, - {test_lmv2, "LMv2"}, - {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"}, - {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"}, - {test_ntlm_lm_broken, "NTLM and LM, LM broken"}, - {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"}, - {test_plaintext_none_broken, "Plaintext"}, - {test_plaintext_lm_broken, "Plaintext LM broken"}, - {test_plaintext_nt_broken, "Plaintext NT broken"}, - {test_plaintext_nt_only, "Plaintext NT only"}, - {test_plaintext_lm_only, "Plaintext LM only"} -}; - -static BOOL diagnose_ntlm_auth(void) -{ - unsigned int i; - BOOL pass = True; - - for (i=0; test_table[i].fn; i++) { - if (!test_table[i].fn()) { - DEBUG(1, ("Test %s failed!\n", test_table[i].name)); - pass = False; - } - } - - return pass; -} - /* Main program */ enum { diff --git a/source/utils/ntlm_auth.h b/source/utils/ntlm_auth.h new file mode 100644 index 00000000000..a96067fbbb8 --- /dev/null +++ b/source/utils/ntlm_auth.h @@ -0,0 +1,27 @@ +/* + Samba Unix/Linux NTLM authentication tool + + Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) + + 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 + (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "../utils/ntlm_auth_proto.h" + +/* Some of the popt variables are needed in the diagnostics code */ +extern const char *opt_username; +extern const char *opt_domain; +extern const char *opt_workstation; +extern const char *opt_password; + diff --git a/source/utils/ntlm_auth_diagnostics.c b/source/utils/ntlm_auth_diagnostics.c new file mode 100644 index 00000000000..40c627588de --- /dev/null +++ b/source/utils/ntlm_auth_diagnostics.c @@ -0,0 +1,600 @@ +/* + Unix SMB/CIFS implementation. + + Winbind status program. + + Copyright (C) Tim Potter 2000-2003 + Copyright (C) Andrew Bartlett 2003-2004 + Copyright (C) Francesco Chemolli 2000 + + 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 + (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "../utils/ntlm_auth.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +enum ntlm_break { + BREAK_NONE, + BREAK_LM, + BREAK_NT, + NO_LM, + NO_NT +}; + +/* + Authenticate a user with a challenge/response, checking session key + and valid authentication types +*/ + +/* + * Test the normal 'LM and NTLM' combination + */ + +static BOOL test_lm_ntlm_broken(enum ntlm_break break_which) +{ + BOOL pass = True; + NTSTATUS nt_status; + uint32 flags = 0; + DATA_BLOB lm_response = data_blob(NULL, 24); + DATA_BLOB nt_response = data_blob(NULL, 24); + DATA_BLOB session_key = data_blob(NULL, 16); + + uchar lm_key[8]; + uchar user_session_key[16]; + uchar lm_hash[16]; + uchar nt_hash[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(user_session_key); + + flags |= WBFLAG_PAM_LMKEY; + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + SMBencrypt(opt_password,chall.data,lm_response.data); + E_deshash(opt_password, lm_hash); + + SMBNTencrypt(opt_password,chall.data,nt_response.data); + + E_md4hash(opt_password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); + + switch (break_which) { + case BREAK_NONE: + break; + case BREAK_LM: + lm_response.data[0]++; + break; + case BREAK_NT: + nt_response.data[0]++; + break; + case NO_LM: + data_blob_free(&lm_response); + break; + case NO_NT: + data_blob_free(&nt_response); + break; + } + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &lm_response, + &nt_response, + flags, + lm_key, + user_session_key, + &error_string, NULL); + + data_blob_free(&lm_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return break_which == BREAK_NT; + } + + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, (const char *)lm_key, 8); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)lm_hash, 8); + pass = False; + } + + if (break_which == NO_NT) { + if (memcmp(lm_hash, user_session_key, + 8) != 0) { + DEBUG(1, ("NT Session Key does not match expectations (should be LM hash)!\n")); + DEBUG(1, ("user_session_key:\n")); + dump_data(1, (const char *)user_session_key, sizeof(user_session_key)); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)lm_hash, sizeof(lm_hash)); + pass = False; + } + } else { + if (memcmp(session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + DEBUG(1, ("NT Session Key does not match expectations!\n")); + DEBUG(1, ("user_session_key:\n")); + dump_data(1, (const char *)user_session_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)session_key.data, session_key.length); + pass = False; + } + } + return pass; +} + +/* + * Test LM authentication, no NT response supplied + */ + +static BOOL test_lm(void) +{ + + return test_lm_ntlm_broken(NO_NT); +} + +/* + * Test the NTLM response only, no LM. + */ + +static BOOL test_ntlm(void) +{ + return test_lm_ntlm_broken(NO_LM); +} + +/* + * Test the NTLM response only, but in the LM field. + */ + +static BOOL test_ntlm_in_lm(void) +{ + BOOL pass = True; + NTSTATUS nt_status; + uint32 flags = 0; + DATA_BLOB nt_response = data_blob(NULL, 24); + + uchar lm_key[8]; + uchar lm_hash[16]; + uchar user_session_key[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(user_session_key); + + flags |= WBFLAG_PAM_LMKEY; + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + SMBNTencrypt(opt_password,chall.data,nt_response.data); + + E_deshash(opt_password, lm_hash); + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &nt_response, + NULL, + flags, + lm_key, + user_session_key, + &error_string, NULL); + + data_blob_free(&nt_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return False; + } + + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, (const char *)lm_key, 8); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)lm_hash, 8); + pass = False; + } + if (memcmp(lm_hash, user_session_key, 8) != 0) { + DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n")); + DEBUG(1, ("user_session_key:\n")); + dump_data(1, (const char *)user_session_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)lm_hash, 8); + pass = False; + } + return pass; +} + +/* + * Test the NTLM response only, but in the both the NT and LM fields. + */ + +static BOOL test_ntlm_in_both(void) +{ + BOOL pass = True; + NTSTATUS nt_status; + uint32 flags = 0; + DATA_BLOB nt_response = data_blob(NULL, 24); + DATA_BLOB session_key = data_blob(NULL, 16); + + char lm_key[8]; + char lm_hash[16]; + char user_session_key[16]; + char nt_hash[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(user_session_key); + + flags |= WBFLAG_PAM_LMKEY; + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + SMBNTencrypt(opt_password,chall.data,nt_response.data); + E_md4hash(opt_password, (unsigned char *)nt_hash); + SMBsesskeygen_ntv1((const unsigned char *)nt_hash, NULL, session_key.data); + + E_deshash(opt_password, (unsigned char *)lm_hash); + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &nt_response, + &nt_response, + flags, + (unsigned char *)lm_key, + (unsigned char *)user_session_key, + &error_string, NULL); + + data_blob_free(&nt_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return False; + } + + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, lm_key, 8); + DEBUG(1, ("expected:\n")); + dump_data(1, lm_hash, 8); + pass = False; + } + if (memcmp(session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + DEBUG(1, ("NT Session Key does not match expectations!\n")); + DEBUG(1, ("user_session_key:\n")); + dump_data(1, user_session_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)session_key.data, session_key.length); + pass = False; + } + + + return pass; +} + +/* + * Test the NTLMv2 and LMv2 responses + */ + +static BOOL test_lmv2_ntlmv2_broken(enum ntlm_break break_which) +{ + BOOL pass = True; + NTSTATUS nt_status; + uint32 flags = 0; + DATA_BLOB ntlmv2_response = data_blob(NULL, 0); + DATA_BLOB lmv2_response = data_blob(NULL, 0); + DATA_BLOB ntlmv2_session_key = data_blob(NULL, 0); + DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain()); + + uchar user_session_key[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(user_session_key); + + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall, + &names_blob, + &lmv2_response, &ntlmv2_response, + &ntlmv2_session_key)) { + data_blob_free(&names_blob); + return False; + } + data_blob_free(&names_blob); + + switch (break_which) { + case BREAK_NONE: + break; + case BREAK_LM: + lmv2_response.data[0]++; + break; + case BREAK_NT: + ntlmv2_response.data[0]++; + break; + case NO_LM: + data_blob_free(&lmv2_response); + break; + case NO_NT: + data_blob_free(&ntlmv2_response); + break; + } + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &lmv2_response, + &ntlmv2_response, + flags, + NULL, + user_session_key, + &error_string, NULL); + + data_blob_free(&lmv2_response); + data_blob_free(&ntlmv2_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return break_which == BREAK_NT; + } + + if (break_which != NO_NT && break_which != BREAK_NT && memcmp(ntlmv2_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + DEBUG(1, ("USER (NTLMv2) Session Key does not match expectations!\n")); + DEBUG(1, ("user_session_key:\n")); + dump_data(1, (const char *)user_session_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)ntlmv2_session_key.data, ntlmv2_session_key.length); + pass = False; + } + return pass; +} + +/* + * Test the NTLMv2 and LMv2 responses + */ + +static BOOL test_lmv2_ntlmv2(void) +{ + return test_lmv2_ntlmv2_broken(BREAK_NONE); +} + +/* + * Test the LMv2 response only + */ + +static BOOL test_lmv2(void) +{ + return test_lmv2_ntlmv2_broken(NO_NT); +} + +/* + * Test the NTLMv2 response only + */ + +static BOOL test_ntlmv2(void) +{ + return test_lmv2_ntlmv2_broken(NO_LM); +} + +static BOOL test_lm_ntlm(void) +{ + return test_lm_ntlm_broken(BREAK_NONE); +} + +static BOOL test_ntlm_lm_broken(void) +{ + return test_lm_ntlm_broken(BREAK_LM); +} + +static BOOL test_ntlm_ntlm_broken(void) +{ + return test_lm_ntlm_broken(BREAK_NT); +} + +static BOOL test_ntlmv2_lmv2_broken(void) +{ + return test_lmv2_ntlmv2_broken(BREAK_LM); +} + +static BOOL test_ntlmv2_ntlmv2_broken(void) +{ + return test_lmv2_ntlmv2_broken(BREAK_NT); +} + +static BOOL test_plaintext(enum ntlm_break break_which) +{ + NTSTATUS nt_status; + uint32 flags = 0; + DATA_BLOB nt_response = data_blob(NULL, 0); + DATA_BLOB lm_response = data_blob(NULL, 0); + char *password; + + uchar user_session_key[16]; + uchar lm_key[16]; + static const uchar zeros[8]; + DATA_BLOB chall = data_blob(zeros, sizeof(zeros)); + char *error_string; + + ZERO_STRUCT(user_session_key); + + flags |= WBFLAG_PAM_LMKEY; + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + if ((push_ucs2_allocate((smb_ucs2_t **)&nt_response.data, opt_password)) == -1) { + DEBUG(0, ("push_ucs2_allocate failed!\n")); + exit(1); + } + + nt_response.length = strlen_w(((void *)nt_response.data))*sizeof(smb_ucs2_t); + + password = strdup_upper(opt_password); + + if ((convert_string_allocate(NULL, CH_UNIX, + CH_DOS, password, + strlen(password)+1, + (void**)&lm_response.data,True)) == -1) { + DEBUG(0, ("push_ascii_allocate failed!\n")); + exit(1); + } + + SAFE_FREE(password); + + lm_response.length = strlen(lm_response.data); + + switch (break_which) { + case BREAK_NONE: + break; + case BREAK_LM: + lm_response.data[0]++; + break; + case BREAK_NT: + nt_response.data[0]++; + break; + case NO_LM: + SAFE_FREE(lm_response.data); + lm_response.length = 0; + break; + case NO_NT: + SAFE_FREE(nt_response.data); + nt_response.length = 0; + break; + } + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &lm_response, + &nt_response, + flags, + lm_key, + user_session_key, + &error_string, NULL); + + SAFE_FREE(nt_response.data); + SAFE_FREE(lm_response.data); + data_blob_free(&chall); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return break_which == BREAK_NT; + } + + return break_which != BREAK_NT; +} + +static BOOL test_plaintext_none_broken(void) { + return test_plaintext(BREAK_NONE); +} + +static BOOL test_plaintext_lm_broken(void) { + return test_plaintext(BREAK_LM); +} + +static BOOL test_plaintext_nt_broken(void) { + return test_plaintext(BREAK_NT); +} + +static BOOL test_plaintext_nt_only(void) { + return test_plaintext(NO_LM); +} + +static BOOL test_plaintext_lm_only(void) { + return test_plaintext(NO_NT); +} + +/* + Tests: + + - LM only + - NT and LM + - NT + - NT in LM field + - NT in both fields + - NTLMv2 + - NTLMv2 and LMv2 + - LMv2 + - plaintext tests (in challenge-response feilds) + + check we get the correct session key in each case + check what values we get for the LM session key + +*/ + +static const struct ntlm_tests { + BOOL (*fn)(void); + const char *name; +} test_table[] = { + {test_lm, "LM"}, + {test_lm_ntlm, "LM and NTLM"}, + {test_ntlm, "NTLM"}, + {test_ntlm_in_lm, "NTLM in LM"}, + {test_ntlm_in_both, "NTLM in both"}, + {test_ntlmv2, "NTLMv2"}, + {test_lmv2_ntlmv2, "NTLMv2 and LMv2"}, + {test_lmv2, "LMv2"}, + {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"}, + {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"}, + {test_ntlm_lm_broken, "NTLM and LM, LM broken"}, + {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"}, + {test_plaintext_none_broken, "Plaintext"}, + {test_plaintext_lm_broken, "Plaintext LM broken"}, + {test_plaintext_nt_broken, "Plaintext NT broken"}, + {test_plaintext_nt_only, "Plaintext NT only"}, + {test_plaintext_lm_only, "Plaintext LM only"} +}; + +BOOL diagnose_ntlm_auth(void) +{ + unsigned int i; + BOOL pass = True; + + for (i=0; test_table[i].fn; i++) { + if (!test_table[i].fn()) { + DEBUG(1, ("Test %s failed!\n", test_table[i].name)); + pass = False; + } + } + + return pass; +} + -- cgit v1.2.1