From adf80f43dc75dae20532b72750e478bed1a24396 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:04:57 +0200 Subject: [PATCH 01/12] CVE-2020-1472(ZeroLogon): libcli/auth: add netlogon_creds_random_challenge() It's good to have just a single isolated function that will generate random challenges, in future we can add some logic in order to avoid weak values, which are likely to be rejected by a server. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher Reviewed-by: Gary Lockyer (backported from commit b813cdcac377210c3ab18e0d0a0c1a76870b1d74) --- libcli/auth/credentials.c | 6 ++++++ libcli/auth/proto.h | 1 + 2 files changed, 7 insertions(+) diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c index 91f37b770c6..c7062b014d6 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -26,6 +26,12 @@ #include "libcli/auth/libcli_auth.h" #include "../libcli/security/dom_sid.h" +void netlogon_creds_random_challenge(struct netr_Credential *challenge) +{ + ZERO_STRUCTP(challenge); + generate_random_buffer(challenge->data, sizeof(challenge->data)); +} + static void netlogon_creds_step_crypt(struct netlogon_creds_CredentialState *creds, const struct netr_Credential *in, struct netr_Credential *out) diff --git a/libcli/auth/proto.h b/libcli/auth/proto.h index a03f45ed7f3..2f51708e465 100644 --- a/libcli/auth/proto.h +++ b/libcli/auth/proto.h @@ -18,6 +18,7 @@ void netlogon_creds_des_decrypt(struct netlogon_creds_CredentialState *creds, st void netlogon_creds_arcfour_crypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len); void netlogon_creds_aes_encrypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len); void netlogon_creds_aes_decrypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len); +void netlogon_creds_random_challenge(struct netr_Credential *challenge); /***************************************************************** The above functions are common to the client and server interface -- 2.17.1 From 33ab6ea41a4759b6fca97047b22cb190b75512f7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:07:30 +0200 Subject: [PATCH 02/12] CVE-2020-1472(ZeroLogon): s4:torture/rpc: make use of netlogon_creds_random_challenge() This will avoid getting flakey tests once our server starts to reject weak challenges. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher Reviewed-by: Gary Lockyer (backported from commit 355efadc6a18ffaaef2e4786e35e89780b10bccc) --- source4/torture/rpc/lsa.c | 2 +- source4/torture/rpc/netlogon.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source4/torture/rpc/lsa.c b/source4/torture/rpc/lsa.c index fa884fb17ee..7e6ed89a660 100644 --- a/source4/torture/rpc/lsa.c +++ b/source4/torture/rpc/lsa.c @@ -2833,7 +2833,7 @@ static bool check_pw_with_ServerAuthenticate3(struct dcerpc_pipe *p, r.in.credentials = &credentials1; r.out.return_credentials = &credentials2; - generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + netlogon_creds_random_challenge(&credentials1); torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), "ServerReqChallenge failed"); diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c index c8e864d00a7..836d5349d88 100644 --- a/source4/torture/rpc/netlogon.c +++ b/source4/torture/rpc/netlogon.c @@ -158,7 +158,7 @@ bool test_SetupCredentials(struct dcerpc_pipe *p, struct torture_context *tctx, r.in.credentials = &credentials1; r.out.return_credentials = &credentials2; - generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + netlogon_creds_random_challenge(&credentials1); torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), "ServerReqChallenge failed"); @@ -227,7 +227,7 @@ bool test_SetupCredentials2ex(struct dcerpc_pipe *p, struct torture_context *tct r.in.credentials = &credentials1; r.out.return_credentials = &credentials2; - generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + netlogon_creds_random_challenge(&credentials1); torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), "ServerReqChallenge failed"); @@ -314,7 +314,7 @@ bool test_SetupCredentials3(struct dcerpc_pipe *p, struct torture_context *tctx, r.in.credentials = &credentials1; r.out.return_credentials = &credentials2; - generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + netlogon_creds_random_challenge(&credentials1); torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), "ServerReqChallenge failed"); @@ -1183,7 +1183,7 @@ static bool test_ServerReqChallengeGlobal(struct torture_context *tctx, r.in.credentials = &credentials1; r.out.return_credentials = &credentials2; - generate_random_buffer(credentials1.data, sizeof(credentials1.data)); + netlogon_creds_random_challenge(&credentials1); torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), "ServerReqChallenge failed on b1"); -- 2.17.1 From d2d8af4bce7bc89a310e2ee609a23de9423932f8 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:08:38 +0200 Subject: [PATCH 03/12] CVE-2020-1472(ZeroLogon): libcli/auth: make use of netlogon_creds_random_challenge() in netlogon_creds_cli.c This will avoid getting rejected by the server if we generate a weak challenge. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher Reviewed-by: Gary Lockyer (cherry picked from commit 46642fd32d91b008615b859cfdf946f63b1ca0aa) --- libcli/auth/netlogon_creds_cli.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c index 29baae40524..1f6e900b30b 100644 --- a/libcli/auth/netlogon_creds_cli.c +++ b/libcli/auth/netlogon_creds_cli.c @@ -1098,8 +1098,7 @@ static void netlogon_creds_cli_auth_challenge_start(struct tevent_req *req) TALLOC_FREE(state->creds); - generate_random_buffer(state->client_challenge.data, - sizeof(state->client_challenge.data)); + netlogon_creds_random_challenge(&state->client_challenge); subreq = dcerpc_netr_ServerReqChallenge_send(state, state->ev, state->binding_handle, -- 2.17.1 From 9325831243da185527a58525d7c7c40826683535 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:10:53 +0200 Subject: [PATCH 04/12] CVE-2020-1472(ZeroLogon): s3:rpc_server:netlogon: make use of netlogon_creds_random_challenge() This is not strictly needed, but makes things more clear. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher Reviewed-by: Gary Lockyer (cherry picked from commit caba2d8082d4b038aa59954b6e812612c2ecc0e1) --- source3/rpc_server/netlogon/srv_netlog_nt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c index 176769f3bbe..c21174a6af8 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -836,8 +836,7 @@ NTSTATUS _netr_ServerReqChallenge(struct pipes_struct *p, pipe_state->client_challenge = *r->in.credentials; - generate_random_buffer(pipe_state->server_challenge.data, - sizeof(pipe_state->server_challenge.data)); + netlogon_creds_random_challenge(&pipe_state->server_challenge); *r->out.return_credentials = pipe_state->server_challenge; -- 2.17.1 From ed9c6a7a42dd2e5eef280bb28ee114e058af5e76 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:10:53 +0200 Subject: [PATCH 05/12] CVE-2020-1472(ZeroLogon): s4:rpc_server:netlogon: make use of netlogon_creds_random_challenge() This is not strictly needed, but makes things more clear. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher Reviewed-by: Gary Lockyer (cherry picked from commit 74eb448adf7fb638fe925eab87a2dbfe9c002cc0) --- source4/rpc_server/netlogon/dcerpc_netlogon.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index 73ac8741012..e85ffeb8c0b 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -97,8 +97,7 @@ static NTSTATUS dcesrv_netr_ServerReqChallenge(struct dcesrv_call_state *dce_cal pipe_state->client_challenge = *r->in.credentials; - generate_random_buffer(pipe_state->server_challenge.data, - sizeof(pipe_state->server_challenge.data)); + netlogon_creds_random_challenge(&pipe_state->server_challenge); *r->out.return_credentials = pipe_state->server_challenge; -- 2.17.1 From a106b76f3ce0f8f65bde5eaeb7d62531ed0f2b21 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:15:26 +0200 Subject: [PATCH 06/12] CVE-2020-1472(ZeroLogon): libcli/auth: add netlogon_creds_is_random_challenge() to avoid weak values This is the check Windows is using, so we won't generate challenges, which are rejected by Windows DCs (and future Samba DCs). BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher Reviewed-by: Gary Lockyer (backported from commit 53528c71ffdb3377c4e73ac596c8507bc3898e83) --- libcli/auth/credentials.c | 23 ++++++++++++++++++++++- libcli/auth/proto.h | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c index c7062b014d6..e866c9de0f1 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -26,10 +26,31 @@ #include "libcli/auth/libcli_auth.h" #include "../libcli/security/dom_sid.h" +bool netlogon_creds_is_random_challenge(const struct netr_Credential *challenge) +{ + /* + * If none of the first 5 bytes of the client challenge is unique, the + * server MUST fail session-key negotiation without further processing + * of the following steps. + */ + + if (challenge->data[1] == challenge->data[0] && + challenge->data[2] == challenge->data[0] && + challenge->data[3] == challenge->data[0] && + challenge->data[4] == challenge->data[0]) + { + return false; + } + + return true; +} + void netlogon_creds_random_challenge(struct netr_Credential *challenge) { ZERO_STRUCTP(challenge); - generate_random_buffer(challenge->data, sizeof(challenge->data)); + while (!netlogon_creds_is_random_challenge(challenge)) { + generate_random_buffer(challenge->data, sizeof(challenge->data)); + } } static void netlogon_creds_step_crypt(struct netlogon_creds_CredentialState *creds, diff --git a/libcli/auth/proto.h b/libcli/auth/proto.h index 2f51708e465..d7d333b2df2 100644 --- a/libcli/auth/proto.h +++ b/libcli/auth/proto.h @@ -18,6 +18,7 @@ void netlogon_creds_des_decrypt(struct netlogon_creds_CredentialState *creds, st void netlogon_creds_arcfour_crypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len); void netlogon_creds_aes_encrypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len); void netlogon_creds_aes_decrypt(struct netlogon_creds_CredentialState *creds, uint8_t *data, size_t len); +bool netlogon_creds_is_random_challenge(const struct netr_Credential *challenge); void netlogon_creds_random_challenge(struct netr_Credential *challenge); /***************************************************************** -- 2.17.1 From b2155274ca34f1f526f6fcd14211d5ec9dc6f465 Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Wed, 1 Mar 2017 11:10:29 +1300 Subject: [PATCH 07/12] lib/util: Add functions to escape log lines but not break all non-ascii We do not want to turn every non-ascii username into a pile of hex, so we instead focus on avoding newline insertion attacks and other low control chars Pair-programmed-by: Andrew Bartlett Signed-off-by: Gary Lockyer Signed-off-by: Andrew Bartlett (backported from commit eacb5aead71299b6bebbddbaf7c9a3d545f9151b) --- lib/util/tests/util_str_escape.c | 90 ++++++++++++++++++++ lib/util/util_str_escape.c | 126 ++++++++++++++++++++++++++++ lib/util/util_str_escape.h | 27 ++++++ lib/util/wscript_build | 5 ++ source4/torture/local/local.c | 1 + source4/torture/local/wscript_build | 3 +- 6 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 lib/util/tests/util_str_escape.c create mode 100644 lib/util/util_str_escape.c create mode 100644 lib/util/util_str_escape.h diff --git a/lib/util/tests/util_str_escape.c b/lib/util/tests/util_str_escape.c new file mode 100644 index 00000000000..82e2209ef55 --- /dev/null +++ b/lib/util/tests/util_str_escape.c @@ -0,0 +1,90 @@ +/* + + util_str_escape testing + + Copyright (C) Andrew Bartlett 2017 + + 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 . +*/ + +#include "includes.h" +#include "torture/torture.h" +#include "torture/local/proto.h" +#include "lib/util/util_str_escape.h" + +static bool test_log_escape_empty_string(struct torture_context *tctx) +{ + char *result = log_escape( tctx, ""); + torture_assert_str_equal(tctx, result, "", "Empty string handling"); + return true; +} + +static bool test_log_escape_null_string(struct torture_context *tctx) +{ + char *result = log_escape( tctx, NULL); + torture_assert(tctx, (result == NULL), "Empty string handling"); + return true; +} + +static bool test_log_escape_plain_string(struct torture_context *tctx) +{ + const char *input = "a plain string with no escapable characters"; + const char *expected = "a plain string with no escapable characters"; + + char *result = log_escape( tctx, input); + torture_assert_str_equal(tctx, result, expected, + "Plain string handling"); + return true; +} + +static bool test_log_escape_string(struct torture_context *tctx) +{ + const char *input = "\a\b\f\n\r\t\v\\\x01"; + const char *expected = "\\a\\b\\f\\n\\r\\t\\v\\\\\\x01"; + + char *result = log_escape( tctx, input); + torture_assert_str_equal(tctx, result, expected, + "Escapable characters in string"); + return true; +} + +static bool test_log_escape_hex_string(struct torture_context *tctx) +{ + const char *input = "\x01\x1F "; + const char *expected = "\\x01\\x1F "; + + char *result = log_escape( tctx, input); + torture_assert_str_equal(tctx, result, expected, + "hex escaping"); + return true; +} +struct torture_suite *torture_local_util_str_escape(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, + "util_str_escape"); + + torture_suite_add_simple_test(suite, "log_escape_empty_string", + test_log_escape_empty_string); + torture_suite_add_simple_test(suite, "log_escape_null_string", + test_log_escape_null_string); + torture_suite_add_simple_test(suite, "log_escape_plain_string", + test_log_escape_plain_string); + torture_suite_add_simple_test(suite, "log_escape_string", + test_log_escape_string); + torture_suite_add_simple_test(suite, "log_escape_hex_string", + test_log_escape_hex_string); + + + return suite; +} diff --git a/lib/util/util_str_escape.c b/lib/util/util_str_escape.c new file mode 100644 index 00000000000..93cdd8de4a8 --- /dev/null +++ b/lib/util/util_str_escape.c @@ -0,0 +1,126 @@ +/* + Samba string escaping routines + + Copyright (C) Andrew Bartlett 2017 + + 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 . +*/ + +#include "includes.h" +#include "lib/util/util_str_escape.h" + + +/* + * Calculate the encoded length of a character for log_escape + * + */ +static size_t encoded_length(char c) +{ + if (c != '\\' && c > 0x1F) { + return 1; + } else { + switch (c) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + case '\\': + return 2; /* C escape sequence */ + default: + return 4; /* hex escape \xhh */ + } + } +} + +/* + * Escape any control characters in the inputs to prevent them from + * interfering with the log output. + */ +char *log_escape(TALLOC_CTX *frame, const char *in) +{ + size_t size = 0; /* Space to allocate for the escaped data */ + char *encoded = NULL; /* The encoded string */ + const char *c; + char *e; + + if (in == NULL) { + return NULL; + } + + /* Calculate the size required for the escaped array */ + c = in; + while (*c) { + size += encoded_length( *c); + c++; + } + size++; + + encoded = talloc_array( frame, char, size); + if (encoded == NULL) { + DBG_ERR( "Out of memory allocating encoded string"); + return NULL; + } + + c = in; + e = encoded; + while (*c) { + if (*c != '\\' && *c > 0x1F) { + *e++ = *c++; + } else { + switch (*c) { + case '\a': + *e++ = '\\'; + *e++ = 'a'; + break; + case '\b': + *e++ = '\\'; + *e++ = 'b'; + break; + case '\f': + *e++ = '\\'; + *e++ = 'f'; + break; + case '\n': + *e++ = '\\'; + *e++ = 'n'; + break; + case '\r': + *e++ = '\\'; + *e++ = 'r'; + break; + case '\t': + *e++ = '\\'; + *e++ = 't'; + break; + case '\v': + *e++ = '\\'; + *e++ = 'v'; + break; + case '\\': + *e++ = '\\'; + *e++ = '\\'; + break; + default: + snprintf(e, 5, "\\x%02X", *c); + e += 4; + } + c++; + } + } + *e = '\0'; + return encoded; +} diff --git a/lib/util/util_str_escape.h b/lib/util/util_str_escape.h new file mode 100644 index 00000000000..0b4c5964c14 --- /dev/null +++ b/lib/util/util_str_escape.h @@ -0,0 +1,27 @@ +/* + Samba string escaping routines + + Copyright (C) Andrew Bartlett 2017 + + 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 . +*/ + +#ifndef _SAMBA_UTIL_STR_ESCAPE_H +#define _SAMBA_UTIL_STR_ESCAPE_H + +#include + +char *log_escape(TALLOC_CTX *frame, const char *in); + +#endif diff --git a/lib/util/wscript_build b/lib/util/wscript_build index 6d2ab4ac27f..8ae61849538 100755 --- a/lib/util/wscript_build +++ b/lib/util/wscript_build @@ -199,3 +199,8 @@ else: deps='talloc tdb strv util_tdb tdb-wrap samba-util', local_include=False, private_library=True) + + bld.SAMBA_SUBSYSTEM('util_str_escape', + source='util_str_escape.c', + deps='talloc', + local_include=False) diff --git a/source4/torture/local/local.c b/source4/torture/local/local.c index 6641f211cff..89066c5f52f 100644 --- a/source4/torture/local/local.c +++ b/source4/torture/local/local.c @@ -74,6 +74,7 @@ torture_local_verif_trailer, torture_local_nss, torture_local_fsrvp, + torture_local_util_str_escape, NULL }; diff --git a/source4/torture/local/wscript_build b/source4/torture/local/wscript_build index 3a12b6bcd27..c8e8fde63fc 100644 --- a/source4/torture/local/wscript_build +++ b/source4/torture/local/wscript_build @@ -20,11 +20,12 @@ TORTURE_LOCAL_SOURCE = '''../../../lib/util/charset/tests/iconv.c ../../../lib/util/tests/strv.c ../../../lib/util/tests/strv_util.c ../../../lib/util/tests/util.c + ../../../lib/util/tests/util_str_escape.c verif_trailer.c nss_tests.c fsrvp_state.c''' -TORTURE_LOCAL_DEPS = 'RPC_NDR_ECHO TDR LIBCLI_SMB MESSAGING iconv POPT_CREDENTIALS TORTURE_AUTH TORTURE_UTIL TORTURE_NDR TORTURE_LIBCRYPTO share torture_registry PROVISION ldb samdb replace-test RPC_FSS_STATE' +TORTURE_LOCAL_DEPS = 'RPC_NDR_ECHO TDR LIBCLI_SMB MESSAGING iconv POPT_CREDENTIALS TORTURE_AUTH TORTURE_UTIL TORTURE_NDR TORTURE_LIBCRYPTO share torture_registry PROVISION ldb samdb replace-test RPC_FSS_STATE util_str_escape' bld.SAMBA_MODULE('TORTURE_LOCAL', source=TORTURE_LOCAL_SOURCE, -- 2.17.1 From 54fa223b5106f2c84fd8ac74e14d4f9a3882740b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:17:29 +0200 Subject: [PATCH 08/12] CVE-2020-1472(ZeroLogon): libcli/auth: reject weak client challenges in netlogon_creds_server_init() This implements the note from MS-NRPC 3.1.4.1 Session-Key Negotiation: 7. If none of the first 5 bytes of the client challenge is unique, the server MUST fail session-key negotiation without further processing of the following steps. It lets ./zerologon_tester.py from https://github.com/SecuraBV/CVE-2020-1472.git report: "Attack failed. Target is probably patched." BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher Reviewed-by: Gary Lockyer (backported from commit d3123858fb59046e826cf2c7ec2a3839e6508624) --- libcli/auth/credentials.c | 16 ++++++++++++++++ libcli/auth/wscript_build | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c index e866c9de0f1..52750153056 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -25,6 +25,7 @@ #include "../lib/crypto/crypto.h" #include "libcli/auth/libcli_auth.h" #include "../libcli/security/dom_sid.h" +#include "lib/util/util_str_escape.h" bool netlogon_creds_is_random_challenge(const struct netr_Credential *challenge) { @@ -452,6 +453,7 @@ struct netlogon_creds_CredentialState *netlogon_creds_server_init(TALLOC_CTX *me { struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState); + bool ok; if (!creds) { return NULL; @@ -464,6 +466,20 @@ struct netlogon_creds_CredentialState *netlogon_creds_server_init(TALLOC_CTX *me dump_data_pw("Server chall", server_challenge->data, sizeof(server_challenge->data)); dump_data_pw("Machine Pass", machine_password->hash, sizeof(machine_password->hash)); + ok = netlogon_creds_is_random_challenge(client_challenge); + if (!ok) { + DBG_WARNING("CVE-2020-1472(ZeroLogon): " + "non-random client challenge rejected for " + "client_account[%s] client_computer_name[%s]\n", + log_escape(mem_ctx, client_account), + log_escape(mem_ctx, client_computer_name)); + dump_data(DBGLVL_WARNING, + client_challenge->data, + sizeof(client_challenge->data)); + talloc_free(creds); + return NULL; + } + creds->computer_name = talloc_strdup(creds, client_computer_name); if (!creds->computer_name) { talloc_free(creds); diff --git a/libcli/auth/wscript_build b/libcli/auth/wscript_build index 475b7d69406..fad60a9c5a7 100755 --- a/libcli/auth/wscript_build +++ b/libcli/auth/wscript_build @@ -18,7 +18,7 @@ bld.SAMBA_SUBSYSTEM('NTLM_CHECK', bld.SAMBA_SUBSYSTEM('LIBCLI_AUTH', source='credentials.c session.c smbencrypt.c smbdes.c', - public_deps='MSRPC_PARSE', + public_deps='MSRPC_PARSE util_str_escape', public_headers='credentials.h:domain_credentials.h' ) -- 2.17.1 From fc1a57e2d019183e736cc4e32ae207164ffc3f1e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 19:20:25 +0200 Subject: [PATCH 09/12] CVE-2020-1472(ZeroLogon): s4:rpc_server/netlogon: protect netr_ServerPasswordSet2 against unencrypted passwords BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher Reviewed-by: Gary Lockyer (cherry picked from commit d8a6e6549c185daa26852d6d85f475cddfb3083a) --- source4/rpc_server/netlogon/dcerpc_netlogon.c | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index e85ffeb8c0b..1fa93dd3ae0 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -664,7 +664,10 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal struct NL_PASSWORD_VERSION version = {}; const uint32_t *new_version = NULL; NTSTATUS nt_status; - DATA_BLOB new_password; + DATA_BLOB new_password = data_blob_null; + size_t confounder_len; + DATA_BLOB dec_blob = data_blob_null; + DATA_BLOB enc_blob = data_blob_null; int ret; struct samr_CryptPassword password_buf; @@ -717,6 +720,61 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal return NT_STATUS_WRONG_PASSWORD; } + /* + * Make sure the length field was encrypted, + * otherwise we are under attack. + */ + if (new_password.length == r->in.new_password->length) { + DBG_WARNING("Length[%zu] field not encrypted\n", + new_password.length); + return NT_STATUS_WRONG_PASSWORD; + } + + /* + * We don't allow empty passwords for machine accounts. + */ + if (new_password.length < 2) { + DBG_WARNING("Empty password Length[%zu]\n", + new_password.length); + return NT_STATUS_WRONG_PASSWORD; + } + + /* + * Make sure the confounder part of CryptPassword + * buffer was encrypted, otherwise we are under attack. + */ + confounder_len = 512 - new_password.length; + enc_blob = data_blob_const(r->in.new_password->data, confounder_len); + dec_blob = data_blob_const(password_buf.data, confounder_len); + if (data_blob_cmp(&dec_blob, &enc_blob) == 0) { + DBG_WARNING("Confounder buffer not encrypted Length[%zu]\n", + confounder_len); + return NT_STATUS_WRONG_PASSWORD; + } + + /* + * Check that the password part was actually encrypted, + * otherwise we are under attack. + */ + enc_blob = data_blob_const(r->in.new_password->data + confounder_len, + new_password.length); + dec_blob = data_blob_const(password_buf.data + confounder_len, + new_password.length); + if (data_blob_cmp(&dec_blob, &enc_blob) == 0) { + DBG_WARNING("Password buffer not encrypted Length[%zu]\n", + new_password.length); + return NT_STATUS_WRONG_PASSWORD; + } + + /* + * don't allow zero buffers + */ + if (all_zero(new_password.data, new_password.length)) { + DBG_WARNING("Password zero buffer Length[%zu]\n", + new_password.length); + return NT_STATUS_WRONG_PASSWORD; + } + /* fetch the old password hashes (at least one of both has to exist) */ ret = gendb_search(sam_ctx, mem_ctx, NULL, &res, attrs, -- 2.17.1 From a996f1c177f9feb406a6e9c4cc466a8c77c38a44 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 16 Sep 2020 12:53:50 -0700 Subject: [PATCH 10/12] CVE-2020-1472(ZeroLogon): s3:rpc_server/netlogon: protect netr_ServerPasswordSet2 against unencrypted passwords BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher Reviewed-by: Gary Lockyer (backported from commit 82d41977a8bef426396e3e00833d55711a55f372) needed to add definition of struct _samr_Credentials_t --- source3/rpc_server/netlogon/srv_netlog_nt.c | 110 +++++++++++++++++++- 1 file changed, 107 insertions(+), 3 deletions(-) diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c index c21174a6af8..3d0eb84bf27 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -1140,6 +1140,17 @@ static NTSTATUS netr_creds_server_step_check(struct pipes_struct *p, /************************************************************************* *************************************************************************/ +struct _samr_Credentials_t { + enum { + CRED_TYPE_NT_HASH, + CRED_TYPE_PLAIN_TEXT, + } cred_type; + union { + struct samr_Password *nt_hash; + const char *password; + } creds; +}; + static NTSTATUS netr_set_machine_account_password(TALLOC_CTX *mem_ctx, struct auth_session_info *session_info, struct messaging_context *msg_ctx, @@ -1324,9 +1335,15 @@ NTSTATUS _netr_ServerPasswordSet2(struct pipes_struct *p, { NTSTATUS status; struct netlogon_creds_CredentialState *creds = NULL; - DATA_BLOB plaintext; + DATA_BLOB plaintext = data_blob_null; + DATA_BLOB new_password = data_blob_null; + size_t confounder_len; + DATA_BLOB dec_blob = data_blob_null; + DATA_BLOB enc_blob = data_blob_null; struct samr_CryptPassword password_buf; struct samr_Password nt_hash; + struct _samr_Credentials_t cr = { CRED_TYPE_PLAIN_TEXT, {0}}; + bool ok; become_root(); status = netr_creds_server_step_check(p, p->mem_ctx, @@ -1358,13 +1375,100 @@ NTSTATUS _netr_ServerPasswordSet2(struct pipes_struct *p, netlogon_creds_arcfour_crypt(creds, password_buf.data, 516); } - if (!extract_pw_from_buffer(p->mem_ctx, password_buf.data, &plaintext)) { + if (!extract_pw_from_buffer(p->mem_ctx, password_buf.data, &new_password)) { + DEBUG(2,("_netr_ServerPasswordSet2: unable to extract password " + "from a buffer. Rejecting auth request as a wrong password\n")); TALLOC_FREE(creds); return NT_STATUS_WRONG_PASSWORD; } - mdfour(nt_hash.hash, plaintext.data, plaintext.length); + /* + * Make sure the length field was encrypted, + * otherwise we are under attack. + */ + if (new_password.length == r->in.new_password->length) { + DBG_WARNING("Length[%zu] field not encrypted\n", + new_password.length); + TALLOC_FREE(creds); + return NT_STATUS_WRONG_PASSWORD; + } + + /* + * We don't allow empty passwords for machine accounts. + */ + if (new_password.length < 2) { + DBG_WARNING("Empty password Length[%zu]\n", + new_password.length); + TALLOC_FREE(creds); + return NT_STATUS_WRONG_PASSWORD; + } + + /* + * Make sure the confounder part of CryptPassword + * buffer was encrypted, otherwise we are under attack. + */ + confounder_len = 512 - new_password.length; + enc_blob = data_blob_const(r->in.new_password->data, confounder_len); + dec_blob = data_blob_const(password_buf.data, confounder_len); + if (data_blob_cmp(&dec_blob, &enc_blob) == 0) { + DBG_WARNING("Confounder buffer not encrypted Length[%zu]\n", + confounder_len); + TALLOC_FREE(creds); + return NT_STATUS_WRONG_PASSWORD; + } + + /* + * Check that the password part was actually encrypted, + * otherwise we are under attack. + */ + enc_blob = data_blob_const(r->in.new_password->data + confounder_len, + new_password.length); + dec_blob = data_blob_const(password_buf.data + confounder_len, + new_password.length); + if (data_blob_cmp(&dec_blob, &enc_blob) == 0) { + DBG_WARNING("Password buffer not encrypted Length[%zu]\n", + new_password.length); + TALLOC_FREE(creds); + return NT_STATUS_WRONG_PASSWORD; + } + + /* + * don't allow zero buffers + */ + if (all_zero(new_password.data, new_password.length)) { + DBG_WARNING("Password zero buffer Length[%zu]\n", + new_password.length); + TALLOC_FREE(creds); + return NT_STATUS_WRONG_PASSWORD; + } + + /* Convert from UTF16 -> plaintext. */ + ok = convert_string_talloc(p->mem_ctx, + CH_UTF16, + CH_UNIX, + new_password.data, + new_password.length, + (void *)&plaintext.data, + &plaintext.length); + if (!ok) { + DBG_WARNING("unable to extract password from a buffer. " + "Rejecting auth request as a wrong password\n"); + TALLOC_FREE(creds); + return NT_STATUS_WRONG_PASSWORD; + } + + /* + * We don't allow empty passwords for machine accounts. + */ + cr.creds.password = (const char*) plaintext.data; + if (strlen(cr.creds.password) == 0) { + DBG_WARNING("Empty plaintext password\n"); + TALLOC_FREE(creds); + return NT_STATUS_WRONG_PASSWORD; + } + + mdfour(nt_hash.hash, plaintext.data, plaintext.length); status = netr_set_machine_account_password(p->mem_ctx, p->session_info, p->msg_ctx, -- 2.17.1 From 063ac1947c9ab39778e78f204bb6a395138c9cf8 Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Fri, 18 Sep 2020 12:39:54 +1200 Subject: [PATCH 11/12] CVE-2020-1472(ZeroLogon): s4 torture rpc: Test empty machine acct pwd Ensure that an empty machine account password can't be set by netr_ServerPasswordSet2 BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Gary Lockyer Reviewed-by: Stefan Metzmacher (cherry picked from commit 4b262b03e1e8285c399338895832a115953d3f23) --- source4/torture/rpc/netlogon.c | 64 +++++++++++++++------------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c index 836d5349d88..221459fa165 100644 --- a/source4/torture/rpc/netlogon.c +++ b/source4/torture/rpc/netlogon.c @@ -627,45 +627,39 @@ static bool test_SetPassword2_with_flags(struct torture_context *tctx, cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED); - if (!torture_setting_bool(tctx, "dangerous", false)) { - torture_comment(tctx, - "Not testing ability to set password to '', enable dangerous tests to perform this test\n"); + /* + * As a consequence of CVE-2020-1472(ZeroLogon) + * Samba explicitly disallows the setting of an empty machine account + * password. + * + * Note that this may fail against Windows, and leave a machine account + * with an empty password. + */ + password = ""; + encode_pw_buffer(password_buf.data, password, STR_UNICODE); + if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { + netlogon_creds_aes_encrypt(creds, password_buf.data, 516); } else { - /* by changing the machine password to "" - * we check if the server uses password restrictions - * for ServerPasswordSet2 - * (win2k3 accepts "") - */ - password = ""; - encode_pw_buffer(password_buf.data, password, STR_UNICODE); - if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { - netlogon_creds_aes_encrypt(creds, password_buf.data, 516); - } else { - netlogon_creds_arcfour_crypt(creds, password_buf.data, 516); - } - memcpy(new_password.data, password_buf.data, 512); - new_password.length = IVAL(password_buf.data, 512); - - torture_comment(tctx, - "Testing ServerPasswordSet2 on machine account\n"); - torture_comment(tctx, - "Changing machine account password to '%s'\n", password); - - netlogon_creds_client_authenticator(creds, &credential); - - torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), - "ServerPasswordSet2 failed"); - torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet2 failed"); + netlogon_creds_arcfour_crypt(creds, password_buf.data, 516); + } + memcpy(new_password.data, password_buf.data, 512); + new_password.length = IVAL(password_buf.data, 512); - if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) { - torture_comment(tctx, "Credential chaining failed\n"); - } + torture_comment(tctx, + "Testing ServerPasswordSet2 on machine account\n"); + torture_comment(tctx, + "Changing machine account password to '%s'\n", password); - cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED); - } + netlogon_creds_client_authenticator(creds, &credential); - torture_assert(tctx, test_SetupCredentials(p, tctx, machine_credentials, &creds), - "ServerPasswordSet failed to actually change the password"); + torture_assert_ntstatus_ok( + tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r), + "ServerPasswordSet2 failed"); + torture_assert_ntstatus_equal( + tctx, + r.out.result, + NT_STATUS_WRONG_PASSWORD, + "ServerPasswordSet2 did not return NT_STATUS_WRONG_PASSWORD"); /* now try a random password */ password = generate_random_password(tctx, 8, 255); -- 2.17.1 From 7c28870320163c255487932cc4fcc70d728fa5ed Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Fri, 18 Sep 2020 15:57:34 +1200 Subject: [PATCH 12/12] CVE-2020-1472(ZeroLogon): s4 torture rpc: repeated bytes in client challenge Ensure that client challenges with the first 5 bytes identical are rejected. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Gary Lockyer Reviewed-by: Stefan Metzmacher Autobuild-User(master): Stefan Metzmacher Autobuild-Date(master): Fri Sep 18 14:13:17 UTC 2020 on sn-devel-184 (Backported from commit 9945f3e3548657c33cc2e5ef97eedd1dfe2edf71) --- source4/torture/rpc/netlogon.c | 336 +++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c index 221459fa165..4edae5b7200 100644 --- a/source4/torture/rpc/netlogon.c +++ b/source4/torture/rpc/netlogon.c @@ -388,6 +388,325 @@ bool test_SetupCredentialsPipe(const struct dcerpc_pipe *p1, return true; } +static bool test_ServerReqChallenge( + struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netr_ServerReqChallenge r; + struct netr_Credential credentials1, credentials2, credentials3; + const char *machine_name; + struct dcerpc_binding_handle *b = p->binding_handle; + struct netr_ServerAuthenticate2 a; + uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS; + uint32_t out_negotiate_flags = 0; + const struct samr_Password *mach_password = NULL; + enum netr_SchannelType sec_chan_type = 0; + struct netlogon_creds_CredentialState *creds = NULL; + const char *account_name = NULL; + + machine_name = cli_credentials_get_workstation(credentials); + mach_password = cli_credentials_get_nt_hash(credentials, tctx); + account_name = cli_credentials_get_username(credentials); + sec_chan_type = cli_credentials_get_secure_channel_type(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + netlogon_creds_random_challenge(&credentials1); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok( + tctx, + r.out.result, + "ServerReqChallenge failed"); + a.in.server_name = NULL; + a.in.account_name = account_name; + a.in.secure_channel_type = sec_chan_type; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &in_negotiate_flags; + a.out.negotiate_flags = &out_negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + mach_password, &credentials3, + in_negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate2\n"); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a), + "ServerAuthenticate2 failed"); + torture_assert_ntstatus_equal( + tctx, + a.out.result, + NT_STATUS_OK, + "ServerAuthenticate2 unexpected"); + + return true; +} + +static bool test_ServerReqChallenge_zero_challenge( + struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netr_ServerReqChallenge r; + struct netr_Credential credentials1, credentials2, credentials3; + const char *machine_name; + struct dcerpc_binding_handle *b = p->binding_handle; + struct netr_ServerAuthenticate2 a; + uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS; + uint32_t out_negotiate_flags = 0; + const struct samr_Password *mach_password = NULL; + enum netr_SchannelType sec_chan_type = 0; + struct netlogon_creds_CredentialState *creds = NULL; + const char *account_name = NULL; + + machine_name = cli_credentials_get_workstation(credentials); + mach_password = cli_credentials_get_nt_hash(credentials, tctx); + account_name = cli_credentials_get_username(credentials); + sec_chan_type = cli_credentials_get_secure_channel_type(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + /* + * Set the client challenge to zero, this should fail + * CVE-2020-1472(ZeroLogon) + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 + */ + ZERO_STRUCT(credentials1); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok( + tctx, + r.out.result, + "ServerReqChallenge failed"); + a.in.server_name = NULL; + a.in.account_name = account_name; + a.in.secure_channel_type = sec_chan_type; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &in_negotiate_flags; + a.out.negotiate_flags = &out_negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + mach_password, &credentials3, + in_negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate2\n"); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a), + "ServerAuthenticate2 failed"); + torture_assert_ntstatus_equal( + tctx, + a.out.result, + NT_STATUS_ACCESS_DENIED, + "ServerAuthenticate2 unexpected"); + + return true; +} + +static bool test_ServerReqChallenge_5_repeats( + struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netr_ServerReqChallenge r; + struct netr_Credential credentials1, credentials2, credentials3; + const char *machine_name; + struct dcerpc_binding_handle *b = p->binding_handle; + struct netr_ServerAuthenticate2 a; + uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS; + uint32_t out_negotiate_flags = 0; + const struct samr_Password *mach_password = NULL; + enum netr_SchannelType sec_chan_type = 0; + struct netlogon_creds_CredentialState *creds = NULL; + const char *account_name = NULL; + + machine_name = cli_credentials_get_workstation(credentials); + mach_password = cli_credentials_get_nt_hash(credentials, tctx); + account_name = cli_credentials_get_username(credentials); + sec_chan_type = cli_credentials_get_secure_channel_type(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + /* + * Set the first 5 bytes of the client challenge to the same value, + * this should fail CVE-2020-1472(ZeroLogon) + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 + */ + credentials1.data[0] = 'A'; + credentials1.data[1] = 'A'; + credentials1.data[2] = 'A'; + credentials1.data[3] = 'A'; + credentials1.data[4] = 'A'; + credentials1.data[5] = 'B'; + credentials1.data[6] = 'C'; + credentials1.data[7] = 'D'; + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok( + tctx, + r.out.result, + "ServerReqChallenge failed"); + a.in.server_name = NULL; + a.in.account_name = account_name; + a.in.secure_channel_type = sec_chan_type; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &in_negotiate_flags; + a.out.negotiate_flags = &out_negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + mach_password, &credentials3, + in_negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate2\n"); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a), + "ServerAuthenticate2 failed"); + torture_assert_ntstatus_equal( + tctx, + a.out.result, + NT_STATUS_ACCESS_DENIED, + "ServerAuthenticate2 unexpected"); + + return true; +} + +static bool test_ServerReqChallenge_4_repeats( + struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *credentials) +{ + struct netr_ServerReqChallenge r; + struct netr_Credential credentials1, credentials2, credentials3; + const char *machine_name; + struct dcerpc_binding_handle *b = p->binding_handle; + struct netr_ServerAuthenticate2 a; + uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS; + uint32_t out_negotiate_flags = 0; + const struct samr_Password *mach_password = NULL; + enum netr_SchannelType sec_chan_type = 0; + struct netlogon_creds_CredentialState *creds = NULL; + const char *account_name = NULL; + + machine_name = cli_credentials_get_workstation(credentials); + mach_password = cli_credentials_get_nt_hash(credentials, tctx); + account_name = cli_credentials_get_username(credentials); + sec_chan_type = cli_credentials_get_secure_channel_type(credentials); + + torture_comment(tctx, "Testing ServerReqChallenge\n"); + + r.in.server_name = NULL; + r.in.computer_name = machine_name; + r.in.credentials = &credentials1; + r.out.return_credentials = &credentials2; + + /* + * Set the first 4 bytes of the client challenge to the same + * value, this should pass as 5 bytes identical are needed to + * fail for CVE-2020-1472(ZeroLogon) + * + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 + */ + credentials1.data[0] = 'A'; + credentials1.data[1] = 'A'; + credentials1.data[2] = 'A'; + credentials1.data[3] = 'A'; + credentials1.data[4] = 'B'; + credentials1.data[5] = 'C'; + credentials1.data[6] = 'D'; + credentials1.data[7] = 'E'; + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerReqChallenge_r(b, tctx, &r), + "ServerReqChallenge failed"); + torture_assert_ntstatus_ok( + tctx, + r.out.result, + "ServerReqChallenge failed"); + a.in.server_name = NULL; + a.in.account_name = account_name; + a.in.secure_channel_type = sec_chan_type; + a.in.computer_name = machine_name; + a.in.negotiate_flags = &in_negotiate_flags; + a.out.negotiate_flags = &out_negotiate_flags; + a.in.credentials = &credentials3; + a.out.return_credentials = &credentials3; + + creds = netlogon_creds_client_init(tctx, a.in.account_name, + a.in.computer_name, + a.in.secure_channel_type, + &credentials1, &credentials2, + mach_password, &credentials3, + in_negotiate_flags); + + torture_assert(tctx, creds != NULL, "memory allocation"); + + torture_comment(tctx, "Testing ServerAuthenticate2\n"); + + torture_assert_ntstatus_ok( + tctx, + dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a), + "ServerAuthenticate2 failed"); + torture_assert_ntstatus_equal( + tctx, + a.out.result, + NT_STATUS_OK, + "ServerAuthenticate2 unexpected"); + + return true; +} + /* try a change password for our machine account */ @@ -4165,6 +4484,23 @@ struct torture_suite *torture_rpc_netlogon(TALLOC_CTX *mem_ctx) torture_rpc_tcase_add_test_creds(tcase, "ServerGetTrustInfo_AES", test_netr_ServerGetTrustInfo_AES); torture_rpc_tcase_add_test_creds(tcase, "GetForestTrustInformation", test_netr_GetForestTrustInformation); + torture_rpc_tcase_add_test_creds( + tcase, + "ServerReqChallenge", + test_ServerReqChallenge); + torture_rpc_tcase_add_test_creds( + tcase, + "ServerReqChallenge_zero_challenge", + test_ServerReqChallenge_zero_challenge); + torture_rpc_tcase_add_test_creds( + tcase, + "ServerReqChallenge_5_repeats", + test_ServerReqChallenge_5_repeats); + torture_rpc_tcase_add_test_creds( + tcase, + "ServerReqChallenge_4_repeats", + test_ServerReqChallenge_4_repeats); + return suite; } -- 2.17.1