From aaf1d3a7b246d83dcc06ad26e1b3c96a27681848 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:04:57 +0200 Subject: [PATCH 01/19] 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 --- libcli/auth/credentials.c | 6 ++++++ libcli/auth/proto.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c index c541eeff470..46259f39306 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -33,6 +33,12 @@ #include #include +void netlogon_creds_random_challenge(struct netr_Credential *challenge) +{ + ZERO_STRUCTP(challenge); + generate_random_buffer(challenge->data, sizeof(challenge->data)); +} + static NTSTATUS 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 88f4a7c6c50..396484a5437 100644 --- a/libcli/auth/proto.h +++ b/libcli/auth/proto.h @@ -13,6 +13,8 @@ /* The following definitions come from /home/jeremy/src/samba/git/master/source3/../source4/../libcli/auth/credentials.c */ +void netlogon_creds_random_challenge(struct netr_Credential *challenge); + NTSTATUS netlogon_creds_des_encrypt_LMKey(struct netlogon_creds_CredentialState *creds, struct netr_LMSessionKey *key); NTSTATUS netlogon_creds_des_decrypt_LMKey(struct netlogon_creds_CredentialState *creds, -- 2.20.1 From 242935f02b1ad1962740de37d34dc006deb40b70 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:07:30 +0200 Subject: [PATCH 02/19] 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 --- source4/torture/rpc/lsa.c | 2 +- source4/torture/rpc/netlogon.c | 34 ++++++++++++---------------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/source4/torture/rpc/lsa.c b/source4/torture/rpc/lsa.c index c342b4e67e6..908ea08019c 100644 --- a/source4/torture/rpc/lsa.c +++ b/source4/torture/rpc/lsa.c @@ -2872,7 +2872,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 65188d2dc85..826793717e7 100644 --- a/source4/torture/rpc/netlogon.c +++ b/source4/torture/rpc/netlogon.c @@ -160,7 +160,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"); @@ -229,7 +229,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"); @@ -324,7 +324,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"); @@ -396,7 +396,7 @@ bool test_SetupCredentialsDowngrade(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"); @@ -1283,7 +1283,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"); @@ -1372,7 +1372,7 @@ static bool test_ServerReqChallengeReuseGlobal(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"); @@ -1461,7 +1461,7 @@ static bool test_ServerReqChallengeReuseGlobal2(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"); @@ -1551,7 +1551,7 @@ static bool test_ServerReqChallengeReuseGlobal3(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"); @@ -1643,8 +1643,7 @@ static bool test_ServerReqChallengeReuseGlobal4(struct torture_context *tctx, r.in.credentials = &credentials1_random; r.out.return_credentials = &credentials_discard; - generate_random_buffer(credentials1_random.data, - sizeof(credentials1_random.data)); + netlogon_creds_random_challenge(&credentials1_random); torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), "ServerReqChallenge failed on b1"); @@ -1656,7 +1655,7 @@ static bool test_ServerReqChallengeReuseGlobal4(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"); @@ -1667,16 +1666,7 @@ static bool test_ServerReqChallengeReuseGlobal4(struct torture_context *tctx, r.in.credentials = &credentials1_random; r.out.return_credentials = &credentials_discard; - generate_random_buffer(credentials1_random.data, - sizeof(credentials1_random.data)); - - r.in.server_name = NULL; - r.in.computer_name = "CHALTEST3"; - r.in.credentials = &credentials1_random; - r.out.return_credentials = &credentials_discard; - - generate_random_buffer(credentials1_random.data, - sizeof(credentials1_random.data)); + netlogon_creds_random_challenge(&credentials1_random); torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r), "ServerReqChallenge failed on b1"); @@ -1752,7 +1742,7 @@ static bool test_ServerReqChallengeReuse(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"); -- 2.20.1 From 78c125a774abc9106c234661caad92990ca39972 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:08:38 +0200 Subject: [PATCH 03/19] 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 --- 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 407cb471cbc..12cb3149ff6 100644 --- a/libcli/auth/netlogon_creds_cli.c +++ b/libcli/auth/netlogon_creds_cli.c @@ -1177,8 +1177,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.20.1 From 4684d16318d9371517f83ad9805f0acb6c169e7a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:10:53 +0200 Subject: [PATCH 04/19] 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 --- 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 2a2e2d0ac6e..548efb44ad2 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -841,8 +841,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.20.1 From 99095e931ec6ef011b5356fe5eca77d6abc79f8c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:10:53 +0200 Subject: [PATCH 05/19] 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 --- 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 0ab55afeab0..7d1b9db0b86 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -90,8 +90,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.20.1 From 40570eec51c06f9d340e4e61021fd9e26b4ba5de Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:15:26 +0200 Subject: [PATCH 06/19] 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 --- 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 46259f39306..54a20100b51 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -33,10 +33,31 @@ #include #include +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 NTSTATUS netlogon_creds_step_crypt(struct netlogon_creds_CredentialState *creds, diff --git a/libcli/auth/proto.h b/libcli/auth/proto.h index 396484a5437..a62668f088f 100644 --- a/libcli/auth/proto.h +++ b/libcli/auth/proto.h @@ -13,6 +13,7 @@ /* The following definitions come from /home/jeremy/src/samba/git/master/source3/../source4/../libcli/auth/credentials.c */ +bool netlogon_creds_is_random_challenge(const struct netr_Credential *challenge); void netlogon_creds_random_challenge(struct netr_Credential *challenge); NTSTATUS netlogon_creds_des_encrypt_LMKey(struct netlogon_creds_CredentialState *creds, -- 2.20.1 From 9aeee6578534b86be60d1616e0b98e18908465f5 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:17:29 +0200 Subject: [PATCH 07/19] 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 --- libcli/auth/credentials.c | 17 ++++++++++++++++- libcli/auth/wscript_build | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c index 54a20100b51..23339d98bfa 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -24,6 +24,7 @@ #include "system/time.h" #include "libcli/auth/libcli_auth.h" #include "../libcli/security/dom_sid.h" +#include "lib/util/util_str_escape.h" #ifndef HAVE_GNUTLS_AES_CFB8 #include "lib/crypto/aes.h" @@ -704,7 +705,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); NTSTATUS status; - + bool ok; if (!creds) { return NULL; @@ -717,6 +718,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 41937623630..2a6a7468e45 100644 --- 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 gnutls GNUTLS_HELPERS', + public_deps='MSRPC_PARSE gnutls GNUTLS_HELPERS util_str_escape', public_headers='credentials.h:domain_credentials.h' ) -- 2.20.1 From 42611147041d78d57f2cbeb9e97bbe75dc2a1fc9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 19:20:25 +0200 Subject: [PATCH 08/19] 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 --- 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 7d1b9db0b86..4aa6f256a07 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -724,7 +724,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; @@ -790,6 +793,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.20.1 From acb50596e53e6ce13c5519674066f19971e2114e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 16 Sep 2020 12:48:21 -0700 Subject: [PATCH 09/19] CVE-2020-1472(ZeroLogon): s3:rpc_server/netlogon: Fix mem leak onto p->mem_ctx in error path of _netr_ServerPasswordSet2(). BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Jeremy Allison --- source3/rpc_server/netlogon/srv_netlog_nt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c index 548efb44ad2..2b68a2db15a 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -1386,6 +1386,7 @@ NTSTATUS _netr_ServerPasswordSet2(struct pipes_struct *p, 516); } if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(creds); return status; } -- 2.20.1 From 7c227dc6d9c6416dc39b1d8d739f8505bcb49805 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 16 Sep 2020 12:53:50 -0700 Subject: [PATCH 10/19] 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 --- source3/rpc_server/netlogon/srv_netlog_nt.c | 98 +++++++++++++++++++-- 1 file changed, 92 insertions(+), 6 deletions(-) diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c index 2b68a2db15a..6f52b4a10cc 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -1344,9 +1344,14 @@ 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_Credentials_t cr = { CRED_TYPE_PLAIN_TEXT, {0}}; + bool ok; become_root(); status = netr_creds_server_step_check(p, p->mem_ctx, @@ -1390,18 +1395,99 @@ NTSTATUS _netr_ServerPasswordSet2(struct pipes_struct *p, return status; } - if (!decode_pw_buffer(p->mem_ctx, - password_buf.data, - (char**) &plaintext.data, - &plaintext.length, - CH_UTF16)) { + 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; } + /* + * 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; + } + status = netr_set_machine_account_password(p->mem_ctx, p->session_info, p->msg_ctx, -- 2.20.1 From 810f9f605f5c7de4626370ff3694e6fd3b7b0920 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 10:18:45 +0200 Subject: [PATCH 11/19] CVE-2020-1472(ZeroLogon): s4:rpc_server/netlogon: refactor dcesrv_netr_creds_server_step_check() We should debug more details about the failing request. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher --- source4/rpc_server/netlogon/dcerpc_netlogon.c | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index 4aa6f256a07..7ccf46ae79b 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -624,26 +624,47 @@ static NTSTATUS dcesrv_netr_creds_server_step_check(struct dcesrv_call_state *dc NTSTATUS nt_status; int schannel = lpcfg_server_schannel(dce_call->conn->dce_ctx->lp_ctx); bool schannel_global_required = (schannel == true); + struct netlogon_creds_CredentialState *creds = NULL; + enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; + uint16_t opnum = dce_call->pkt.u.request.opnum; + const char *opname = ""; - if (schannel_global_required) { - enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; - - dcesrv_call_auth_info(dce_call, &auth_type, NULL); - - if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL) { - DBG_ERR("[%s] is not using schannel\n", - computer_name); - return NT_STATUS_ACCESS_DENIED; - } + if (opnum < ndr_table_netlogon.num_calls) { + opname = ndr_table_netlogon.calls[opnum].name; } + dcesrv_call_auth_info(dce_call, &auth_type, NULL); + nt_status = schannel_check_creds_state(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, computer_name, received_authenticator, return_authenticator, - creds_out); - return nt_status; + &creds); + if (!NT_STATUS_IS_OK(nt_status)) { + ZERO_STRUCTP(return_authenticator); + return nt_status; + } + + if (schannel_global_required) { + if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { + *creds_out = creds; + return NT_STATUS_OK; + } + + DBG_ERR("CVE-2020-1472(ZeroLogon): " + "%s request (opnum[%u]) without schannel from " + "client_account[%s] client_computer_name[%s]\n", + opname, opnum, + log_escape(mem_ctx, creds->account_name), + log_escape(mem_ctx, creds->computer_name)); + TALLOC_FREE(creds); + ZERO_STRUCTP(return_authenticator); + return NT_STATUS_ACCESS_DENIED; + } + + *creds_out = creds; + return NT_STATUS_OK; } /* -- 2.20.1 From 220a013148077ce2b5066f49e2bfa80af769a456 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 10:56:53 +0200 Subject: [PATCH 12/19] CVE-2020-1472(ZeroLogon): s4:rpc_server/netlogon: support "server require schannel:WORKSTATION$ = no" This allows to add expections for individual workstations, when using "server schannel = yes". "server schannel = auto" is very insecure and will be removed soon. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher --- source4/rpc_server/netlogon/dcerpc_netlogon.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index 7ccf46ae79b..7994cb904b7 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -624,6 +624,7 @@ static NTSTATUS dcesrv_netr_creds_server_step_check(struct dcesrv_call_state *dc NTSTATUS nt_status; int schannel = lpcfg_server_schannel(dce_call->conn->dce_ctx->lp_ctx); bool schannel_global_required = (schannel == true); + bool schannel_required = schannel_global_required; struct netlogon_creds_CredentialState *creds = NULL; enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; uint16_t opnum = dce_call->pkt.u.request.opnum; @@ -646,7 +647,13 @@ static NTSTATUS dcesrv_netr_creds_server_step_check(struct dcesrv_call_state *dc return nt_status; } - if (schannel_global_required) { + schannel_required = lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx, + NULL, + "server require schannel", + creds->account_name, + schannel_global_required); + + if (schannel_required) { if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { *creds_out = creds; return NT_STATUS_OK; -- 2.20.1 From a8c00792aea953498af83e5b3692190811f9f211 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 17 Sep 2020 13:37:26 +0200 Subject: [PATCH 13/19] CVE-2020-1472(ZeroLogon): s4:rpc_server/netlogon: log warnings about unsecure configurations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should give admins wawrnings until they have a secure configuration. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme Reviewed-by: Günther Deschner --- source4/rpc_server/netlogon/dcerpc_netlogon.c | 66 ++++++++++++++++++- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index 7994cb904b7..9972138dbde 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -625,10 +625,12 @@ static NTSTATUS dcesrv_netr_creds_server_step_check(struct dcesrv_call_state *dc int schannel = lpcfg_server_schannel(dce_call->conn->dce_ctx->lp_ctx); bool schannel_global_required = (schannel == true); bool schannel_required = schannel_global_required; + const char *explicit_opt = NULL; struct netlogon_creds_CredentialState *creds = NULL; enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; uint16_t opnum = dce_call->pkt.u.request.opnum; const char *opname = ""; + static bool warned_global_once = false; if (opnum < ndr_table_netlogon.num_calls) { opname = ndr_table_netlogon.calls[opnum].name; @@ -647,11 +649,18 @@ static NTSTATUS dcesrv_netr_creds_server_step_check(struct dcesrv_call_state *dc return nt_status; } - schannel_required = lpcfg_parm_bool(dce_call->conn->dce_ctx->lp_ctx, + /* + * We don't use lpcfg_parm_bool(), as we + * need the explicit_opt pointer in order to + * adjust the debug messages. + */ + explicit_opt = lpcfg_get_parametric(dce_call->conn->dce_ctx->lp_ctx, NULL, "server require schannel", - creds->account_name, - schannel_global_required); + creds->account_name); + if (explicit_opt != NULL) { + schannel_required = lp_bool(explicit_opt); + } if (schannel_required) { if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { @@ -665,11 +674,62 @@ static NTSTATUS dcesrv_netr_creds_server_step_check(struct dcesrv_call_state *dc opname, opnum, log_escape(mem_ctx, creds->account_name), log_escape(mem_ctx, creds->computer_name)); + DBG_ERR("CVE-2020-1472(ZeroLogon): Check if option " + "'server require schannel:%s = no' is needed! \n", + log_escape(mem_ctx, creds->account_name)); TALLOC_FREE(creds); ZERO_STRUCTP(return_authenticator); return NT_STATUS_ACCESS_DENIED; } + if (!schannel_global_required && !warned_global_once) { + /* + * We want admins to notice their misconfiguration! + */ + DBG_ERR("CVE-2020-1472(ZeroLogon): " + "Please configure 'server schannel = yes', " + "See https://bugzilla.samba.org/show_bug.cgi?id=14497\n"); + warned_global_once = true; + } + + if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { + DBG_ERR("CVE-2020-1472(ZeroLogon): " + "%s request (opnum[%u]) WITH schannel from " + "client_account[%s] client_computer_name[%s]\n", + opname, opnum, + log_escape(mem_ctx, creds->account_name), + log_escape(mem_ctx, creds->computer_name)); + DBG_ERR("CVE-2020-1472(ZeroLogon): " + "Option 'server require schannel:%s = no' not needed!?\n", + log_escape(mem_ctx, creds->account_name)); + + *creds_out = creds; + return NT_STATUS_OK; + } + + + if (explicit_opt != NULL) { + DBG_INFO("CVE-2020-1472(ZeroLogon): " + "%s request (opnum[%u]) without schannel from " + "client_account[%s] client_computer_name[%s]\n", + opname, opnum, + log_escape(mem_ctx, creds->account_name), + log_escape(mem_ctx, creds->computer_name)); + DBG_INFO("CVE-2020-1472(ZeroLogon): " + "Option 'server require schannel:%s = no' still needed!\n", + log_escape(mem_ctx, creds->account_name)); + } else { + DBG_ERR("CVE-2020-1472(ZeroLogon): " + "%s request (opnum[%u]) without schannel from " + "client_account[%s] client_computer_name[%s]\n", + opname, opnum, + log_escape(mem_ctx, creds->account_name), + log_escape(mem_ctx, creds->computer_name)); + DBG_ERR("CVE-2020-1472(ZeroLogon): Check if option " + "'server require schannel:%s = no' might be needed!\n", + log_escape(mem_ctx, creds->account_name)); + } + *creds_out = creds; return NT_STATUS_OK; } -- 2.20.1 From 8e240909500def151b168b43706668b1922ee766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Deschner?= Date: Thu, 17 Sep 2020 14:57:22 +0200 Subject: [PATCH 14/19] CVE-2020-1472(ZeroLogon): s3:rpc_server/netlogon: refactor dcesrv_netr_creds_server_step_check() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should debug more details about the failing request. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Günther Deschner Signed-off-by: Stefan Metzmacher --- source3/rpc_server/netlogon/srv_netlog_nt.c | 43 +++++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c index 6f52b4a10cc..edd397bc833 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -48,6 +48,7 @@ #include "../lib/tsocket/tsocket.h" #include "lib/param/param.h" #include "libsmb/dsgetdcname.h" +#include "lib/util/util_str_escape.h" extern userdom_struct current_user_info; @@ -1074,19 +1075,21 @@ static NTSTATUS netr_creds_server_step_check(struct pipes_struct *p, NTSTATUS status; bool schannel_global_required = (lp_server_schannel() == true) ? true:false; struct loadparm_context *lp_ctx; + struct netlogon_creds_CredentialState *creds = NULL; + enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; + uint16_t opnum = p->opnum; + const char *opname = ""; if (creds_out != NULL) { *creds_out = NULL; } - if (schannel_global_required) { - if (p->auth.auth_type != DCERPC_AUTH_TYPE_SCHANNEL) { - DBG_ERR("[%s] is not using schannel\n", - computer_name); - return NT_STATUS_ACCESS_DENIED; - } + if (opnum < ndr_table_netlogon.num_calls) { + opname = ndr_table_netlogon.calls[opnum].name; } + auth_type = p->auth.auth_type; + lp_ctx = loadparm_init_s3(mem_ctx, loadparm_s3_helpers()); if (lp_ctx == NULL) { DEBUG(0, ("loadparm_init_s3 failed\n")); @@ -1095,9 +1098,33 @@ static NTSTATUS netr_creds_server_step_check(struct pipes_struct *p, status = schannel_check_creds_state(mem_ctx, lp_ctx, computer_name, received_authenticator, - return_authenticator, creds_out); + return_authenticator, &creds); talloc_unlink(mem_ctx, lp_ctx); - return status; + + if (!NT_STATUS_IS_OK(status)) { + ZERO_STRUCTP(return_authenticator); + return status; + } + + if (schannel_global_required) { + if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { + *creds_out = creds; + return NT_STATUS_OK; + } + + DBG_ERR("CVE-2020-1472(ZeroLogon): " + "%s request (opnum[%u]) without schannel from " + "client_account[%s] client_computer_name[%s]\n", + opname, opnum, + log_escape(mem_ctx, creds->account_name), + log_escape(mem_ctx, creds->computer_name)); + TALLOC_FREE(creds); + ZERO_STRUCTP(return_authenticator); + return NT_STATUS_ACCESS_DENIED; + } + + *creds_out = creds; + return NT_STATUS_OK; } -- 2.20.1 From deb1e337d2aa20a6e59abeb2e842ef40aab1954f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Deschner?= Date: Thu, 17 Sep 2020 14:23:16 +0200 Subject: [PATCH 15/19] CVE-2020-1472(ZeroLogon): s3:rpc_server/netlogon: support "server require schannel:WORKSTATION$ = no" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to add expections for individual workstations, when using "server schannel = yes". "server schannel = auto" is very insecure and will be removed soon. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Günther Deschner Signed-off-by: Stefan Metzmacher --- source3/rpc_server/netlogon/srv_netlog_nt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c index edd397bc833..dc1bfa0c4da 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -1074,6 +1074,7 @@ static NTSTATUS netr_creds_server_step_check(struct pipes_struct *p, { NTSTATUS status; bool schannel_global_required = (lp_server_schannel() == true) ? true:false; + bool schannel_required = schannel_global_required; struct loadparm_context *lp_ctx; struct netlogon_creds_CredentialState *creds = NULL; enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; @@ -1106,7 +1107,11 @@ static NTSTATUS netr_creds_server_step_check(struct pipes_struct *p, return status; } - if (schannel_global_required) { + schannel_required = lp_parm_bool(GLOBAL_SECTION_SNUM, + "server require schannel", + creds->account_name, + schannel_global_required); + if (schannel_required) { if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { *creds_out = creds; return NT_STATUS_OK; -- 2.20.1 From 491ac95771ee32eaa64c2eaa4a75d7553de972e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Deschner?= Date: Thu, 17 Sep 2020 14:42:52 +0200 Subject: [PATCH 16/19] CVE-2020-1472(ZeroLogon): s3:rpc_server/netlogon: log warnings about unsecure configurations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Günther Deschner Signed-off-by: Stefan Metzmacher --- source3/rpc_server/netlogon/srv_netlog_nt.c | 70 +++++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c index dc1bfa0c4da..c217fee9c43 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -1075,11 +1075,13 @@ static NTSTATUS netr_creds_server_step_check(struct pipes_struct *p, NTSTATUS status; bool schannel_global_required = (lp_server_schannel() == true) ? true:false; bool schannel_required = schannel_global_required; + const char *explicit_opt = NULL; struct loadparm_context *lp_ctx; struct netlogon_creds_CredentialState *creds = NULL; enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE; uint16_t opnum = p->opnum; const char *opname = ""; + static bool warned_global_once = false; if (creds_out != NULL) { *creds_out = NULL; @@ -1107,10 +1109,20 @@ static NTSTATUS netr_creds_server_step_check(struct pipes_struct *p, return status; } - schannel_required = lp_parm_bool(GLOBAL_SECTION_SNUM, - "server require schannel", - creds->account_name, - schannel_global_required); + /* + * We don't use lp_parm_bool(), as we + * need the explicit_opt pointer in order to + * adjust the debug messages. + */ + + explicit_opt = lp_parm_const_string(GLOBAL_SECTION_SNUM, + "server require schannel", + creds->account_name, + NULL); + if (explicit_opt != NULL) { + schannel_required = lp_bool(explicit_opt); + } + if (schannel_required) { if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { *creds_out = creds; @@ -1123,11 +1135,61 @@ static NTSTATUS netr_creds_server_step_check(struct pipes_struct *p, opname, opnum, log_escape(mem_ctx, creds->account_name), log_escape(mem_ctx, creds->computer_name)); + DBG_ERR("CVE-2020-1472(ZeroLogon): Check if option " + "'server require schannel:%s = no' is needed! \n", + log_escape(mem_ctx, creds->account_name)); TALLOC_FREE(creds); ZERO_STRUCTP(return_authenticator); return NT_STATUS_ACCESS_DENIED; } + if (!schannel_global_required && !warned_global_once) { + /* + * We want admins to notice their misconfiguration! + */ + DBG_ERR("CVE-2020-1472(ZeroLogon): " + "Please configure 'server schannel = yes', " + "See https://bugzilla.samba.org/show_bug.cgi?id=14497\n"); + warned_global_once = true; + } + + if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL) { + DBG_ERR("CVE-2020-1472(ZeroLogon): " + "%s request (opnum[%u]) WITH schannel from " + "client_account[%s] client_computer_name[%s]\n", + opname, opnum, + log_escape(mem_ctx, creds->account_name), + log_escape(mem_ctx, creds->computer_name)); + DBG_ERR("CVE-2020-1472(ZeroLogon): " + "Option 'server require schannel:%s = no' not needed!?\n", + log_escape(mem_ctx, creds->account_name)); + + *creds_out = creds; + return NT_STATUS_OK; + } + + if (explicit_opt != NULL) { + DBG_INFO("CVE-2020-1472(ZeroLogon): " + "%s request (opnum[%u]) without schannel from " + "client_account[%s] client_computer_name[%s]\n", + opname, opnum, + log_escape(mem_ctx, creds->account_name), + log_escape(mem_ctx, creds->computer_name)); + DBG_INFO("CVE-2020-1472(ZeroLogon): " + "Option 'server require schannel:%s = no' still needed!\n", + log_escape(mem_ctx, creds->account_name)); + } else { + DBG_ERR("CVE-2020-1472(ZeroLogon): " + "%s request (opnum[%u]) without schannel from " + "client_account[%s] client_computer_name[%s]\n", + opname, opnum, + log_escape(mem_ctx, creds->account_name), + log_escape(mem_ctx, creds->computer_name)); + DBG_ERR("CVE-2020-1472(ZeroLogon): Check if option " + "'server require schannel:%s = no' might be needed!\n", + log_escape(mem_ctx, creds->account_name)); + } + *creds_out = creds; return NT_STATUS_OK; } -- 2.20.1 From 6f6fae3479ffc5630939480fa54a293e2c547d73 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 17 Sep 2020 17:27:54 +0200 Subject: [PATCH 17/19] CVE-2020-1472(ZeroLogon): docs-xml: document 'server require schannel:COMPUTERACCOUNT' BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497 Signed-off-by: Stefan Metzmacher --- .../smbdotconf/security/serverschannel.xml | 69 +++++++++++++++---- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/docs-xml/smbdotconf/security/serverschannel.xml b/docs-xml/smbdotconf/security/serverschannel.xml index 489492d79b1..b682d086f76 100644 --- a/docs-xml/smbdotconf/security/serverschannel.xml +++ b/docs-xml/smbdotconf/security/serverschannel.xml @@ -7,26 +7,65 @@ - This option is deprecated with Samba 4.8 and will be removed in future. - At the same time the default changed to yes, which will be the - hardcoded behavior in future. If you have the need for the behavior of "auto" - to be kept, please file a bug at https://bugzilla.samba.org. + This option is deprecated and will be removed in future, + as it is a security problem if not set to "yes" (which will be + the hardcoded behavior in future). - This controls whether the server offers or even demands the use of the netlogon schannel. - no does not offer the schannel, auto offers the schannel but does not enforce it, and yes denies access if the client is not able to speak netlogon schannel. - This is only the case for Windows NT4 before SP4. - - + Samba will complain in the log files at log level 0, + about the security problem if the option is not set to "yes". + - Please note that with this set to no, you will have to apply the WindowsXP - WinXP_SignOrSeal.reg registry patch found in the docs/registry subdirectory of the Samba distribution tarball. - + See CVE-2020-1472(ZeroLogon) https://bugzilla.samba.org/show_bug.cgi?id=14497 + + + If you still have legacy domain members use the option. + + + This option yields precedence to the option. + yes -auto + + + + + + If you still have legacy domain members, which required "server schannel = auto" before, + it is possible to specify explicit expection per computer account + by using 'server require schannel:COMPUTERACCOUNT = no' as option. + Note that COMPUTERACCOUNT has to be the sAMAccountName value of + the computer account (including the trailing '$' sign). + + + + Samba will complain in the log files at log level 0, + about the security problem if the option is not set to "no", + but the related computer is actually using the netlogon + secure channel (schannel) feature. + + + + Samba will warn in the log files at log level 5, + if a setting is still needed for the specified computer account. + + + + See CVE-2020-1472(ZeroLogon) https://bugzilla.samba.org/show_bug.cgi?id=14497 + + + This option takes precedence to the option. + + + server require schannel:LEGACYCOMPUTER1$ = no + server require schannel:NASBOX$ = no + server require schannel:LEGACYCOMPUTER2$ = no + + + -- 2.20.1 From b7ccad46a8d2b32e6e6c562893c39c4058f0bdbb Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Fri, 18 Sep 2020 12:39:54 +1200 Subject: [PATCH 18/19] 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 --- 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 826793717e7..af9d94b99ff 100644 --- a/source4/torture/rpc/netlogon.c +++ b/source4/torture/rpc/netlogon.c @@ -725,45 +725,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.20.1 From 9ea1ec31eb6b116d8d85abc1003eef8619e540e4 Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Fri, 18 Sep 2020 15:57:34 +1200 Subject: [PATCH 19/19] 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 [abartlet@samba.org: backported from master as test order was flipped] --- source4/torture/rpc/netlogon.c | 335 +++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c index af9d94b99ff..c9e614fda30 100644 --- a/source4/torture/rpc/netlogon.c +++ b/source4/torture/rpc/netlogon.c @@ -486,6 +486,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 */ @@ -4954,6 +5273,22 @@ struct torture_suite *torture_rpc_netlogon(TALLOC_CTX *mem_ctx) torture_rpc_tcase_add_test(tcase, "lsa_over_netlogon", test_lsa_over_netlogon); torture_rpc_tcase_add_test_creds(tcase, "SetupCredentialsDowngrade", test_SetupCredentialsDowngrade); + 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.20.1