summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2020-09-18 15:38:14 +0200
committerStefan Metzmacher <metze@samba.org>2020-09-18 15:38:55 +0200
commit70e9e595121161778efae0118cad1e0895dce372 (patch)
tree4c648835a8ed0f9fd11b8d6da1326be1b5713fdc
parent061d484f6da666dd5cb14ae665eeb0d4611d36f7 (diff)
parentef64fc24b31f057bd344e7135d32732c8b3057e2 (diff)
downloadsamba-70e9e595121161778efae0118cad1e0895dce372.tar.gz
Merge tag 'samba-4.11.13' into v4-11-test
samba: tag release samba-4.11.13 Signed-off-by: Stefan Metzmacher <metze@samba.org>
-rw-r--r--VERSION2
-rw-r--r--WHATSNEW.txt107
-rw-r--r--docs-xml/smbdotconf/security/serverschannel.xml69
-rw-r--r--libcli/auth/credentials.c44
-rw-r--r--libcli/auth/netlogon_creds_cli.c3
-rw-r--r--libcli/auth/proto.h4
-rw-r--r--libcli/auth/wscript_build2
-rw-r--r--source3/rpc_server/netlogon/srv_netlog_nt.c212
-rw-r--r--source4/rpc_server/netlogon/dcerpc_netlogon.c175
-rw-r--r--source4/torture/rpc/lsa.c2
-rw-r--r--source4/torture/rpc/netlogon.c433
11 files changed, 943 insertions, 110 deletions
diff --git a/VERSION b/VERSION
index 5885b36fd71..cd93dc7e95f 100644
--- a/VERSION
+++ b/VERSION
@@ -99,7 +99,7 @@ SAMBA_VERSION_RC_RELEASE=
# e.g. SAMBA_VERSION_IS_SVN_SNAPSHOT=yes #
# -> "3.0.0-SVN-build-199" #
########################################################
-SAMBA_VERSION_IS_GIT_SNAPSHOT=yes
+SAMBA_VERSION_IS_GIT_SNAPSHOT=no
########################################################
# This is for specifying a release nickname #
diff --git a/WHATSNEW.txt b/WHATSNEW.txt
index 80e5f32b1a0..76dc4cc0d5a 100644
--- a/WHATSNEW.txt
+++ b/WHATSNEW.txt
@@ -1,4 +1,107 @@
===============================
+ Release Notes for Samba 4.11.13
+ September 18, 2020
+ ===============================
+
+
+This is a security release in order to address the following defect:
+
+o CVE-2020-1472: Unauthenticated domain takeover via netlogon ("ZeroLogon").
+
+The following applies to Samba used as domain controller only (most
+seriously the Active Directory DC, but also the classic/NT4-style DC).
+
+Installations running Samba as a file server only are not directly
+affected by this flaw, though they may need configuration changes to
+continue to talk to domain controllers (see "file servers and domain
+members" below).
+
+The netlogon protocol contains a flaw that allows an authentication
+bypass. This was reported and patched by Microsoft as CVE-2020-1472.
+Since the bug is a protocol level flaw, and Samba implements the
+protocol, Samba is also vulnerable.
+
+However, since version 4.8 (released in March 2018), the default
+behaviour of Samba has been to insist on a secure netlogon channel,
+which is a sufficient fix against the known exploits. This default is
+equivalent to having 'server schannel = yes' in the smb.conf.
+
+Therefore versions 4.8 and above are not vulnerable unless they have
+the smb.conf lines 'server schannel = no' or 'server schannel = auto'.
+
+Samba versions 4.7 and below are vulnerable unless they have 'server
+schannel = yes' in the smb.conf.
+
+Note each domain controller needs the correct settings in its smb.conf.
+
+Vendors supporting Samba 4.7 and below are advised to patch their
+installations and packages to add this line to the [global] section if
+their smb.conf file.
+
+The 'server schannel = yes' smb.conf line is equivalent to Microsoft's
+'FullSecureChannelProtection=1' registry key, the introduction of
+which we understand forms the core of Microsoft's fix.
+
+Some domains employ third-party software that will not work with a
+'server schannel = yes'. For these cases patches are available that
+allow specific machines to use insecure netlogon. For example, the
+following smb.conf:
+
+ server schannel = yes
+ server require schannel:triceratops$ = no
+ server require schannel:greywacke$ = no
+
+will allow only "triceratops$" and "greywacke$" to avoid schannel.
+
+More details can be found here:
+https://www.samba.org/samba/security/CVE-2020-1472.html
+
+
+Changes since 4.11.12
+---------------------
+
+o Jeremy Allison <jra@samba.org>
+ * BUG 14497: CVE-2020-1472(ZeroLogon): s3:rpc_server/netlogon: Protect
+ netr_ServerPasswordSet2 against unencrypted passwords.
+
+o Günther Deschner <gd@samba.org>
+ * BUG 14497: CVE-2020-1472(ZeroLogon): s3:rpc_server/netlogon: Support
+ "server require schannel:WORKSTATION$ = no" about unsecure configurations.
+
+o Gary Lockyer <gary@catalyst.net.nz>
+ * BUG 14497: CVE-2020-1472(ZeroLogon): s4 torture rpc: repeated bytes in
+ client challenge.
+
+o Stefan Metzmacher <metze@samba.org>
+ * BUG 14497: CVE-2020-1472(ZeroLogon): libcli/auth: Reject weak client
+ challenges in netlogon_creds_server_init()
+ "server require schannel:WORKSTATION$ = no".
+
+
+#######################################
+Reporting bugs & Development Discussion
+#######################################
+
+Please discuss this release on the samba-technical mailing list or by
+joining the #samba-technical IRC channel on irc.freenode.net.
+
+If you do report problems then please try to send high quality
+feedback. If you don't provide vital information to help us track down
+the problem then you will probably be ignored. All bug reports should
+be filed under the "Samba 4.1 and newer" product in the project's Bugzilla
+database (https://bugzilla.samba.org/).
+
+
+======================================================================
+== Our Code, Our Bugs, Our Responsibility.
+== The Samba Team
+======================================================================
+
+
+Release notes for older releases follow:
+----------------------------------------
+
+ ===============================
Release Notes for Samba 4.11.12
August 25, 2020
===============================
@@ -64,8 +167,8 @@ database (https://bugzilla.samba.org/).
======================================================================
-Release notes for older releases follow:
-----------------------------------------
+----------------------------------------------------------------------
+
===============================
Release Notes for Samba 4.11.11
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 @@
<description>
<para>
- 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).
</para>
<para>
- This controls whether the server offers or even demands the use of the netlogon schannel.
- <smbconfoption name="server schannel">no</smbconfoption> does not offer the schannel, <smbconfoption
- name="server schannel">auto</smbconfoption> offers the schannel but does not enforce it, and <smbconfoption
- name="server schannel">yes</smbconfoption> denies access if the client is not able to speak netlogon schannel.
- This is only the case for Windows NT4 before SP4.
- </para>
-
+ Samba will complain in the log files at log level 0,
+ about the security problem if the option is not set to "yes".
+ </para>
<para>
- Please note that with this set to <literal>no</literal>, you will have to apply the WindowsXP
- <filename>WinXP_SignOrSeal.reg</filename> registry patch found in the docs/registry subdirectory of the Samba distribution tarball.
- </para>
+ See CVE-2020-1472(ZeroLogon) https://bugzilla.samba.org/show_bug.cgi?id=14497
+ </para>
+
+ <para>If you still have legacy domain members use the <smbconfoption name="server require schannel:COMPUTERACCOUNT"/> option.
+ </para>
+
+ <para>This option yields precedence to the <smbconfoption name="server require schannel:COMPUTERACCOUNT"/> option.</para>
+
</description>
<value type="default">yes</value>
-<value type="example">auto</value>
+</samba:parameter>
+
+<samba:parameter name="server require schannel:COMPUTERACCOUNT"
+ context="G"
+ type="string"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+
+ <para>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).
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ Samba will warn in the log files at log level 5,
+ if a setting is still needed for the specified computer account.
+ </para>
+
+ <para>
+ See CVE-2020-1472(ZeroLogon) https://bugzilla.samba.org/show_bug.cgi?id=14497
+ </para>
+
+ <para>This option takes precedence to the <smbconfoption name="server schannel"/> option.</para>
+
+ <programlisting>
+ server require schannel:LEGACYCOMPUTER1$ = no
+ server require schannel:NASBOX$ = no
+ server require schannel:LEGACYCOMPUTER2$ = no
+ </programlisting>
+</description>
+
</samba:parameter>
diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c
index 319dacdac0b..0ba1d95afd3 100644
--- a/libcli/auth/credentials.c
+++ b/libcli/auth/credentials.c
@@ -25,14 +25,43 @@
#include "../lib/crypto/crypto.h"
#include "libcli/auth/libcli_auth.h"
#include "../libcli/security/dom_sid.h"
+#include "lib/util/util_str_escape.h"
#include "lib/crypto/gnutls_helpers.h"
#include <gnutls/gnutls.h>
#include <gnutls/crypto.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);
+ 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,
const struct netr_Credential *in,
struct netr_Credential *out)
+
{
if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
AES_KEY key;
@@ -506,6 +535,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;
@@ -518,6 +548,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/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c
index 50a5f50a57d..0b31dc91b4b 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,
diff --git a/libcli/auth/proto.h b/libcli/auth/proto.h
index afd7f0d148d..51d5deaab2d 100644
--- a/libcli/auth/proto.h
+++ b/libcli/auth/proto.h
@@ -11,10 +11,14 @@
/* 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);
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);
void netlogon_creds_des_decrypt(struct netlogon_creds_CredentialState *creds, struct samr_Password *pass);
+
NTSTATUS netlogon_creds_arcfour_crypt(struct netlogon_creds_CredentialState *creds,
uint8_t *data,
size_t len);
diff --git a/libcli/auth/wscript_build b/libcli/auth/wscript_build
index 04e2b09eadf..6f7d9a76404 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'
)
diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c
index c9aaa90cbb9..3221ebaa2e2 100644
--- a/source3/rpc_server/netlogon/srv_netlog_nt.c
+++ b/source3/rpc_server/netlogon/srv_netlog_nt.c
@@ -47,6 +47,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;
@@ -839,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;
@@ -1072,20 +1072,25 @@ 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 = "<unknown>";
+ static bool warned_global_once = false;
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 +1099,97 @@ 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;
+ }
+
+ /*
+ * 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;
+ 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));
+ 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;
}
@@ -1326,9 +1419,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,
@@ -1365,22 +1463,104 @@ NTSTATUS _netr_ServerPasswordSet2(struct pipes_struct *p,
password_buf.data,
516);
if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(creds);
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,
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index ac745e32b02..63e2821cac7 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;
@@ -623,26 +622,114 @@ 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;
+ 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 = "<unknown>";
+ static bool warned_global_once = false;
- 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;
+ }
+
+ /*
+ * 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);
+ if (explicit_opt != NULL) {
+ schannel_required = lp_bool(explicit_opt);
+ }
+
+ if (schannel_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));
+ 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;
}
/*
@@ -722,7 +809,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;
@@ -785,6 +875,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,
diff --git a/source4/torture/rpc/lsa.c b/source4/torture/rpc/lsa.c
index 0ce113feb5d..52370f4fe8e 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 94fe4020fa9..da4d407c627 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");
@@ -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
*/
@@ -725,45 +1044,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);
@@ -1283,7 +1596,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 +1685,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 +1774,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 +1864,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 +1956,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 +1968,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 +1979,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 +2055,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");
@@ -4970,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;
}