From ce2d1d0fd743ded18c9ad737d999f5c1120c4f6e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 21:02:23 +0200 Subject: [PATCH 01/12] libcli/auth: let netlogon_creds_copy() copy all scalar elements This version is good for now, as we want it to be backportable. For master we'll add a ndr_deepcopy_struct() helper in order to avoid future problems. Signed-off-by: Stefan Metzmacher --- libcli/auth/credentials.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c index 02e6fc6852b0..242c52b6c9bb 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -1190,9 +1190,7 @@ struct netlogon_creds_CredentialState *netlogon_creds_copy( return NULL; } - creds->sequence = creds_in->sequence; - creds->negotiate_flags = creds_in->negotiate_flags; - creds->secure_channel_type = creds_in->secure_channel_type; + *creds = *creds_in; creds->computer_name = talloc_strdup(creds, creds_in->computer_name); if (!creds->computer_name) { @@ -1213,10 +1211,5 @@ struct netlogon_creds_CredentialState *netlogon_creds_copy( } } - memcpy(creds->session_key, creds_in->session_key, sizeof(creds->session_key)); - memcpy(creds->seed.data, creds_in->seed.data, sizeof(creds->seed.data)); - memcpy(creds->client.data, creds_in->client.data, sizeof(creds->client.data)); - memcpy(creds->server.data, creds_in->server.data, sizeof(creds->server.data)); - return creds; } -- 2.34.1 From 92a60fe0e63cbc87d8107669534c095186eb931d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 20:55:55 +0200 Subject: [PATCH 02/12] librpc/ndr: add ndr_deepcopy_struct() helper Signed-off-by: Stefan Metzmacher --- librpc/ndr/libndr.h | 10 ++++++++++ librpc/ndr/ndr.c | 26 ++++++++++++++++++++++++++ librpc/wscript_build | 4 ++-- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/librpc/ndr/libndr.h b/librpc/ndr/libndr.h index 556d17f68c77..368f02958c53 100644 --- a/librpc/ndr/libndr.h +++ b/librpc/ndr/libndr.h @@ -721,6 +721,16 @@ enum ndr_err_code ndr_pull_struct_blob_all_noalloc(const DATA_BLOB *blob, enum ndr_err_code ndr_pull_union_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p, uint32_t level, ndr_pull_flags_fn_t fn); enum ndr_err_code ndr_pull_union_blob_all(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p, uint32_t level, ndr_pull_flags_fn_t fn); +enum ndr_err_code _ndr_deepcopy_struct(ndr_push_flags_fn_t push_fn, + const void *src, + ndr_pull_flags_fn_t pull_fn, + TALLOC_CTX *dst_mem, void *dst); +#define ndr_deepcopy_struct(type, src, dst_mem, dst) \ + _ndr_deepcopy_struct((ndr_push_flags_fn_t)ndr_push_ ## type, \ + src, \ + (ndr_pull_flags_fn_t)ndr_pull_ ## type, \ + dst_mem, dst) + /* from libndr_basic.h */ #define NDR_SCALAR_PROTO(name, type) \ enum ndr_err_code ndr_push_ ## name(struct ndr_push *ndr, int ndr_flags, type v); \ diff --git a/librpc/ndr/ndr.c b/librpc/ndr/ndr.c index d187a0d01101..2549bb4f2102 100644 --- a/librpc/ndr/ndr.c +++ b/librpc/ndr/ndr.c @@ -1340,6 +1340,32 @@ _PUBLIC_ enum ndr_err_code ndr_pull_struct_blob_all(const DATA_BLOB *blob, TALLO return NDR_ERR_SUCCESS; } +_PUBLIC_ enum ndr_err_code +_ndr_deepcopy_struct(ndr_push_flags_fn_t push_fn, + const void *src, + ndr_pull_flags_fn_t pull_fn, + TALLOC_CTX *dst_mem, void *dst) +{ + TALLOC_CTX *frame = talloc_stackframe(); + DATA_BLOB blob = { .length = 0, }; + enum ndr_err_code ndr_err; + + ndr_err = ndr_push_struct_blob(&blob, frame, src, push_fn); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + TALLOC_FREE(frame); + return ndr_err; + } + + ndr_err = ndr_pull_struct_blob_all(&blob, dst_mem, dst, pull_fn); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + TALLOC_FREE(frame); + return ndr_err; + } + + TALLOC_FREE(frame); + return NDR_ERR_SUCCESS; +} + /* pull a struct from a blob using NDR - failing if all bytes are not consumed diff --git a/librpc/wscript_build b/librpc/wscript_build index cd1988033d29..15341defab42 100644 --- a/librpc/wscript_build +++ b/librpc/wscript_build @@ -658,9 +658,9 @@ bld.SAMBA_LIBRARY('ndr', public_deps='samba-errors talloc samba-util util_str_hex', public_headers='gen_ndr/misc.h gen_ndr/ndr_misc.h ndr/libndr.h:ndr.h', header_path= [('*gen_ndr*', 'gen_ndr')], - vnum='3.0.0', + vnum='3.1.0', abi_directory='ABI', - abi_match='!ndr_table_* ndr_* GUID_* _ndr_pull_error* _ndr_push_error*', + abi_match='!ndr_table_* ndr_* GUID_* _ndr_pull_error* _ndr_push_error* _ndr_deepcopy_*', ) bld.SAMBA_LIBRARY('dcerpc-binding', -- 2.34.1 From 91aa6616796654c040a9d88d6c2eccf5ad7bfe6b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 21:04:53 +0200 Subject: [PATCH 03/12] libcli/auth: let netlogon_creds_copy() make use of ndr_deepcopy_struct() Signed-off-by: Stefan Metzmacher --- libcli/auth/credentials.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c index 242c52b6c9bb..72db2afb66a9 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -22,9 +22,11 @@ #include "includes.h" #include "system/time.h" +#include "librpc/gen_ndr/ndr_schannel.h" #include "libcli/auth/libcli_auth.h" #include "../libcli/security/dom_sid.h" #include "lib/util/util_str_escape.h" +#include "librpc/ndr/libndr.h" #include "lib/crypto/gnutls_helpers.h" #include @@ -1185,31 +1187,18 @@ struct netlogon_creds_CredentialState *netlogon_creds_copy( const struct netlogon_creds_CredentialState *creds_in) { struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState); + enum ndr_err_code ndr_err; if (!creds) { return NULL; } - *creds = *creds_in; - - creds->computer_name = talloc_strdup(creds, creds_in->computer_name); - if (!creds->computer_name) { - talloc_free(creds); - return NULL; - } - creds->account_name = talloc_strdup(creds, creds_in->account_name); - if (!creds->account_name) { - talloc_free(creds); + ndr_err = ndr_deepcopy_struct(netlogon_creds_CredentialState, + creds_in, creds, creds); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + TALLOC_FREE(creds); return NULL; } - if (creds_in->sid) { - creds->sid = dom_sid_dup(creds, creds_in->sid); - if (!creds->sid) { - talloc_free(creds); - return NULL; - } - } - return creds; } -- 2.34.1 From 551842b89a2778f1c675094fae80767593e5cc25 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 17:43:00 +0200 Subject: [PATCH 04/12] libcli/auth: make use of netlogon_creds_cli_store_internal() in netlogon_creds_cli_auth_srvauth_done() Signed-off-by: Stefan Metzmacher --- libcli/auth/netlogon_creds_cli.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c index 118c81f6246b..154906342b7b 100644 --- a/libcli/auth/netlogon_creds_cli.c +++ b/libcli/auth/netlogon_creds_cli.c @@ -1417,9 +1417,6 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq) NTSTATUS status; NTSTATUS result; bool ok; - enum ndr_err_code ndr_err; - DATA_BLOB blob; - TDB_DATA data; bool downgraded; if (state->try_auth3) { @@ -1518,20 +1515,8 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq) return; } - ndr_err = ndr_push_struct_blob(&blob, state, state->creds, - (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - status = ndr_map_error2ntstatus(ndr_err); - tevent_req_nterror(req, status); - return; - } - - data.dptr = blob.data; - data.dsize = blob.length; - - status = dbwrap_store(state->context->db.ctx, - state->context->db.key_data, - data, TDB_REPLACE); + status = netlogon_creds_cli_store_internal(state->context, + state->creds); if (tevent_req_nterror(req, status)) { return; } -- 2.34.1 From b49a30ddc7a545476504dc4fc96106576163d660 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 09:27:48 +0200 Subject: [PATCH 05/12] netlogon.idl: the capabilities in query_level=2 are the once send by the client BUG: https://bugzilla.samba.org/show_bug.cgi?id=15425 Signed-off-by: Stefan Metzmacher --- librpc/idl/netlogon.idl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librpc/idl/netlogon.idl b/librpc/idl/netlogon.idl index 383c7b567e30..ed75fe3797af 100644 --- a/librpc/idl/netlogon.idl +++ b/librpc/idl/netlogon.idl @@ -1236,7 +1236,7 @@ interface netlogon /* Function 0x15 */ typedef [switch_type(uint32)] union { [case(1)] netr_NegotiateFlags server_capabilities; - [case(2)] netr_NegotiateFlags server_capabilities; + [case(2)] netr_NegotiateFlags requested_flags; } netr_Capabilities; NTSTATUS netr_LogonGetCapabilities( -- 2.34.1 From 14a8aa30e2da8451ea157bb2a8c6e4cb8b1289ae Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 17:41:18 +0200 Subject: [PATCH 06/12] schannel.idl: add custom ndr_pull_netlogon_creds_CredentialState() --- librpc/idl/schannel.idl | 2 +- librpc/ndr/ndr_schannel.c | 65 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/librpc/idl/schannel.idl b/librpc/idl/schannel.idl index 3bc8a92c92f6..5d6e5afee483 100644 --- a/librpc/idl/schannel.idl +++ b/librpc/idl/schannel.idl @@ -14,7 +14,7 @@ interface schannel { /* this structure is used internally in the NETLOGON server */ - typedef [public,flag(NDR_PAHEX)] struct { + typedef [public,flag(NDR_PAHEX),nopull] struct { netr_NegotiateFlags negotiate_flags; uint8 session_key[16]; uint32 sequence; diff --git a/librpc/ndr/ndr_schannel.c b/librpc/ndr/ndr_schannel.c index 6b08a79cab2c..9768b62047bf 100644 --- a/librpc/ndr/ndr_schannel.c +++ b/librpc/ndr/ndr_schannel.c @@ -20,6 +20,9 @@ */ #include "includes.h" +#include "../librpc/gen_ndr/ndr_misc.h" +#include "../librpc/gen_ndr/ndr_netlogon.h" +#include "../librpc/gen_ndr/ndr_security.h" #include "../librpc/gen_ndr/ndr_schannel.h" #include "../librpc/ndr/ndr_schannel.h" #include "../libcli/nbt/libnbt.h" @@ -105,3 +108,65 @@ void dump_NL_AUTH_SIGNATURE(TALLOC_CTX *mem_ctx, break; } } + +_PUBLIC_ enum ndr_err_code ndr_pull_netlogon_creds_CredentialState(struct ndr_pull *ndr, int ndr_flags, struct netlogon_creds_CredentialState *r) +{ + uint32_t size_session_key_0 = 0; + uint32_t size_computer_name_0 = 0; + uint32_t length_computer_name_0 = 0; + uint32_t size_account_name_0 = 0; + uint32_t length_account_name_0 = 0; + uint32_t _ptr_sid; + TALLOC_CTX *_mem_save_sid_0 = NULL; + { + uint32_t _flags_save_STRUCT = ndr->flags; + ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX); + NDR_PULL_CHECK_FLAGS(ndr, ndr_flags); + if (ndr_flags & NDR_SCALARS) { + NDR_CHECK(ndr_pull_align(ndr, 5)); + NDR_CHECK(ndr_pull_netr_NegotiateFlags(ndr, NDR_SCALARS, &r->negotiate_flags)); + size_session_key_0 = 16; + NDR_CHECK(ndr_pull_array_uint8(ndr, NDR_SCALARS, r->session_key, size_session_key_0)); + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->sequence)); + NDR_CHECK(ndr_pull_netr_Credential(ndr, NDR_SCALARS, &r->seed)); + NDR_CHECK(ndr_pull_netr_Credential(ndr, NDR_SCALARS, &r->client)); + NDR_CHECK(ndr_pull_netr_Credential(ndr, NDR_SCALARS, &r->server)); + NDR_CHECK(ndr_pull_netr_SchannelType(ndr, NDR_SCALARS, &r->secure_channel_type)); + NDR_CHECK(ndr_pull_array_size(ndr, &r->computer_name)); + NDR_CHECK(ndr_pull_array_length(ndr, &r->computer_name)); + NDR_CHECK(ndr_steal_array_size(ndr, (void*)&r->computer_name, &size_computer_name_0)); + NDR_CHECK(ndr_steal_array_length(ndr, (void*)&r->computer_name, &length_computer_name_0)); + if (length_computer_name_0 > size_computer_name_0) { + return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, "Bad array size %u should exceed array length %u", size_computer_name_0, length_computer_name_0); + } + NDR_CHECK(ndr_check_string_terminator(ndr, length_computer_name_0, sizeof(uint8_t))); + NDR_CHECK(ndr_pull_charset(ndr, NDR_SCALARS, &r->computer_name, length_computer_name_0, sizeof(uint8_t), CH_UTF8)); + NDR_CHECK(ndr_pull_array_size(ndr, &r->account_name)); + NDR_CHECK(ndr_pull_array_length(ndr, &r->account_name)); + NDR_CHECK(ndr_steal_array_size(ndr, (void*)&r->account_name, &size_account_name_0)); + NDR_CHECK(ndr_steal_array_length(ndr, (void*)&r->account_name, &length_account_name_0)); + if (length_account_name_0 > size_account_name_0) { + return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, "Bad array size %u should exceed array length %u", size_account_name_0, length_account_name_0); + } + NDR_CHECK(ndr_check_string_terminator(ndr, length_account_name_0, sizeof(uint8_t))); + NDR_CHECK(ndr_pull_charset(ndr, NDR_SCALARS, &r->account_name, length_account_name_0, sizeof(uint8_t), CH_UTF8)); + NDR_CHECK(ndr_pull_generic_ptr(ndr, &_ptr_sid)); + if (_ptr_sid) { + NDR_PULL_ALLOC(ndr, r->sid); + } else { + r->sid = NULL; + } + NDR_CHECK(ndr_pull_trailer_align(ndr, 5)); + } + if (ndr_flags & NDR_BUFFERS) { + if (r->sid) { + _mem_save_sid_0 = NDR_PULL_GET_MEM_CTX(ndr); + NDR_PULL_SET_MEM_CTX(ndr, r->sid, 0); + NDR_CHECK(ndr_pull_dom_sid(ndr, NDR_SCALARS, r->sid)); + NDR_PULL_SET_MEM_CTX(ndr, _mem_save_sid_0, 0); + } + } + ndr->flags = _flags_save_STRUCT; + } + return NDR_ERR_SUCCESS; +} -- 2.34.1 From 9eedef55dc4e27d05baaf00d208f8f5deee626e7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 17:45:13 +0200 Subject: [PATCH 07/12] TODO schannel.idl: add new optional elements to netlogon_creds_CredentialState TODO we should have ndr_pull tests to get an version 0 and version 1 entry. --- librpc/idl/schannel.idl | 32 ++++++++++++++++++++++++++++++-- librpc/ndr/ndr_schannel.c | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/librpc/idl/schannel.idl b/librpc/idl/schannel.idl index 5d6e5afee483..92bf342ed4df 100644 --- a/librpc/idl/schannel.idl +++ b/librpc/idl/schannel.idl @@ -4,7 +4,7 @@ schannel structures */ -import "netlogon.idl", "nbt.idl", "misc.idl", "security.idl"; +import "netlogon.idl", "nbt.idl", "misc.idl", "security.idl", "server_id.idl"; [ pointer_default(unique), @@ -12,7 +12,21 @@ import "netlogon.idl", "nbt.idl", "misc.idl", "security.idl"; ] interface schannel { - /* this structure is used internally in the NETLOGON server */ + /* + * this structure is used internally in schannel_store.tdb + * and also in (the potentially clustered) netlogon_creds_cli.tdb + * + * Because records in netlogon_creds_cli.tdb might + * be used by older Samba version on a different + * cluster node, we need to maintain downlevel + * compat for records with sid=NULL and without + * version, auth_time, auth_server_id, + * update_time, update_server_id, and client_flags. + */ + typedef [public,v1_enum] enum { + NETLOGON_CREDS_CREDENTIAL_STATE_VERSION_0 = 0x00000000, + NETLOGON_CREDS_CREDENTIAL_STATE_VERSION_1 = 0x00000001 + } netlogon_creds_CredentialStateVersion; typedef [public,flag(NDR_PAHEX),nopull] struct { netr_NegotiateFlags negotiate_flags; @@ -25,6 +39,20 @@ interface schannel [string,charset(UTF8)] uint8 computer_name[]; [string,charset(UTF8)] uint8 account_name[]; dom_sid *sid; + /* + * The version field was added for + * version 1 along with auth_time, update_time + * and client_flags + * + * It is garanteed to be there if 'sid' is + * not NULL. + */ + netlogon_creds_CredentialStateVersion version; + NTTIME auth_time; + server_id auth_server_id; + NTTIME update_time; + server_id update_server_id; + netr_NegotiateFlags client_flags; } netlogon_creds_CredentialState; /* This is used in the schannel_store.tdb */ diff --git a/librpc/ndr/ndr_schannel.c b/librpc/ndr/ndr_schannel.c index 9768b62047bf..bfb0a7a0021f 100644 --- a/librpc/ndr/ndr_schannel.c +++ b/librpc/ndr/ndr_schannel.c @@ -23,9 +23,11 @@ #include "../librpc/gen_ndr/ndr_misc.h" #include "../librpc/gen_ndr/ndr_netlogon.h" #include "../librpc/gen_ndr/ndr_security.h" +#include "../librpc/gen_ndr/ndr_server_id.h" #include "../librpc/gen_ndr/ndr_schannel.h" #include "../librpc/ndr/ndr_schannel.h" #include "../libcli/nbt/libnbt.h" +#include "lib/util/server_id.h" _PUBLIC_ void ndr_print_NL_AUTH_MESSAGE_BUFFER(struct ndr_print *ndr, const char *name, const union NL_AUTH_MESSAGE_BUFFER *r) { @@ -123,7 +125,7 @@ _PUBLIC_ enum ndr_err_code ndr_pull_netlogon_creds_CredentialState(struct ndr_pu ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX); NDR_PULL_CHECK_FLAGS(ndr, ndr_flags); if (ndr_flags & NDR_SCALARS) { - NDR_CHECK(ndr_pull_align(ndr, 5)); + NDR_CHECK(ndr_pull_align(ndr, 8)); NDR_CHECK(ndr_pull_netr_NegotiateFlags(ndr, NDR_SCALARS, &r->negotiate_flags)); size_session_key_0 = 16; NDR_CHECK(ndr_pull_array_uint8(ndr, NDR_SCALARS, r->session_key, size_session_key_0)); @@ -156,7 +158,37 @@ _PUBLIC_ enum ndr_err_code ndr_pull_netlogon_creds_CredentialState(struct ndr_pu } else { r->sid = NULL; } - NDR_CHECK(ndr_pull_trailer_align(ndr, 5)); + /* + * We need to keep compat with old + * netlogon_creds_cli.tdb entries + * + * They have no sid and the buffer ends after + * the sid pointer + * + * We mark these entries with + * NETLOGON_CREDS_CREDENTIAL_STATE_VERSION_0, + * it means r->client_flags is not valid and + * set to 0. + */ + if (_ptr_sid != 0 || ndr->offset != ndr->data_size) { + NDR_CHECK(ndr_pull_netlogon_creds_CredentialStateVersion(ndr, NDR_SCALARS, &r->version)); + } else { + r->version = NETLOGON_CREDS_CREDENTIAL_STATE_VERSION_0; + } + if (r->version >= NETLOGON_CREDS_CREDENTIAL_STATE_VERSION_1) { + NDR_CHECK(ndr_pull_NTTIME(ndr, NDR_SCALARS, &r->auth_time)); + NDR_CHECK(ndr_pull_server_id(ndr, NDR_SCALARS, &r->auth_server_id)); + NDR_CHECK(ndr_pull_NTTIME(ndr, NDR_SCALARS, &r->update_time)); + NDR_CHECK(ndr_pull_server_id(ndr, NDR_SCALARS, &r->update_server_id)); + NDR_CHECK(ndr_pull_netr_NegotiateFlags(ndr, NDR_SCALARS, &r->client_flags)); + } else { + r->auth_time = 0; + server_id_set_disconnected(&r->auth_server_id); + r->update_time = 0; + server_id_set_disconnected(&r->update_server_id); + r->client_flags = 0; + } + NDR_CHECK(ndr_pull_trailer_align(ndr, 8)); } if (ndr_flags & NDR_BUFFERS) { if (r->sid) { -- 2.34.1 From 0e343361c4426162bf0a56381857fb8283a78293 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 12:55:33 +0200 Subject: [PATCH 08/12] s3:rpc_server/netlogon: correctly negotiate flags in ServerAuthenticate2/3 Signed-off-by: Stefan Metzmacher --- source3/rpc_server/netlogon/srv_netlog_nt.c | 39 ++++++++++++--------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c index e8aa14167fc4..da7b02dd06b4 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -877,6 +877,7 @@ NTSTATUS _netr_ServerAuthenticate3(struct pipes_struct *p, /* r->in.negotiate_flags is an aliased pointer to r->out.negotiate_flags, * so use a copy to avoid destroying the client values. */ uint32_t in_neg_flags = *r->in.negotiate_flags; + uint32_t neg_flags = 0; const char *fn; struct loadparm_context *lp_ctx = p->dce_call->conn->dce_ctx->lp_ctx; struct dom_sid sid; @@ -893,7 +894,6 @@ NTSTATUS _netr_ServerAuthenticate3(struct pipes_struct *p, * an error or not. */ - /* 0x000001ff */ srv_flgs = NETLOGON_NEG_ACCOUNT_LOCKOUT | NETLOGON_NEG_PERSISTENT_SAMREPL | NETLOGON_NEG_ARCFOUR | @@ -903,20 +903,10 @@ NTSTATUS _netr_ServerAuthenticate3(struct pipes_struct *p, NETLOGON_NEG_MULTIPLE_SIDS | NETLOGON_NEG_REDO | NETLOGON_NEG_PASSWORD_CHANGE_REFUSAL | - NETLOGON_NEG_PASSWORD_SET2; - - /* Ensure we support strong (128-bit) keys. */ - if (in_neg_flags & NETLOGON_NEG_STRONG_KEYS) { - srv_flgs |= NETLOGON_NEG_STRONG_KEYS; - } - - if (in_neg_flags & NETLOGON_NEG_SUPPORTS_AES) { - srv_flgs |= NETLOGON_NEG_SUPPORTS_AES; - } - - if (in_neg_flags & NETLOGON_NEG_SCHANNEL) { - srv_flgs |= NETLOGON_NEG_SCHANNEL; - } + NETLOGON_NEG_PASSWORD_SET2 | + NETLOGON_NEG_STRONG_KEYS | + NETLOGON_NEG_SUPPORTS_AES | + NETLOGON_NEG_SCHANNEL; /* * Support authentication of trusted domains. @@ -938,6 +928,8 @@ NTSTATUS _netr_ServerAuthenticate3(struct pipes_struct *p, srv_flgs &= ~NETLOGON_NEG_ARCFOUR; } + neg_flags = in_neg_flags & srv_flgs; + switch (dce_call->pkt.u.request.opnum) { case NDR_NETR_SERVERAUTHENTICATE: fn = "_netr_ServerAuthenticate"; @@ -952,6 +944,19 @@ NTSTATUS _netr_ServerAuthenticate3(struct pipes_struct *p, return NT_STATUS_INTERNAL_ERROR; } + if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED) { + if (!(neg_flags & NETLOGON_NEG_SUPPORTS_AES)) { + DBG_NOTICE("%s: no AES support negotiated from client %s\n", + fn, r->in.computer_name); + /* + * Here we match Windows 2012 and return no flags. + */ + neg_flags = 0; + status = NT_STATUS_DOWNGRADE_DETECTED; + goto out; + } + } + /* We use this as the key to store the creds: */ /* r->in.computer_name */ @@ -991,7 +996,7 @@ NTSTATUS _netr_ServerAuthenticate3(struct pipes_struct *p, &mach_pwd, r->in.credentials, r->out.return_credentials, - srv_flgs); + neg_flags); if (!creds) { DEBUG(0,("%s: netlogon_creds_server_check failed. Rejecting auth " "request from client %s machine account %s\n", @@ -1023,7 +1028,7 @@ NTSTATUS _netr_ServerAuthenticate3(struct pipes_struct *p, out: - *r->out.negotiate_flags = srv_flgs; + *r->out.negotiate_flags = neg_flags; return status; } -- 2.34.1 From 997c0542e7e071fb2adf745dc3fa912ff73d3683 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 13:15:58 +0200 Subject: [PATCH 09/12] TODO schannel: remember client_flags and auth_time in netlogon_creds_CredentialState This will be used in the next steps. The related tdbs are CLEAR_IF_FIRST so we don't need to change the database keys for the new format on the server and we already have fallback code for the client in ndr_pull_netlogon_creds_CredentialState() for NETLOGON_CREDS_CREDENTIAL_STATE_VERSION_0 fill NETLOGON_CREDS_CREDENTIAL_STATE_VERSION_1 fields --- libcli/auth/credentials.c | 20 ++++++++++++++++++- libcli/auth/netlogon_creds_cli.c | 13 ++++++++++++ libcli/auth/proto.h | 1 + libcli/auth/schannel_state_tdb.c | 6 ++++++ source3/rpc_server/netlogon/srv_netlog_nt.c | 1 + source4/rpc_server/netlogon/dcerpc_netlogon.c | 6 +++++- 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/libcli/auth/credentials.c b/libcli/auth/credentials.c index 72db2afb66a9..36b4123452ad 100644 --- a/libcli/auth/credentials.c +++ b/libcli/auth/credentials.c @@ -27,6 +27,7 @@ #include "../libcli/security/dom_sid.h" #include "lib/util/util_str_escape.h" #include "librpc/ndr/libndr.h" +#include "lib/util/server_id.h" #include "lib/crypto/gnutls_helpers.h" #include @@ -497,12 +498,20 @@ struct netlogon_creds_CredentialState *netlogon_creds_client_init(TALLOC_CTX *me { struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState); NTSTATUS status; + struct timeval tv = timeval_current(); + NTTIME now = timeval_to_nttime(&tv); if (!creds) { return NULL; } - creds->sequence = time(NULL); + creds->version = NETLOGON_CREDS_CREDENTIAL_STATE_VERSION_1; + creds->sequence = tv.tv_sec; + creds->auth_time = now; + server_id_set_disconnected(&creds->auth_server_id); + creds->update_time = now; + server_id_set_disconnected(&creds->update_server_id); + creds->client_flags = negotiate_flags; creds->negotiate_flags = negotiate_flags; creds->secure_channel_type = secure_channel_type; @@ -678,17 +687,26 @@ struct netlogon_creds_CredentialState *netlogon_creds_server_init(TALLOC_CTX *me const struct samr_Password *machine_password, const struct netr_Credential *credentials_in, struct netr_Credential *credentials_out, + uint32_t client_flags, uint32_t negotiate_flags) { struct netlogon_creds_CredentialState *creds = talloc_zero(mem_ctx, struct netlogon_creds_CredentialState); NTSTATUS status; bool ok; + struct timeval tv = timeval_current(); + NTTIME now = timeval_to_nttime(&tv); if (!creds) { return NULL; } + creds->version = NETLOGON_CREDS_CREDENTIAL_STATE_VERSION_1; + creds->auth_time = now; + server_id_set_disconnected(&creds->auth_server_id); + creds->update_time = now; + server_id_set_disconnected(&creds->update_server_id); + creds->client_flags = client_flags; creds->negotiate_flags = negotiate_flags; creds->secure_channel_type = secure_channel_type; diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c index 154906342b7b..95feeaf008ef 100644 --- a/libcli/auth/netlogon_creds_cli.c +++ b/libcli/auth/netlogon_creds_cli.c @@ -33,6 +33,7 @@ #include "../librpc/gen_ndr/ndr_netlogon_c.h" #include "../librpc/gen_ndr/ndr_netlogon.h" #include "../librpc/gen_ndr/server_id.h" +#include "lib/util/server_id.h" #include "netlogon_creds_cli.h" #include "source3/include/messages.h" #include "source3/include/g_lock.h" @@ -70,6 +71,7 @@ struct netlogon_creds_cli_context { struct g_lock_ctx *g_ctx; struct netlogon_creds_cli_locked_state *locked_state; enum netlogon_creds_cli_lck_type lock; + struct server_id server_id; } db; }; @@ -196,6 +198,8 @@ static NTSTATUS netlogon_creds_cli_context_common( context->db.key_data = string_term_tdb_data(context->db.key_name); + server_id_set_disconnected(&context->db.server_id); + *_context = context; return NT_STATUS_OK; } @@ -532,6 +536,8 @@ NTSTATUS netlogon_creds_cli_context_global(struct loadparm_context *lp_ctx, return status; } + context->db.server_id = messaging_server_id(msg_ctx); + context->db.g_ctx = g_lock_ctx_init(context, msg_ctx); if (context->db.g_ctx == NULL) { TALLOC_FREE(context); @@ -749,6 +755,11 @@ static NTSTATUS netlogon_creds_cli_store_internal( enum ndr_err_code ndr_err; DATA_BLOB blob; TDB_DATA data; + struct timeval tv = timeval_current(); + NTTIME now = timeval_to_nttime(&tv); + + creds->update_time = now; + creds->update_server_id = context->db.server_id; if (DEBUGLEVEL >= 10) { NDR_PRINT_DEBUG(netlogon_creds_CredentialState, creds); @@ -1515,6 +1526,8 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq) return; } + state->creds->auth_server_id = state->context->db.server_id; + status = netlogon_creds_cli_store_internal(state->context, state->creds); if (tevent_req_nterror(req, status)) { diff --git a/libcli/auth/proto.h b/libcli/auth/proto.h index b202542068d1..f1e5c5e5f463 100644 --- a/libcli/auth/proto.h +++ b/libcli/auth/proto.h @@ -71,6 +71,7 @@ struct netlogon_creds_CredentialState *netlogon_creds_server_init(TALLOC_CTX *me const struct samr_Password *machine_password, const struct netr_Credential *credentials_in, struct netr_Credential *credentials_out, + uint32_t client_flags, uint32_t negotiate_flags); NTSTATUS netlogon_creds_server_step_check(struct netlogon_creds_CredentialState *creds, const struct netr_Authenticator *received_authenticator, diff --git a/libcli/auth/schannel_state_tdb.c b/libcli/auth/schannel_state_tdb.c index ac3654e2c993..2ce3d0b34796 100644 --- a/libcli/auth/schannel_state_tdb.c +++ b/libcli/auth/schannel_state_tdb.c @@ -28,6 +28,7 @@ #include "../lib/param/param.h" #include "../libcli/auth/schannel.h" #include "../librpc/gen_ndr/ndr_schannel.h" +#include "lib/util/server_id.h" #include "lib/dbwrap/dbwrap.h" #define SECRETS_SCHANNEL_STATE "SECRETS/SCHANNEL" @@ -87,6 +88,8 @@ NTSTATUS schannel_store_session_key_tdb(struct db_context *db_sc, char *keystr; char *name_upper; NTSTATUS status; + struct timeval tv = timeval_current(); + NTTIME now = timeval_to_nttime(&tv); if (strlen(creds->computer_name) > 15) { /* @@ -108,6 +111,9 @@ NTSTATUS schannel_store_session_key_tdb(struct db_context *db_sc, return NT_STATUS_NO_MEMORY; } + creds->update_time = now; + server_id_set_disconnected(&creds->update_server_id); + ndr_err = ndr_push_struct_blob(&blob, mem_ctx, creds, (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c index da7b02dd06b4..bd97023c81c7 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -996,6 +996,7 @@ NTSTATUS _netr_ServerAuthenticate3(struct pipes_struct *p, &mach_pwd, r->in.credentials, r->out.return_credentials, + in_neg_flags, neg_flags); if (!creds) { DEBUG(0,("%s: netlogon_creds_server_check failed. Rejecting auth " diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index dc2167f08b2d..abee26e961d4 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -413,6 +413,7 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper( const char *attrs[] = {"unicodePwd", "userAccountControl", "objectSid", "samAccountName", NULL}; uint32_t server_flags = 0; + uint32_t client_flags = 0; uint32_t negotiate_flags = 0; ZERO_STRUCTP(r->out.return_credentials); @@ -501,7 +502,8 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper( server_flags &= ~NETLOGON_NEG_ARCFOUR; } - negotiate_flags = *r->in.negotiate_flags & server_flags; + client_flags = *r->in.negotiate_flags; + negotiate_flags = client_flags & server_flags; switch (r->in.secure_channel_type) { case SEC_CHAN_WKSTA: @@ -751,6 +753,7 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper( curNtHash, r->in.credentials, r->out.return_credentials, + client_flags, negotiate_flags); if (creds == NULL && prevNtHash != NULL) { /* @@ -768,6 +771,7 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper( prevNtHash, r->in.credentials, r->out.return_credentials, + client_flags, negotiate_flags); } -- 2.34.1 From 058d61d3a77b772e784cf5b265fa9fc98f6baad5 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 17:59:56 +0200 Subject: [PATCH 10/12] libcli/auth/netlogon_creds_cli.c use netr_LogonGetCapabilities query_level=2 --- libcli/auth/netlogon_creds_cli.c | 125 ++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 3 deletions(-) diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c index 95feeaf008ef..c48d0c587bef 100644 --- a/libcli/auth/netlogon_creds_cli.c +++ b/libcli/auth/netlogon_creds_cli.c @@ -1598,13 +1598,15 @@ struct netlogon_creds_cli_check_state { union netr_Capabilities caps; struct netlogon_creds_CredentialState *creds; + struct netlogon_creds_CredentialState tmp_creds; struct netr_Authenticator req_auth; struct netr_Authenticator rep_auth; }; static void netlogon_creds_cli_check_cleanup(struct tevent_req *req, NTSTATUS status); -static void netlogon_creds_cli_check_caps(struct tevent_req *subreq); +static void netlogon_creds_cli_check_negotiate_caps(struct tevent_req *subreq); +static void netlogon_creds_cli_check_client_caps(struct tevent_req *subreq); struct tevent_req *netlogon_creds_cli_check_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -1688,7 +1690,7 @@ struct tevent_req *netlogon_creds_cli_check_send(TALLOC_CTX *mem_ctx, } tevent_req_set_callback(subreq, - netlogon_creds_cli_check_caps, + netlogon_creds_cli_check_negotiate_caps, req); return req; @@ -1718,7 +1720,7 @@ static void netlogon_creds_cli_check_cleanup(struct tevent_req *req, TALLOC_FREE(state->creds); } -static void netlogon_creds_cli_check_caps(struct tevent_req *subreq) +static void netlogon_creds_cli_check_negotiate_caps(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, @@ -1860,6 +1862,123 @@ static void netlogon_creds_cli_check_caps(struct tevent_req *subreq) return; } + if (state->creds->version == NETLOGON_CREDS_CREDENTIAL_STATE_VERSION_0) { + /* + * This can happen in a cluster where old + * Samba versions on other nodes wrote + * the entry and we don't have + * state->creds->client_flags + */ + tevent_req_done(req); + return; + } + + /* + * Now try to verify our client proposed flags + * arrived at the server, using query_level = 2 + */ + state->tmp_creds = *state->creds; + status = netlogon_creds_client_authenticator(&state->tmp_creds, + &state->req_auth); + if (tevent_req_nterror(req, status)) { + return; + } + ZERO_STRUCT(state->rep_auth); + + subreq = dcerpc_netr_LogonGetCapabilities_send(state, state->ev, + state->binding_handle, + state->srv_name_slash, + state->context->client.computer, + &state->req_auth, + &state->rep_auth, + 2, + &state->caps); + if (tevent_req_nomem(subreq, req)) { + return; + } + + tevent_req_set_callback(subreq, + netlogon_creds_cli_check_client_caps, + req); + return; +} + +static void netlogon_creds_cli_check_client_caps(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct netlogon_creds_cli_check_state *state = + tevent_req_data(req, + struct netlogon_creds_cli_check_state); + NTSTATUS status; + NTSTATUS result; + bool ok; + + status = dcerpc_netr_LogonGetCapabilities_recv(subreq, state, + &result); + TALLOC_FREE(subreq); + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_BAD_STUB_DATA)) { + /* + * unpatched Samba server, see + * https://bugzilla.samba.org/show_bug.cgi?id=15418 + */ + status = NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) { + /* + * Here we know the negotiated flags were already + * verified with query_level=1, which means + * the server supported NETLOGON_NEG_SUPPORTS_AES + * and also NETLOGON_NEG_AUTHENTICATED_RPC + * + * As we're using DCERPC_AUTH_TYPE_SCHANNEL with + * DCERPC_AUTH_LEVEL_INTEGRITY or DCERPC_AUTH_LEVEL_PRIVACY + * we should detect a faked NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE + * with the next request as the sequence number processing + * gets out of sync. + */ + status = netlogon_creds_cli_store_internal(state->context, + state->creds); + if (tevent_req_nterror(req, status)) { + return; + } + + tevent_req_done(req); + } + if (tevent_req_nterror(req, status)) { + netlogon_creds_cli_check_cleanup(req, status); + return; + } + + ok = netlogon_creds_client_check(&state->tmp_creds, + &state->rep_auth.cred); + if (!ok) { + status = NT_STATUS_ACCESS_DENIED; + tevent_req_nterror(req, status); + netlogon_creds_cli_check_cleanup(req, status); + return; + } + + if (tevent_req_nterror(req, result)) { + netlogon_creds_cli_check_cleanup(req, result); + return; + } + + if (state->caps.requested_flags != state->creds->client_flags) { + status = NT_STATUS_DOWNGRADE_DETECTED; + tevent_req_nterror(req, status); + netlogon_creds_cli_check_cleanup(req, status); + return; + } + + *state->creds = state->tmp_creds; + status = netlogon_creds_cli_store_internal(state->context, + state->creds); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); } -- 2.34.1 From 8670e5d83406cd0de7f3ff6aaae4ef860476b7ee Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 18:00:31 +0200 Subject: [PATCH 11/12] source4/rpc_server/netlogon/dcerpc_netlogon.c dcesrv_netr_LogonGetCapabilities implement query_level=2 --- source4/rpc_server/netlogon/dcerpc_netlogon.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index abee26e961d4..1e6b12f9987a 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -2372,12 +2372,7 @@ static NTSTATUS dcesrv_netr_LogonGetCapabilities(struct dcesrv_call_state *dce_c case 1: break; case 2: - /* - * Until we know the details behind KB5028166 - * just return DCERPC_NCA_S_FAULT_INVALID_TAG - * like an unpatched Windows Server. - */ - FALL_THROUGH; + break; default: /* * There would not be a way to marshall the @@ -2403,7 +2398,14 @@ static NTSTATUS dcesrv_netr_LogonGetCapabilities(struct dcesrv_call_state *dce_c } NT_STATUS_NOT_OK_RETURN(status); - r->out.capabilities->server_capabilities = creds->negotiate_flags; + switch (r->in.query_level) { + case 1: + r->out.capabilities->server_capabilities = creds->negotiate_flags; + break; + case 2: + r->out.capabilities->requested_flags = creds->client_flags; + break; + } return NT_STATUS_OK; } -- 2.34.1 From 56b59b4259eac94e23839091f6ff7117c8c41b68 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 19 Jul 2023 18:03:09 +0200 Subject: [PATCH 12/12] source3/rpc_server/netlogon/srv_netlog_nt.c _netr_LogonGetCapabilities query_level=2 --- source3/rpc_server/netlogon/srv_netlog_nt.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c index bd97023c81c7..851a2dfbe5f6 100644 --- a/source3/rpc_server/netlogon/srv_netlog_nt.c +++ b/source3/rpc_server/netlogon/srv_netlog_nt.c @@ -2294,12 +2294,7 @@ NTSTATUS _netr_LogonGetCapabilities(struct pipes_struct *p, case 1: break; case 2: - /* - * Until we know the details behind KB5028166 - * just return DCERPC_NCA_S_FAULT_INVALID_TAG - * like an unpatched Windows Server. - */ - FALL_THROUGH; + break; default: /* * There would not be a way to marshall the @@ -2327,7 +2322,14 @@ NTSTATUS _netr_LogonGetCapabilities(struct pipes_struct *p, return status; } - r->out.capabilities->server_capabilities = creds->negotiate_flags; + switch (r->in.query_level) { + case 1: + r->out.capabilities->server_capabilities = creds->negotiate_flags; + break; + case 2: + r->out.capabilities->requested_flags = creds->client_flags; + break; + } return NT_STATUS_OK; } -- 2.34.1