From 46b7eb7737b5ce7e0f6b9d03a502baf051a7f3cf Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Wed, 2 Oct 2024 19:06:59 +0200
Subject: [PATCH] libcli/auth: also use
 netlogon_creds_CredentialState_extra_info for the client

In order to allow backports and cluster updates we simulate a
dom_sid, so that the old code is able to parse the blob.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15425

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
(cherry picked from commit 8b972fea0978101575f847eac33b09d2fd8d02e7)
---
 libcli/auth/credentials.c             | 15 ++++++++-
 libcli/auth/netlogon_creds_cli.c      | 13 ++++++--
 libcli/auth/proto.h                   |  1 +
 librpc/idl/schannel.idl               | 47 ++++++++++++++++++++++++++-
 source4/librpc/rpc/dcerpc_schannel.c  |  1 +
 source4/torture/ntp/ntp_signd.c       |  1 +
 source4/torture/rpc/lsa.c             |  2 ++
 source4/torture/rpc/netlogon.c        | 22 +++++++++++++
 source4/torture/rpc/netlogon_crypto.c |  1 +
 source4/torture/rpc/samba3rpc.c       |  8 +++--
 10 files changed, 104 insertions(+), 7 deletions(-)

diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c
index 59db4bc28ea..75ac4ddb0f2 100644
--- a/libcli/auth/credentials.c
+++ b/libcli/auth/credentials.c
@@ -491,16 +491,19 @@ struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *me
 								  const struct netr_Credential *server_challenge,
 								  const struct samr_Password *machine_password,
 								  struct netr_Credential *initial_credential,
+								  uint32_t client_requested_flags,
 								  uint32_t negotiate_flags)
 {
 	struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState);
+	struct timeval tv = timeval_current();
+	NTTIME now = timeval_to_nttime(&tv);
 	NTSTATUS status;
 
 	if (!creds) {
 		return NULL;
 	}
 
-	creds->sequence = time(NULL);
+	creds->sequence = tv.tv_sec;
 	creds->negotiate_flags = negotiate_flags;
 	creds->secure_channel_type = secure_channel_type;
 
@@ -515,6 +518,16 @@ struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *me
 		return NULL;
 	}
 
+	creds->ex = talloc_zero(creds,
+			struct netlogon_creds_CredentialState_extra_info);
+	if (creds->ex == NULL) {
+		talloc_free(creds);
+		return NULL;
+	}
+	creds->ex->client_requested_flags = client_requested_flags;
+	creds->ex->auth_time = now;
+	creds->ex->client_sid = global_sid_NULL;
+
 	dump_data_pw("Client chall", client_challenge->data, sizeof(client_challenge->data));
 	dump_data_pw("Server chall", server_challenge->data, sizeof(server_challenge->data));
 	dump_data_pw("Machine Pass", machine_password->hash, sizeof(machine_password->hash));
diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c
index 40a40cb99ae..35a5462ff2e 100644
--- a/libcli/auth/netlogon_creds_cli.c
+++ b/libcli/auth/netlogon_creds_cli.c
@@ -1355,6 +1355,7 @@ static void netlogon_creds_cli_auth_challenge_done(struct tevent_req *subreq)
 						  &state->server_challenge,
 						  state->used_nt_hash,
 						  &state->client_credential,
+						  state->context->client.proposed_flags,
 						  state->current_flags);
 	if (tevent_req_nomem(state->creds, req)) {
 		return;
@@ -1558,6 +1559,7 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq)
 		return;
 	}
 
+	state->creds->ex->client_sid.sub_auths[0] = state->rid;
 	status = netlogon_creds_cli_store_internal(state->context,
 						   state->creds);
 	if (tevent_req_nterror(req, status)) {
@@ -1935,6 +1937,7 @@ static void netlogon_creds_cli_check_client_caps(struct tevent_req *subreq)
 	struct netlogon_creds_cli_check_state *state =
 		tevent_req_data(req,
 		struct netlogon_creds_cli_check_state);
+	uint32_t requested_flags;
 	NTSTATUS status;
 	NTSTATUS result;
 	bool ok;
@@ -1986,9 +1989,13 @@ static void netlogon_creds_cli_check_client_caps(struct tevent_req *subreq)
 		return;
 	}
 
-	if (state->client_caps.requested_flags !=
-	    state->context->client.proposed_flags)
-	{
+	if (state->creds->ex != NULL) {
+		requested_flags = state->creds->ex->client_requested_flags;
+	} else {
+		requested_flags = state->context->client.proposed_flags;
+	}
+
+	if (state->client_caps.requested_flags != requested_flags) {
 		status = NT_STATUS_DOWNGRADE_DETECTED;
 		tevent_req_nterror(req, status);
 		netlogon_creds_cli_check_cleanup(req, status);
diff --git a/libcli/auth/proto.h b/libcli/auth/proto.h
index 3094292657a..ae68aab192a 100644
--- a/libcli/auth/proto.h
+++ b/libcli/auth/proto.h
@@ -46,6 +46,7 @@ struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *me
 								  const struct netr_Credential *server_challenge,
 								  const struct samr_Password *machine_password,
 								  struct netr_Credential *initial_credential,
+								  uint32_t client_requested_flags,
 								  uint32_t negotiate_flags);
 NTSTATUS
 netlogon_creds_client_authenticator(struct netlogon_creds_CredentialState *creds,
diff --git a/librpc/idl/schannel.idl b/librpc/idl/schannel.idl
index 619e9e5591c..8905d514f55 100644
--- a/librpc/idl/schannel.idl
+++ b/librpc/idl/schannel.idl
@@ -20,8 +20,53 @@ interface schannel
 		 * with a single dom_sid for the client_sid.
 		 *
 		 * On the server we use CLEAR_IF_FIRST,
-		 * so db layout changes don't matter there.
+		 * so db layout changes don't matter there,
+		 * but on the client side we need to handle
+		 * the ctdb case were CLEAR_IF_FIRST only
+		 * works if all cluster nodes are restarted.
+		 *
+		 * As this was a single dom_sid before,
+		 * we add some magic in order to let
+		 * old code (on other nodes to parse the new layout).
+		 *
+		 * We have basically this definition of dom_sid:
+		 *
+		 * typedef struct {
+		 *    uint8 sid_rev_num;
+		 *    [range(0,15)] int8 num_auths;
+		 *    uint8  id_auth[6];
+		 *    uint32 sub_auths[num_auths];
+		 * } dom_sid;
+		 *
+		 * It means it consumes at least 8 bytes while
+		 * and it's also 4 byte aligned (before sid_rev_num).
+		 * The largest sid would have 68 bytes.
+		 *
+		 * The old client side code would see a sid like
+		 * this: S-1-RSV-CRF-ATL-ATH-257-0-RID
+		 *
+		 * RSV => reserved (the last 4 bytes of id_auth)
+		 *
+		 * CRF => client_requested_flags (sub_auths[0]
+		 *
+		 * Note NTTIME used ndr_pull_udlong, it's not NTTIME_hyper!
+		 * ATL => low 4 bytes of auth_time (sub_auths[1])
+		 * ATH => high 4 bytes of auth_time (sub_auths[2])
+		 *
+		 * From client_sid (S-1-0-RID): sub_auth[3-5]
+		 *
+		 * 257 => 0x01 0x01 0x00 0x00 =
+		 *        (sid_rev_num = 1, num_auths =1,
+		 *         id_auth[0] = 0, id_auth[1] = 0)
+		 * 0   => id_auth[2-6]
+		 *
+		 * RID => the RID of the client
+		 *
+		 * It means the magic needs to simulate
+		 * num_auths = 6
 		 */
+		[value(0x00000601)] uint32 magic;
+		[value(0)] uint32 reserved;
 		netr_NegotiateFlags client_requested_flags;
 		NTTIME auth_time;
 		dom_sid client_sid;
diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c
index 9d96abd2521..ca0495c4d47 100644
--- a/source4/librpc/rpc/dcerpc_schannel.c
+++ b/source4/librpc/rpc/dcerpc_schannel.c
@@ -197,6 +197,7 @@ static void continue_srv_challenge(struct tevent_req *subreq)
 					      s->a.in.secure_channel_type,
 					      &s->credentials1, &s->credentials2,
 					      s->mach_pwd, &s->credentials3,
+					      s->requested_negotiate_flags,
 					      s->local_negotiate_flags);
 	if (composite_nomem(s->creds, c)) {
 		return;
diff --git a/source4/torture/ntp/ntp_signd.c b/source4/torture/ntp/ntp_signd.c
index fd7da67c551..2bf8e0fe127 100644
--- a/source4/torture/ntp/ntp_signd.c
+++ b/source4/torture/ntp/ntp_signd.c
@@ -119,6 +119,7 @@ static bool test_ntp_signd(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2, 
 					   pwhash, &credentials3,
+					   negotiate_flags,
 					   negotiate_flags);
 	
 	torture_assert(tctx, creds != NULL, "memory allocation");
diff --git a/source4/torture/rpc/lsa.c b/source4/torture/rpc/lsa.c
index d91a5414de1..60186693ea2 100644
--- a/source4/torture/rpc/lsa.c
+++ b/source4/torture/rpc/lsa.c
@@ -2951,6 +2951,7 @@ static bool check_pw_with_ServerAuthenticate3(struct dcerpc_pipe *p,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   new_password, &credentials3,
+					   negotiate_flags,
 					   negotiate_flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -2976,6 +2977,7 @@ static bool check_pw_with_ServerAuthenticate3(struct dcerpc_pipe *p,
 						   a.in.secure_channel_type,
 						   &credentials1, &credentials2,
 						   old_password, &credentials3,
+						   negotiate_flags,
 						   negotiate_flags);
 
 		torture_assert(tctx, creds != NULL, "memory allocation");
diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c
index 6b403e2cf50..c9a9202892e 100644
--- a/source4/torture/rpc/netlogon.c
+++ b/source4/torture/rpc/netlogon.c
@@ -180,6 +180,7 @@ bool test_SetupCredentials(struct dcerpc_pipe *p, struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   mach_password, &credentials3,
+					   0,
 					   0);
 	torture_assert(tctx, creds != NULL, "memory allocation");
 
@@ -251,6 +252,7 @@ bool test_SetupCredentials2ex(struct dcerpc_pipe *p, struct torture_context *tct
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   mach_password, &credentials3,
+					   negotiate_flags,
 					   negotiate_flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -349,6 +351,7 @@ bool test_SetupCredentials3(struct dcerpc_pipe *p, struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   negotiate_flags,
 					   negotiate_flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -421,6 +424,7 @@ bool test_SetupCredentialsDowngrade(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   negotiate_flags,
 					   negotiate_flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -437,6 +441,7 @@ bool test_SetupCredentialsDowngrade(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   negotiate_flags,
 					   negotiate_flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -541,6 +546,7 @@ static bool test_ServerReqChallenge(
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   mach_password, &credentials3,
+					   in_negotiate_flags,
 					   in_negotiate_flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -618,6 +624,7 @@ static bool test_ServerReqChallenge_zero_challenge(
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   mach_password, &credentials3,
+					   in_negotiate_flags,
 					   in_negotiate_flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -702,6 +709,7 @@ static bool test_ServerReqChallenge_5_repeats(
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   mach_password, &credentials3,
+					   in_negotiate_flags,
 					   in_negotiate_flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -788,6 +796,7 @@ static bool test_ServerReqChallenge_4_repeats(
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   mach_password, &credentials3,
+					   in_negotiate_flags,
 					   in_negotiate_flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -881,6 +890,7 @@ static bool test_ServerAuthenticate2_encrypts_to_zero(
 			&credentials2,
 			mach_password,
 			&credentials3,
+			flags,
 			flags);
 
 		torture_assert(tctx, creds != NULL, "memory allocation");
@@ -2272,6 +2282,7 @@ static bool test_ServerReqChallengeGlobal(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -2361,6 +2372,7 @@ static bool test_ServerReqChallengeReuseGlobal(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -2378,6 +2390,7 @@ static bool test_ServerReqChallengeReuseGlobal(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b3, tctx, &a),
@@ -2450,6 +2463,7 @@ static bool test_ServerReqChallengeReuseGlobal2(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -2467,6 +2481,7 @@ static bool test_ServerReqChallengeReuseGlobal2(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a),
@@ -2540,6 +2555,7 @@ static bool test_ServerReqChallengeReuseGlobal3(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -2557,6 +2573,7 @@ static bool test_ServerReqChallengeReuseGlobal3(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -2655,6 +2672,7 @@ static bool test_ServerReqChallengeReuseGlobal4(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -2672,6 +2690,7 @@ static bool test_ServerReqChallengeReuseGlobal4(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b1, tctx, &a),
@@ -2731,6 +2750,7 @@ static bool test_ServerReqChallengeReuse(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
@@ -2748,6 +2768,7 @@ static bool test_ServerReqChallengeReuse(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a),
@@ -2762,6 +2783,7 @@ static bool test_ServerReqChallengeReuse(struct torture_context *tctx,
 					   a.in.secure_channel_type,
 					   &credentials1, &credentials2,
 					   &mach_password, &credentials3,
+					   flags,
 					   flags);
 
 	torture_assert(tctx, creds != NULL, "memory allocation");
diff --git a/source4/torture/rpc/netlogon_crypto.c b/source4/torture/rpc/netlogon_crypto.c
index eec8a753179..858a17e0ea8 100644
--- a/source4/torture/rpc/netlogon_crypto.c
+++ b/source4/torture/rpc/netlogon_crypto.c
@@ -124,6 +124,7 @@ static bool test_ServerAuth3Crypto(struct dcerpc_pipe *p,
 						 &netr_creds2,
 						 &machine_password,
 						 &netr_creds3,
+						 negotiate_flags,
 						 negotiate_flags);
 	GNUTLS_FIPS140_SET_STRICT_MODE();
 	/* Test that we fail to encrypt with RC4 */
diff --git a/source4/torture/rpc/samba3rpc.c b/source4/torture/rpc/samba3rpc.c
index 10f0fccde73..96ff34f5270 100644
--- a/source4/torture/rpc/samba3rpc.c
+++ b/source4/torture/rpc/samba3rpc.c
@@ -1091,7 +1091,9 @@ static bool auth2(struct torture_context *tctx,
 						 a.in.secure_channel_type,
 						 r.in.credentials,
 						 r.out.return_credentials, &mach_pw,
-						 &netr_cred, negotiate_flags);
+						 &netr_cred,
+						 negotiate_flags,
+						 negotiate_flags);
 	torture_assert(tctx, (creds_state != NULL), "memory allocation failed");
 
 	status = dcerpc_netr_ServerAuthenticate2_r(net_handle, mem_ctx, &a);
@@ -2158,7 +2160,9 @@ static bool torture_samba3_rpc_randomauth2(struct torture_context *torture)
 						 a.in.secure_channel_type,
 						 r.in.credentials,
 						 r.out.return_credentials, &mach_pw,
-						 &netr_cred, negotiate_flags);
+						 &netr_cred,
+						 negotiate_flags,
+						 negotiate_flags);
 	torture_assert(torture, (creds_state != NULL), "memory allocation failed");
 
 	status = dcerpc_netr_ServerAuthenticate2_r(net_handle, mem_ctx, &a);