From 02e4376ac531f8573278dea2a7a98b2dfa32b58d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:04:57 +0200 Subject: [PATCH 01/18] 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 | 8 ++++++++ libcli/auth/proto.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c index b6c8ba281ba..dbbef9e7a3c 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -26,9 +26,17 @@ #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) + { if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) { AES_KEY key; diff --git a/libcli/auth/proto.h b/libcli/auth/proto.h index 82febe74440..82797d453ed 100644 --- a/libcli/auth/proto.h +++ b/libcli/auth/proto.h @@ -11,6 +11,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); + void netlogon_creds_des_encrypt_LMKey(struct netlogon_creds_CredentialState *creds, struct netr_LMSessionKey *key); void netlogon_creds_des_decrypt_LMKey(struct netlogon_creds_CredentialState *creds, struct netr_LMSessionKey *key); void netlogon_creds_des_encrypt(struct netlogon_creds_CredentialState *creds, struct samr_Password *pass); -- 2.20.1 From 7ef780886ff1911fd389b02f781fad2860a77f83 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:07:30 +0200 Subject: [PATCH 02/18] 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 21cc16afbaf..7bdc0cf679a 100644 --- a/source4/torture/rpc/lsa.c +++ b/source4/torture/rpc/lsa.c @@ -2847,7 +2847,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 026d86d50e4..e11014922f8 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"); @@ -318,7 +318,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"); @@ -390,7 +390,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"); @@ -1278,7 +1278,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"); @@ -1367,7 +1367,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"); @@ -1456,7 +1456,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"); @@ -1546,7 +1546,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"); @@ -1638,8 +1638,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"); @@ -1651,7 +1650,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"); @@ -1662,16 +1661,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"); @@ -1747,7 +1737,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 e018b705cadb0fb0414f0d9d82dfc6faf8ca1556 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:08:38 +0200 Subject: [PATCH 03/18] 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 817d2cd041a..0f6ca11ff96 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 d9df268d9d1f7eea34102ec596c5995d6a8294bb Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:10:53 +0200 Subject: [PATCH 04/18] 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 d799ba4feef..61169fff17b 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -840,8 +840,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 a3216d7964212c68f4415173519c8c47dba7954e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:10:53 +0200 Subject: [PATCH 05/18] 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 023adfd99e9..de260d8051d 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 543772ca23bd2869423e5be666f7af6814a91e6c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:15:26 +0200 Subject: [PATCH 06/18] 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 dbbef9e7a3c..64b424c099f 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -27,10 +27,31 @@ #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 82797d453ed..ad768682b9f 100644 --- a/libcli/auth/proto.h +++ b/libcli/auth/proto.h @@ -11,6 +11,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); void netlogon_creds_des_encrypt_LMKey(struct netlogon_creds_CredentialState *creds, struct netr_LMSessionKey *key); -- 2.20.1 From 52fae84d0b28759e68b3314850f2589a3bea180a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 16:17:29 +0200 Subject: [PATCH 07/18] 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 [dbagnall@samba.org, abartlet@samba.org: wscript_build backport differs because 4.10 has no gnutls dependency] --- 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 64b424c099f..e2bc82809b7 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) @@ -451,6 +452,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; @@ -463,6 +465,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 d319d9b879e..394505d166d 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', + public_deps='MSRPC_PARSE util_str_escape', public_headers='credentials.h:domain_credentials.h' ) -- 2.20.1 From 81d4dee9adcf230524c2f282319cc55f76c3f87e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 19:20:25 +0200 Subject: [PATCH 08/18] 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 de260d8051d..acbf077c6c7 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -722,7 +722,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; @@ -780,6 +783,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 f8081e37726c7edcb814429e08a928f941ba088f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 16 Sep 2020 12:53:50 -0700 Subject: [PATCH 09/18] 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 61169fff17b..989770bd0ae 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -1326,9 +1326,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, @@ -1364,18 +1369,99 @@ NTSTATUS _netr_ServerPasswordSet2(struct pipes_struct *p, netlogon_creds_arcfour_crypt(creds, password_buf.data, 516); } - 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 606648ad0f1efbda3311ef566735686aace991fd Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 10:18:45 +0200 Subject: [PATCH 10/18] 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 acbf077c6c7..b4326a4ecaa 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -623,26 +623,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 4497e552568ab692869a0702970b6fd530cf9575 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Sep 2020 10:56:53 +0200 Subject: [PATCH 11/18] 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 b4326a4ecaa..e7bafb31e83 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -623,6 +623,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; @@ -645,7 +646,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 b689e99601c114db4aed9810e98106c5296185ac Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 17 Sep 2020 13:37:26 +0200 Subject: [PATCH 12/18] 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 e7bafb31e83..7668a9eb923 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -624,10 +624,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; @@ -646,11 +648,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) { @@ -664,11 +673,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 8185efbf6bb8015bc01fe0070a7cc7d87273485e 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 13/18] 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 989770bd0ae..50455ee4bce 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; @@ -1073,19 +1074,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")); @@ -1094,9 +1097,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 2b3e932112bd7ef4edb651b482e4ec7d04c93b5c 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 14/18] 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 50455ee4bce..28381405ebd 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -1073,6 +1073,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; @@ -1105,7 +1106,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 763342961a69873ef2ce874ff9ae7e2af7a57f5d 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 15/18] 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 28381405ebd..c36c247c55c 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -1074,11 +1074,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; @@ -1106,10 +1108,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; @@ -1122,11 +1134,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 907dc07f23e02347a95b26c939dcd8ce18af0787 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 17 Sep 2020 17:27:54 +0200 Subject: [PATCH 16/18] 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 b1f7eb7fbf54e2c4849a1b5d5d1303e8e4511933 Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Fri, 18 Sep 2020 12:39:54 +1200 Subject: [PATCH 17/18] 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 e11014922f8..0ba45f0c1da 100644 --- a/source4/torture/rpc/netlogon.c +++ b/source4/torture/rpc/netlogon.c @@ -719,45 +719,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 e1f241fe0373c7afdebef03c24f6eb6814189590 Mon Sep 17 00:00:00 2001 From: Gary Lockyer Date: Fri, 18 Sep 2020 15:57:34 +1200 Subject: [PATCH 18/18] 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 0ba45f0c1da..97c16688bc9 100644 --- a/source4/torture/rpc/netlogon.c +++ b/source4/torture/rpc/netlogon.c @@ -480,6 +480,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 */ @@ -4949,6 +5268,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