From 3454eae9d41b23f856b350d1aac88795f339bdc3 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 27 Jul 2018 08:44:24 +1200 Subject: CVE-2018-1139 libcli/auth: Add initial tests for ntlm_password_check() BUG: https://bugzilla.samba.org/show_bug.cgi?id=13360 Signed-off-by: Andrew Bartlett --- libcli/auth/tests/ntlm_check.c | 413 +++++++++++++++++++++++++++++++++++++++++ libcli/auth/wscript_build | 13 ++ 2 files changed, 426 insertions(+) create mode 100644 libcli/auth/tests/ntlm_check.c (limited to 'libcli') diff --git a/libcli/auth/tests/ntlm_check.c b/libcli/auth/tests/ntlm_check.c new file mode 100644 index 00000000000..e87a0a276d4 --- /dev/null +++ b/libcli/auth/tests/ntlm_check.c @@ -0,0 +1,413 @@ +/* + * Unit tests for the ntlm_check password hash check library. + * + * Copyright (C) Andrew Bartlett 2018 + * + * 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 3 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, see . + * + */ + +/* + * from cmocka.c: + * These headers or their equivalents should be included prior to + * including + * this header file. + * + * #include + * #include + * #include + * + * This allows test applications to use custom definitions of C standard + * library functions and types. + * + */ + +/* + * Note that the messaging routines (audit_message_send and get_event_server) + * are not tested by these unit tests. Currently they are for integration + * test support, and as such are exercised by the integration tests. + */ +#include +#include +#include +#include + +#include "includes.h" +#include "../lib/crypto/crypto.h" +#include "librpc/gen_ndr/netlogon.h" +#include "libcli/auth/libcli_auth.h" +#include "auth/credentials/credentials.h" + +struct ntlm_state { + const char *username; + const char *domain; + DATA_BLOB challenge; + DATA_BLOB ntlm; + DATA_BLOB lm; + DATA_BLOB ntlm_key; + DATA_BLOB lm_key; + const struct samr_Password *nt_hash; +}; + +static int test_ntlm_setup_with_options(void **state, + int flags, bool upn) +{ + NTSTATUS status; + DATA_BLOB challenge = { + .data = discard_const_p(uint8_t, "I am a teapot"), + .length = 8 + }; + struct ntlm_state *ntlm_state = talloc(NULL, struct ntlm_state); + DATA_BLOB target_info = NTLMv2_generate_names_blob(ntlm_state, + NULL, + "serverdom"); + struct cli_credentials *creds = cli_credentials_init(ntlm_state); + cli_credentials_set_username(creds, + "testuser", + CRED_SPECIFIED); + cli_credentials_set_domain(creds, + "testdom", + CRED_SPECIFIED); + cli_credentials_set_workstation(creds, + "testwksta", + CRED_SPECIFIED); + cli_credentials_set_password(creds, + "testpass", + CRED_SPECIFIED); + + if (upn) { + cli_credentials_set_principal(creds, + "testuser@samba.org", + CRED_SPECIFIED); + } + + cli_credentials_get_ntlm_username_domain(creds, + ntlm_state, + &ntlm_state->username, + &ntlm_state->domain); + + status = cli_credentials_get_ntlm_response(creds, + ntlm_state, + &flags, + challenge, + NULL, + target_info, + &ntlm_state->lm, + &ntlm_state->ntlm, + &ntlm_state->lm_key, + &ntlm_state->ntlm_key); + ntlm_state->challenge = challenge; + + ntlm_state->nt_hash = cli_credentials_get_nt_hash(creds, + ntlm_state); + + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + *state = ntlm_state; + return 0; +} + +static int test_ntlm_setup(void **state) { + return test_ntlm_setup_with_options(state, 0, false); +} + +static int test_ntlm_and_lm_setup(void **state) { + return test_ntlm_setup_with_options(state, + CLI_CRED_LANMAN_AUTH, + false); +} + +static int test_ntlm2_setup(void **state) { + return test_ntlm_setup_with_options(state, + CLI_CRED_NTLM2, + false); +} + +static int test_ntlmv2_setup(void **state) { + return test_ntlm_setup_with_options(state, + CLI_CRED_NTLMv2_AUTH, + false); +} + +static int test_ntlm_teardown(void **state) +{ + struct ntlm_state *ntlm_state + = talloc_get_type_abort(*state, + struct ntlm_state); + TALLOC_FREE(ntlm_state); + *state = NULL; + return 0; +} + +static void test_ntlm_allowed(void **state) +{ + DATA_BLOB user_sess_key, lm_sess_key; + struct ntlm_state *ntlm_state + = talloc_get_type_abort(*state, + struct ntlm_state); + NTSTATUS status; + status = ntlm_password_check(ntlm_state, + false, + NTLM_AUTH_ON, + 0, + &ntlm_state->challenge, + &ntlm_state->lm, + &ntlm_state->ntlm, + ntlm_state->username, + ntlm_state->username, + ntlm_state->domain, + NULL, + ntlm_state->nt_hash, + &user_sess_key, + &lm_sess_key); + + assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_OK)); +} + +static void test_ntlm_allowed_lm_supplied(void **state) +{ + return test_ntlm_allowed(state); +} + +static void test_ntlm_disabled(void **state) +{ + DATA_BLOB user_sess_key, lm_sess_key; + struct ntlm_state *ntlm_state + = talloc_get_type_abort(*state, + struct ntlm_state); + NTSTATUS status; + status = ntlm_password_check(ntlm_state, + false, + NTLM_AUTH_DISABLED, + 0, + &ntlm_state->challenge, + &ntlm_state->lm, + &ntlm_state->ntlm, + ntlm_state->username, + ntlm_state->username, + ntlm_state->domain, + NULL, + ntlm_state->nt_hash, + &user_sess_key, + &lm_sess_key); + + assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_NTLM_BLOCKED)); +} + +static void test_ntlm2(void **state) +{ + DATA_BLOB user_sess_key, lm_sess_key; + struct ntlm_state *ntlm_state + = talloc_get_type_abort(*state, + struct ntlm_state); + NTSTATUS status; + status = ntlm_password_check(ntlm_state, + false, + NTLM_AUTH_ON, + 0, + &ntlm_state->challenge, + &ntlm_state->lm, + &ntlm_state->ntlm, + ntlm_state->username, + ntlm_state->username, + ntlm_state->domain, + NULL, + ntlm_state->nt_hash, + &user_sess_key, + &lm_sess_key); + + /* + * NTLM2 session security (where the real challenge is the + * MD5(challenge, client-challenge) (in the first 8 bytes of + * the lm) isn't decoded by ntlm_password_check(), it must + * first be converted back into normal NTLM by the NTLMSSP + * layer + */ + assert_int_equal(NT_STATUS_V(status), + NT_STATUS_V(NT_STATUS_WRONG_PASSWORD)); +} + +static void test_ntlm_mschapv2_only_allowed(void **state) +{ + DATA_BLOB user_sess_key, lm_sess_key; + struct ntlm_state *ntlm_state + = talloc_get_type_abort(*state, + struct ntlm_state); + NTSTATUS status; + status = ntlm_password_check(ntlm_state, + false, + NTLM_AUTH_MSCHAPv2_NTLMV2_ONLY, + MSV1_0_ALLOW_MSVCHAPV2, + &ntlm_state->challenge, + &ntlm_state->lm, + &ntlm_state->ntlm, + ntlm_state->username, + ntlm_state->username, + ntlm_state->domain, + NULL, + ntlm_state->nt_hash, + &user_sess_key, + &lm_sess_key); + + assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_OK)); +} + +static void test_ntlm_mschapv2_only_denied(void **state) +{ + DATA_BLOB user_sess_key, lm_sess_key; + struct ntlm_state *ntlm_state + = talloc_get_type_abort(*state, + struct ntlm_state); + NTSTATUS status; + status = ntlm_password_check(ntlm_state, + false, + NTLM_AUTH_MSCHAPv2_NTLMV2_ONLY, + 0, + &ntlm_state->challenge, + &ntlm_state->lm, + &ntlm_state->ntlm, + ntlm_state->username, + ntlm_state->username, + ntlm_state->domain, + NULL, + ntlm_state->nt_hash, + &user_sess_key, + &lm_sess_key); + + assert_int_equal(NT_STATUS_V(status), + NT_STATUS_V(NT_STATUS_WRONG_PASSWORD)); +} + +static void test_ntlmv2_only_ntlmv2(void **state) +{ + DATA_BLOB user_sess_key, lm_sess_key; + struct ntlm_state *ntlm_state + = talloc_get_type_abort(*state, + struct ntlm_state); + NTSTATUS status; + status = ntlm_password_check(ntlm_state, + false, + NTLM_AUTH_NTLMV2_ONLY, + 0, + &ntlm_state->challenge, + &ntlm_state->lm, + &ntlm_state->ntlm, + ntlm_state->username, + ntlm_state->username, + ntlm_state->domain, + NULL, + ntlm_state->nt_hash, + &user_sess_key, + &lm_sess_key); + + assert_int_equal(NT_STATUS_V(status), NT_STATUS_V(NT_STATUS_OK)); +} + +static void test_ntlmv2_only_ntlm(void **state) +{ + DATA_BLOB user_sess_key, lm_sess_key; + struct ntlm_state *ntlm_state + = talloc_get_type_abort(*state, + struct ntlm_state); + NTSTATUS status; + status = ntlm_password_check(ntlm_state, + false, + NTLM_AUTH_NTLMV2_ONLY, + 0, + &ntlm_state->challenge, + &ntlm_state->lm, + &ntlm_state->ntlm, + ntlm_state->username, + ntlm_state->username, + ntlm_state->domain, + NULL, + ntlm_state->nt_hash, + &user_sess_key, + &lm_sess_key); + + assert_int_equal(NT_STATUS_V(status), + NT_STATUS_V(NT_STATUS_WRONG_PASSWORD)); +} + +static void test_ntlmv2_only_ntlm_and_lanman(void **state) +{ + return test_ntlmv2_only_ntlm(state); +} + +static void test_ntlmv2_only_ntlm_once(void **state) +{ + DATA_BLOB user_sess_key, lm_sess_key; + struct ntlm_state *ntlm_state + = talloc_get_type_abort(*state, + struct ntlm_state); + NTSTATUS status; + status = ntlm_password_check(ntlm_state, + false, + NTLM_AUTH_NTLMV2_ONLY, + 0, + &ntlm_state->challenge, + &data_blob_null, + &ntlm_state->ntlm, + ntlm_state->username, + ntlm_state->username, + ntlm_state->domain, + NULL, + ntlm_state->nt_hash, + &user_sess_key, + &lm_sess_key); + + assert_int_equal(NT_STATUS_V(status), + NT_STATUS_V(NT_STATUS_WRONG_PASSWORD)); +} + +int main(int argc, const char **argv) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_ntlm_allowed, + test_ntlm_setup, + test_ntlm_teardown), + cmocka_unit_test_setup_teardown(test_ntlm_allowed_lm_supplied, + test_ntlm_and_lm_setup, + test_ntlm_teardown), + cmocka_unit_test_setup_teardown(test_ntlm_disabled, + test_ntlm_setup, + test_ntlm_teardown), + cmocka_unit_test_setup_teardown(test_ntlm2, + test_ntlm2_setup, + test_ntlm_teardown), + cmocka_unit_test_setup_teardown(test_ntlm_mschapv2_only_allowed, + test_ntlm_setup, + test_ntlm_teardown), + cmocka_unit_test_setup_teardown(test_ntlm_mschapv2_only_denied, + test_ntlm_setup, + test_ntlm_teardown), + cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlm, + test_ntlm_setup, + test_ntlm_teardown), + cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlm_and_lanman, + test_ntlm_and_lm_setup, + test_ntlm_teardown), + cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlm_once, + test_ntlm_setup, + test_ntlm_teardown), + cmocka_unit_test_setup_teardown(test_ntlmv2_only_ntlmv2, + test_ntlmv2_setup, + test_ntlm_teardown) + }; + + cmocka_set_message_output(CM_OUTPUT_SUBUNIT); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/libcli/auth/wscript_build b/libcli/auth/wscript_build index 475b7d69406..d319d9b879e 100644 --- a/libcli/auth/wscript_build +++ b/libcli/auth/wscript_build @@ -41,3 +41,16 @@ bld.SAMBA_SUBSYSTEM('PAM_ERRORS', bld.SAMBA_SUBSYSTEM('SPNEGO_PARSE', source='spnego_parse.c', deps='asn1util') + +bld.SAMBA_BINARY( + 'test_ntlm_check', + source='tests/ntlm_check.c', + deps=''' + NTLM_CHECK + CREDENTIALS_NTLM + samba-credentials + cmocka + talloc + ''', + install=False + ) -- cgit v1.2.1