s4:rpc_server: Fix code spelling
[samba.git] / source4 / rpc_server / netlogon / dcerpc_netlogon.c
index 72b50327c5093ed25e3c347a0fedcb82b4978e8d..d77d524cb28cc78e86ab0c413511ef1010ba7e46 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "includes.h"
 #include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
 #include "auth/auth.h"
 #include "auth/auth_sam_reply.h"
 #include "dsdb/samdb/samdb.h"
 #include "librpc/gen_ndr/ndr_irpc.h"
 #include "librpc/gen_ndr/ndr_winbind.h"
 #include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "librpc/rpc/server/netlogon/schannel_util.h"
 #include "lib/socket/netif.h"
-#include "rpc_server/common/sid_helper.h"
 #include "lib/util/util_str_escape.h"
+#include "lib/param/loadparm.h"
 
-#define DCESRV_INTERFACE_NETLOGON_BIND(call, iface) \
-       dcesrv_interface_netlogon_bind(call, iface)
+#define DCESRV_INTERFACE_NETLOGON_BIND(context, iface) \
+       dcesrv_interface_netlogon_bind(context, iface)
+
+#undef strcasecmp
 
 /*
  * This #define allows the netlogon interface to accept invalid
  */
 #define DCESRV_INTERFACE_NETLOGON_FLAGS DCESRV_INTERFACE_FLAGS_HANDLES_NOT_USED
 
-static NTSTATUS dcesrv_interface_netlogon_bind(struct dcesrv_call_state *dce_call,
+static NTSTATUS dcesrv_interface_netlogon_bind(struct dcesrv_connection_context *context,
                                               const struct dcesrv_interface *iface)
 {
-       return dcesrv_interface_bind_reject_connect(dce_call, iface);
-}
+       struct loadparm_context *lp_ctx = context->conn->dce_ctx->lp_ctx;
+       bool global_allow_nt4_crypto = lpcfg_allow_nt4_crypto(lp_ctx);
+       bool global_reject_md5_client = lpcfg_reject_md5_clients(lp_ctx);
+       int schannel = lpcfg_server_schannel(lp_ctx);
+       bool schannel_global_required = (schannel == true);
+       bool global_require_seal = lpcfg_server_schannel_require_seal(lp_ctx);
+       static bool warned_global_nt4_once = false;
+       static bool warned_global_md5_once = false;
+       static bool warned_global_schannel_once = false;
+       static bool warned_global_seal_once = false;
 
-struct netlogon_server_pipe_state {
-       struct netr_Credential client_challenge;
-       struct netr_Credential server_challenge;
-};
+       if (global_allow_nt4_crypto && !warned_global_nt4_once) {
+               /*
+                * We want admins to notice their misconfiguration!
+                */
+               D_ERR("CVE-2022-38023 (and others): "
+                     "Please configure 'allow nt4 crypto = no' (the default), "
+                     "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+               warned_global_nt4_once = true;
+       }
+
+       if (!global_reject_md5_client && !warned_global_md5_once) {
+               /*
+                * We want admins to notice their misconfiguration!
+                */
+               D_ERR("CVE-2022-38023: "
+                     "Please configure 'reject md5 clients = yes' (the default), "
+                     "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+               warned_global_md5_once = true;
+       }
+
+       if (!schannel_global_required && !warned_global_schannel_once) {
+               /*
+                * We want admins to notice their misconfiguration!
+                */
+               D_ERR("CVE-2020-1472(ZeroLogon): "
+                     "Please configure 'server schannel = yes' (the default), "
+                     "See https://bugzilla.samba.org/show_bug.cgi?id=14497\n");
+               warned_global_schannel_once = true;
+       }
+
+       if (!global_require_seal && !warned_global_seal_once) {
+               /*
+                * We want admins to notice their misconfiguration!
+                */
+               D_ERR("CVE-2022-38023 (and others): "
+                     "Please configure 'server schannel require seal = yes' (the default), "
+                     "See https://bugzilla.samba.org/show_bug.cgi?id=15240\n");
+               warned_global_seal_once = true;
+       }
+
+       return dcesrv_interface_bind_reject_connect(context, iface);
+}
 
 static NTSTATUS dcesrv_netr_ServerReqChallenge(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
                                        struct netr_ServerReqChallenge *r)
 {
-       struct netlogon_server_pipe_state *pipe_state =
-               talloc_get_type(dce_call->context->private_data, struct netlogon_server_pipe_state);
+       struct netlogon_server_pipe_state *pipe_state = NULL;
        NTSTATUS ntstatus;
 
        ZERO_STRUCTP(r->out.return_credentials);
 
-       if (pipe_state) {
-               talloc_free(pipe_state);
-               dce_call->context->private_data = NULL;
-       }
+       pipe_state = dcesrv_iface_state_find_conn(dce_call,
+                       NETLOGON_SERVER_PIPE_STATE_MAGIC,
+                       struct netlogon_server_pipe_state);
+       TALLOC_FREE(pipe_state);
 
-       pipe_state = talloc(dce_call->context, struct netlogon_server_pipe_state);
-       NT_STATUS_HAVE_NO_MEMORY(pipe_state);
+       pipe_state = talloc_zero(dce_call,
+                                struct netlogon_server_pipe_state);
+       if (pipe_state == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
        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;
 
-       dce_call->context->private_data = pipe_state;
+       ntstatus = dcesrv_iface_state_store_conn(dce_call,
+                       NETLOGON_SERVER_PIPE_STATE_MAGIC,
+                       pipe_state);
+       if (!NT_STATUS_IS_OK(ntstatus)) {
+               return ntstatus;
+       }
 
        ntstatus = schannel_save_challenge(dce_call->conn->dce_ctx->lp_ctx,
                                           &pipe_state->client_challenge,
                                           &pipe_state->server_challenge,
                                           r->in.computer_name);
        if (!NT_STATUS_IS_OK(ntstatus)) {
+               TALLOC_FREE(pipe_state);
                return ntstatus;
        }
 
        return NT_STATUS_OK;
 }
 
+static NTSTATUS dcesrv_netr_ServerAuthenticate3_check_downgrade(
+       struct dcesrv_call_state *dce_call,
+       struct netr_ServerAuthenticate3 *r,
+       struct netlogon_server_pipe_state *pipe_state,
+       uint32_t negotiate_flags,
+       const char *trust_account_in_db,
+       NTSTATUS orig_status)
+{
+       struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+       bool global_allow_nt4_crypto = lpcfg_allow_nt4_crypto(lp_ctx);
+       bool account_allow_nt4_crypto = global_allow_nt4_crypto;
+       const char *explicit_nt4_opt = NULL;
+       bool global_reject_md5_client = lpcfg_reject_md5_clients(lp_ctx);
+       bool account_reject_md5_client = global_reject_md5_client;
+       const char *explicit_md5_opt = NULL;
+       bool reject_des_client;
+       bool allow_nt4_crypto;
+       bool reject_md5_client;
+       bool need_des = true;
+       bool need_md5 = true;
+       int CVE_2022_38023_warn_level = lpcfg_parm_int(lp_ctx, NULL,
+                       "CVE_2022_38023", "warn_about_unused_debug_level", DBGLVL_ERR);
+       int CVE_2022_38023_error_level = lpcfg_parm_int(lp_ctx, NULL,
+                       "CVE_2022_38023", "error_debug_level", DBGLVL_ERR);
+
+       /*
+        * We don't use lpcfg_parm_bool(), as we
+        * need the explicit_opt pointer in order to
+        * adjust the debug messages.
+        */
+
+       if (trust_account_in_db != NULL) {
+               explicit_nt4_opt = lpcfg_get_parametric(lp_ctx,
+                                                       NULL,
+                                                       "allow nt4 crypto",
+                                                       trust_account_in_db);
+       }
+       if (explicit_nt4_opt != NULL) {
+               account_allow_nt4_crypto = lp_bool(explicit_nt4_opt);
+       }
+       allow_nt4_crypto = account_allow_nt4_crypto;
+       if (trust_account_in_db != NULL) {
+               explicit_md5_opt = lpcfg_get_parametric(lp_ctx,
+                                                       NULL,
+                                                       "server reject md5 schannel",
+                                                       trust_account_in_db);
+       }
+       if (explicit_md5_opt != NULL) {
+               account_reject_md5_client = lp_bool(explicit_md5_opt);
+       }
+       reject_md5_client = account_reject_md5_client;
+
+       reject_des_client = !allow_nt4_crypto;
+
+       /*
+        * If weak crypto is disabled, do not announce that we support RC4.
+        */
+       if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED) {
+               /* Without RC4 and DES we require AES */
+               reject_des_client = true;
+               reject_md5_client = true;
+       }
+
+       if (negotiate_flags & NETLOGON_NEG_STRONG_KEYS) {
+               need_des = false;
+               reject_des_client = false;
+       }
+
+       if (negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+               need_des = false;
+               need_md5 = false;
+               reject_des_client = false;
+               reject_md5_client = false;
+       }
+
+       if (reject_des_client || reject_md5_client) {
+               TALLOC_CTX *frame = talloc_stackframe();
+
+               if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED) {
+                       if (CVE_2022_38023_error_level < DBGLVL_NOTICE) {
+                               CVE_2022_38023_error_level = DBGLVL_NOTICE;
+                       }
+                       DEBUG(CVE_2022_38023_error_level, (
+                             "CVE-2022-38023: "
+                             "client_account[%s] computer_name[%s] "
+                             "schannel_type[%u] "
+                             "client_negotiate_flags[0x%x] "
+                             "%s%s%s "
+                             "NT_STATUS_DOWNGRADE_DETECTED "
+                             "WEAK_CRYPTO_DISALLOWED\n",
+                             log_escape(frame, r->in.account_name),
+                             log_escape(frame, r->in.computer_name),
+                             r->in.secure_channel_type,
+                             (unsigned)*r->in.negotiate_flags,
+                             trust_account_in_db ? "real_account[" : "",
+                             trust_account_in_db ? trust_account_in_db : "",
+                             trust_account_in_db ? "]" : ""));
+                       goto return_downgrade;
+               }
+
+               DEBUG(CVE_2022_38023_error_level, (
+                     "CVE-2022-38023: "
+                     "client_account[%s] computer_name[%s] "
+                     "schannel_type[%u] "
+                     "client_negotiate_flags[0x%x] "
+                     "%s%s%s "
+                     "NT_STATUS_DOWNGRADE_DETECTED "
+                     "reject_des[%u] reject_md5[%u]\n",
+                     log_escape(frame, r->in.account_name),
+                     log_escape(frame, r->in.computer_name),
+                     r->in.secure_channel_type,
+                     (unsigned)*r->in.negotiate_flags,
+                     trust_account_in_db ? "real_account[" : "",
+                     trust_account_in_db ? trust_account_in_db : "",
+                     trust_account_in_db ? "]" : "",
+                     reject_des_client,
+                     reject_md5_client));
+               if (trust_account_in_db == NULL) {
+                       goto return_downgrade;
+               }
+
+               if (reject_md5_client && explicit_md5_opt == NULL) {
+                       DEBUG(CVE_2022_38023_error_level, (
+                             "CVE-2022-38023: Check if option "
+                             "'server reject md5 schannel:%s = no' "
+                             "might be needed for a legacy client.\n",
+                             trust_account_in_db));
+               }
+               if (reject_des_client && explicit_nt4_opt == NULL) {
+                       DEBUG(CVE_2022_38023_error_level, (
+                             "CVE-2022-38023: Check if option "
+                             "'allow nt4 crypto:%s = yes' "
+                             "might be needed for a legacy client.\n",
+                             trust_account_in_db));
+               }
+
+return_downgrade:
+               /*
+                * Here we match Windows 2012 and return no flags.
+                */
+               *r->out.negotiate_flags = 0;
+               TALLOC_FREE(frame);
+               return NT_STATUS_DOWNGRADE_DETECTED;
+       }
+
+       /*
+        * This talloc_free is important to prevent re-use of the
+        * challenge.  We have to delay it this far due to NETApp
+        * servers per:
+        * https://bugzilla.samba.org/show_bug.cgi?id=11291
+        */
+       TALLOC_FREE(pipe_state);
+
+       /*
+        * At this point we must also cleanup the TDB cache
+        * entry, if we fail the client needs to call
+        * netr_ServerReqChallenge again.
+        *
+        * Note: this handles a non existing record just fine,
+        * the r->in.computer_name might not be the one used
+        * in netr_ServerReqChallenge(), but we are trying to
+        * just tidy up the normal case to prevent re-use.
+        */
+       schannel_delete_challenge(dce_call->conn->dce_ctx->lp_ctx,
+                                 r->in.computer_name);
+
+       /*
+        * According to Microsoft (see bugid #6099)
+        * Windows 7 looks at the negotiate_flags
+        * returned in this structure *even if the
+        * call fails with access denied!
+        */
+       *r->out.negotiate_flags = negotiate_flags;
+
+       if (!NT_STATUS_IS_OK(orig_status) || trust_account_in_db == NULL) {
+               return orig_status;
+       }
+
+       if (global_reject_md5_client && account_reject_md5_client && explicit_md5_opt) {
+               D_INFO("CVE-2022-38023: Check if option "
+                      "'server reject md5 schannel:%s = yes' not needed!?\n",
+                      trust_account_in_db);
+       } else if (need_md5 && !account_reject_md5_client && explicit_md5_opt) {
+               D_INFO("CVE-2022-38023: Check if option "
+                        "'server reject md5 schannel:%s = no' "
+                        "still needed for a legacy client.\n",
+                        trust_account_in_db);
+       } else if (need_md5 && explicit_md5_opt == NULL) {
+               DEBUG(CVE_2022_38023_error_level, (
+                     "CVE-2022-38023: Check if option "
+                     "'server reject md5 schannel:%s = no' "
+                     "might be needed for a legacy client.\n",
+                     trust_account_in_db));
+       } else if (!account_reject_md5_client && explicit_md5_opt) {
+               DEBUG(CVE_2022_38023_warn_level, (
+                     "CVE-2022-38023: Check if option "
+                     "'server reject md5 schannel:%s = no' not needed!?\n",
+                     trust_account_in_db));
+       }
+
+       if (!global_allow_nt4_crypto && !account_allow_nt4_crypto && explicit_nt4_opt) {
+               D_INFO("CVE-2022-38023: Check if option "
+                      "'allow nt4 crypto:%s = no' not needed!?\n",
+                      trust_account_in_db);
+       } else if (need_des && account_allow_nt4_crypto && explicit_nt4_opt) {
+               D_INFO("CVE-2022-38023: Check if option "
+                        "'allow nt4 crypto:%s = yes' "
+                        "still needed for a legacy client.\n",
+                        trust_account_in_db);
+       } else if (need_des && explicit_nt4_opt == NULL) {
+               DEBUG(CVE_2022_38023_error_level, (
+                     "CVE-2022-38023: Check if option "
+                     "'allow nt4 crypto:%s = yes' "
+                     "might be needed for a legacy client.\n",
+                     trust_account_in_db));
+       } else if (account_allow_nt4_crypto && explicit_nt4_opt) {
+               DEBUG(CVE_2022_38023_warn_level, (
+                     "CVE-2022-38023: Check if option "
+                     "'allow nt4 crypto:%s = yes' not needed!?\n",
+                     trust_account_in_db));
+       }
+
+       return orig_status;
+}
+
 /*
  * Do the actual processing of a netr_ServerAuthenticate3 message.
  * called from dcesrv_netr_ServerAuthenticate3, which handles the logging.
@@ -117,8 +399,7 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
        const char **trust_account_in_db,
        struct dom_sid **sid)
 {
-       struct netlogon_server_pipe_state *pipe_state =
-               talloc_get_type(dce_call->context->private_data, struct netlogon_server_pipe_state);
+       struct netlogon_server_pipe_state *pipe_state = NULL;
        bool challenge_valid = false;
        struct netlogon_server_pipe_state challenge;
        struct netlogon_creds_CredentialState *creds;
@@ -133,18 +414,15 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
                               "objectSid", "samAccountName", NULL};
        uint32_t server_flags = 0;
        uint32_t negotiate_flags = 0;
-       bool allow_nt4_crypto = lpcfg_allow_nt4_crypto(dce_call->conn->dce_ctx->lp_ctx);
-       bool reject_des_client = !allow_nt4_crypto;
-       bool reject_md5_client = lpcfg_reject_md5_clients(dce_call->conn->dce_ctx->lp_ctx);
-       int schannel = lpcfg_server_schannel(dce_call->conn->dce_ctx->lp_ctx);
-       bool reject_none_rpc = (schannel == true);
 
        ZERO_STRUCTP(r->out.return_credentials);
+       *r->out.negotiate_flags = 0;
        *r->out.rid = 0;
 
+       pipe_state = dcesrv_iface_state_find_conn(dce_call,
+                       NETLOGON_SERVER_PIPE_STATE_MAGIC,
+                       struct netlogon_server_pipe_state);
        if (pipe_state != NULL) {
-               dce_call->context->private_data = NULL;
-
                /*
                 * If we had a challenge remembered on the connection
                 * consider this for usage. This can't be cleanup
@@ -215,67 +493,16 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
                       NETLOGON_NEG_AUTHENTICATED_RPC_LSASS |
                       NETLOGON_NEG_AUTHENTICATED_RPC;
 
-       negotiate_flags = *r->in.negotiate_flags & server_flags;
-
-       if (negotiate_flags & NETLOGON_NEG_AUTHENTICATED_RPC) {
-               reject_none_rpc = false;
-       }
-
-       if (negotiate_flags & NETLOGON_NEG_STRONG_KEYS) {
-               reject_des_client = false;
-       }
-
-       if (negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
-               reject_des_client = false;
-               reject_md5_client = false;
-       }
-
-       if (reject_des_client || reject_md5_client) {
-               /*
-                * Here we match Windows 2012 and return no flags.
-                */
-               *r->out.negotiate_flags = 0;
-               return NT_STATUS_DOWNGRADE_DETECTED;
-       }
-
-       /*
-        * This talloc_free is important to prevent re-use of the
-        * challenge.  We have to delay it this far due to NETApp
-        * servers per:
-        * https://bugzilla.samba.org/show_bug.cgi?id=11291
-        */
-       TALLOC_FREE(pipe_state);
-
-       /*
-        * At this point we must also cleanup the TDB cache
-        * entry, if we fail the client needs to call
-        * netr_ServerReqChallenge again.
-        *
-        * Note: this handles a non existing record just fine,
-        * the r->in.computer_name might not be the one used
-        * in netr_ServerReqChallenge(), but we are trying to
-        * just tidy up the normal case to prevent re-use.
-        */
-       schannel_delete_challenge(dce_call->conn->dce_ctx->lp_ctx,
-                                 r->in.computer_name);
-
        /*
-        * According to Microsoft (see bugid #6099)
-        * Windows 7 looks at the negotiate_flags
-        * returned in this structure *even if the
-        * call fails with access denied!
+        * If weak crypto is disabled, do not announce that we support RC4.
         */
-       *r->out.negotiate_flags = negotiate_flags;
-
-       if (reject_none_rpc) {
-               /* schannel must be used, but client did not offer it. */
-               DEBUG(0,("%s: schannel required but client failed "
-                       "to offer it. Client was %s\n",
-                        __func__,
-                        log_escape(mem_ctx, r->in.account_name)));
-               return NT_STATUS_ACCESS_DENIED;
+       if (lpcfg_weak_crypto(dce_call->conn->dce_ctx->lp_ctx) ==
+           SAMBA_WEAK_CRYPTO_DISALLOWED) {
+               server_flags &= ~NETLOGON_NEG_ARCFOUR;
        }
 
+       negotiate_flags = *r->in.negotiate_flags & server_flags;
+
        switch (r->in.secure_channel_type) {
        case SEC_CHAN_WKSTA:
        case SEC_CHAN_DNS_DOMAIN:
@@ -284,21 +511,25 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
        case SEC_CHAN_RODC:
                break;
        case SEC_CHAN_NULL:
-               return NT_STATUS_INVALID_PARAMETER;
+               return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_INVALID_PARAMETER);
        default:
                DEBUG(1, ("Client asked for an invalid secure channel type: %d\n",
                          r->in.secure_channel_type));
-               return NT_STATUS_INVALID_PARAMETER;
+               return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_INVALID_PARAMETER);
        }
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               dce_call->conn->dce_ctx->lp_ctx,
-                               system_session(dce_call->conn->dce_ctx->lp_ctx),
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
-               return NT_STATUS_INVALID_SYSTEM_SERVICE;
+               return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_INVALID_SYSTEM_SERVICE);
        }
 
        if (r->in.secure_channel_type == SEC_CHAN_DOMAIN ||
@@ -327,16 +558,25 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
                encoded_name = ldb_binary_encode_string(mem_ctx,
                                                        r->in.account_name);
                if (encoded_name == NULL) {
-                       return NT_STATUS_NO_MEMORY;
+                       return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_NO_MEMORY);
                }
 
                len = strlen(encoded_name);
                if (len < 2) {
-                       return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+                       return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_NO_TRUST_SAM_ACCOUNT);
                }
 
                if (require_trailer && encoded_name[len - 1] != trailer) {
-                       return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+                       return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_NO_TRUST_SAM_ACCOUNT);
                }
                encoded_name[len - 1] = '\0';
 
@@ -354,30 +594,48 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
                                  "but there's no tdo for [%s] => [%s] \n",
                                  log_escape(mem_ctx, r->in.account_name),
                                  encoded_name));
-                       return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+                       return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_NO_TRUST_SAM_ACCOUNT);
                }
                if (!NT_STATUS_IS_OK(nt_status)) {
-                       return nt_status;
+                       return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               nt_status);
                }
 
                nt_status = dsdb_trust_get_incoming_passwords(tdo_msg, mem_ctx,
                                                              &curNtHash,
                                                              &prevNtHash);
                if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED)) {
-                       return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+                       return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_NO_TRUST_SAM_ACCOUNT);
                }
                if (!NT_STATUS_IS_OK(nt_status)) {
-                       return nt_status;
+                       return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               nt_status);
                }
 
                flatname = ldb_msg_find_attr_as_string(tdo_msg, "flatName", NULL);
                if (flatname == NULL) {
-                       return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+                       return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_NO_TRUST_SAM_ACCOUNT);
                }
 
                *trust_account_for_search = talloc_asprintf(mem_ctx, "%s$", flatname);
                if (*trust_account_for_search == NULL) {
-                       return NT_STATUS_NO_MEMORY;
+                       return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_NO_MEMORY);
                }
        } else {
                *trust_account_for_search = r->in.account_name;
@@ -392,14 +650,20 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
        if (num_records == 0) {
                DEBUG(3,("Couldn't find user [%s] in samdb.\n",
                         log_escape(mem_ctx, r->in.account_name)));
-               return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+               return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_NO_TRUST_SAM_ACCOUNT);
        }
 
        if (num_records > 1) {
                DEBUG(0,("Found %d records matching user [%s]\n",
                         num_records,
                         log_escape(mem_ctx, r->in.account_name)));
-               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_INTERNAL_DB_CORRUPTION);
        }
 
        *trust_account_in_db = ldb_msg_find_attr_as_string(msgs[0],
@@ -408,9 +672,20 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
        if (*trust_account_in_db == NULL) {
                DEBUG(0,("No samAccountName returned in record matching user [%s]\n",
                         r->in.account_name));
-               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               return dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                               dce_call, r, pipe_state, negotiate_flags,
+                               NULL, /* trust_account_in_db */
+                               NT_STATUS_INTERNAL_DB_CORRUPTION);
+       }
+
+       nt_status = dcesrv_netr_ServerAuthenticate3_check_downgrade(
+                       dce_call, r, pipe_state, negotiate_flags,
+                       *trust_account_in_db,
+                       NT_STATUS_OK);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
        }
-       
+
        user_account_control = ldb_msg_find_attr_as_uint(msgs[0], "userAccountControl", 0);
 
        if (user_account_control & UF_ACCOUNTDISABLE) {
@@ -449,7 +724,7 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
        if (!(user_account_control & UF_INTERDOMAIN_TRUST_ACCOUNT)) {
                nt_status = samdb_result_passwords_no_lockout(mem_ctx,
                                        dce_call->conn->dce_ctx->lp_ctx,
-                                       msgs[0], NULL, &curNtHash);
+                                       msgs[0], &curNtHash);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        return NT_STATUS_ACCESS_DENIED;
                }
@@ -529,6 +804,8 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(
        struct dom_sid *sid = NULL;
        const char *trust_account_for_search = NULL;
        const char *trust_account_in_db = NULL;
+       struct imessaging_context *imsg_ctx =
+               dcesrv_imessaging_context(dce_call->conn);
        struct auth_usersupplied_info ui = {
                .local_host = dce_call->conn->local_address,
                .remote_host = dce_call->conn->remote_address,
@@ -555,15 +832,16 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(
        ui.netlogon_trust_account.account_name = trust_account_in_db;
        ui.mapped.account_name = trust_account_for_search;
        log_authentication_event(
-               dce_call->conn->msg_ctx,
+               imsg_ctx,
                dce_call->conn->dce_ctx->lp_ctx,
                NULL,
                &ui,
                status,
                lpcfg_workgroup(dce_call->conn->dce_ctx->lp_ctx),
                trust_account_in_db,
-               NULL,
-               sid);
+               sid,
+               NULL /* client_audit_info */,
+               NULL /* server_audit_info */);
 
        return status;
 }
@@ -614,40 +892,6 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate2(struct dcesrv_call_state *dce_ca
        return dcesrv_netr_ServerAuthenticate3(dce_call, mem_ctx, &r3);
 }
 
-/*
- * NOTE: The following functions are nearly identical to the ones available in
- * source3/rpc_server/srv_nelog_nt.c
- * The reason we keep 2 copies is that they use different structures to
- * represent the auth_info and the decrpc pipes.
- */
-static NTSTATUS dcesrv_netr_creds_server_step_check(struct dcesrv_call_state *dce_call,
-                                                   TALLOC_CTX *mem_ctx,
-                                                   const char *computer_name,
-                                                   struct netr_Authenticator *received_authenticator,
-                                                   struct netr_Authenticator *return_authenticator,
-                                                   struct netlogon_creds_CredentialState **creds_out)
-{
-       NTSTATUS nt_status;
-       int schannel = lpcfg_server_schannel(dce_call->conn->dce_ctx->lp_ctx);
-       bool schannel_global_required = (schannel == true);
-
-       if (schannel_global_required) {
-               if (dce_call->conn->auth_state.auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
-                       DBG_ERR("[%s] is not using schannel\n",
-                               computer_name);
-                       return NT_STATUS_ACCESS_DENIED;
-               }
-       }
-
-       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;
-}
-
 /*
   Change the machine account password for the currently connected
   client.  Supplies only the NT#.
@@ -658,11 +902,7 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call
 {
        struct netlogon_creds_CredentialState *creds;
        struct ldb_context *sam_ctx;
-       const char * const attrs[] = { "unicodePwd", NULL };
-       struct ldb_message **res;
-       struct samr_Password *oldNtHash;
        NTSTATUS nt_status;
-       int ret;
 
        nt_status = dcesrv_netr_creds_server_step_check(dce_call,
                                                        mem_ctx,
@@ -671,41 +911,21 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call
                                                        &creds);
        NT_STATUS_NOT_OK_RETURN(nt_status);
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               dce_call->conn->dce_ctx->lp_ctx,
-                               system_session(dce_call->conn->dce_ctx->lp_ctx),
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
 
-       netlogon_creds_des_decrypt(creds, r->in.new_password);
-
-       /* fetch the old password hashes (the NT hash has to exist) */
-
-       ret = gendb_search(sam_ctx, mem_ctx, NULL, &res, attrs,
-                          "(&(objectClass=user)(objectSid=%s))",
-                          ldap_encode_ndr_dom_sid(mem_ctx, creds->sid));
-       if (ret != 1) {
-               return NT_STATUS_WRONG_PASSWORD;
-       }
-
-       nt_status = samdb_result_passwords_no_lockout(mem_ctx,
-                                                     dce_call->conn->dce_ctx->lp_ctx,
-                                                     res[0], NULL, &oldNtHash);
-       if (!NT_STATUS_IS_OK(nt_status) || !oldNtHash) {
-               return NT_STATUS_WRONG_PASSWORD;
-       }
+       nt_status = netlogon_creds_des_decrypt(creds, r->in.new_password);
+       NT_STATUS_NOT_OK_RETURN(nt_status);
 
        /* Using the sid for the account as the key, set the password */
        nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
                                           creds->sid,
                                           NULL, /* Don't have version */
                                           NULL, /* Don't have plaintext */
-                                          NULL, r->in.new_password,
-                                          NULL, oldNtHash, /* Password change */
+                                          r->in.new_password,
+                                          DSDB_PASSWORD_CHECKED_AND_CORRECT, /* Password change */
                                           NULL, NULL);
        return nt_status;
 }
@@ -719,14 +939,13 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
 {
        struct netlogon_creds_CredentialState *creds;
        struct ldb_context *sam_ctx;
-       const char * const attrs[] = { "dBCSPwd", "unicodePwd", NULL };
-       struct ldb_message **res;
-       struct samr_Password *oldLmHash, *oldNtHash;
        struct NL_PASSWORD_VERSION version = {};
        const uint32_t *new_version = NULL;
        NTSTATUS nt_status;
-       DATA_BLOB new_password;
-       int ret;
+       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;
 
        nt_status = dcesrv_netr_creds_server_step_check(dce_call,
@@ -736,12 +955,7 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
                                                        &creds);
        NT_STATUS_NOT_OK_RETURN(nt_status);
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               dce_call->conn->dce_ctx->lp_ctx,
-                               system_session(dce_call->conn->dce_ctx->lp_ctx),
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
@@ -750,9 +964,17 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
        SIVAL(password_buf.data, 512, r->in.new_password->length);
 
        if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
-               netlogon_creds_aes_decrypt(creds, password_buf.data, 516);
+               nt_status = netlogon_creds_aes_decrypt(creds,
+                                                      password_buf.data,
+                                                      516);
        } else {
-               netlogon_creds_arcfour_crypt(creds, password_buf.data, 516);
+               nt_status = netlogon_creds_arcfour_crypt(creds,
+                                                        password_buf.data,
+                                                        516);
+       }
+
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
        }
 
        switch (creds->secure_channel_type) {
@@ -783,19 +1005,58 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
                return NT_STATUS_WRONG_PASSWORD;
        }
 
-       /* fetch the old password hashes (at least one of both has to exist) */
+       /*
+        * 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;
+       }
 
-       ret = gendb_search(sam_ctx, mem_ctx, NULL, &res, attrs,
-                          "(&(objectClass=user)(objectSid=%s))",
-                          ldap_encode_ndr_dom_sid(mem_ctx, creds->sid));
-       if (ret != 1) {
+       /*
+        * 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;
        }
 
-       nt_status = samdb_result_passwords_no_lockout(mem_ctx,
-                                                     dce_call->conn->dce_ctx->lp_ctx,
-                                                     res[0], &oldLmHash, &oldNtHash);
-       if (!NT_STATUS_IS_OK(nt_status) || (!oldLmHash && !oldNtHash)) {
+       /*
+        * 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 (confounder_len > 0 && data_blob_equal_const_time(&dec_blob, &enc_blob)) {
+               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_equal_const_time(&dec_blob, &enc_blob)) {
+               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;
        }
 
@@ -804,8 +1065,8 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
                                           creds->sid,
                                           new_version,
                                           &new_password, /* we have plaintext */
-                                          NULL, NULL,
-                                          oldLmHash, oldNtHash, /* Password change */
+                                          NULL,
+                                          DSDB_PASSWORD_CHECKED_AND_CORRECT, /* Password change */
                                           NULL, NULL);
        return nt_status;
 }
@@ -834,6 +1095,8 @@ static WERROR dcesrv_netr_LogonUasLogoff(struct dcesrv_call_state *dce_call, TAL
 static NTSTATUS dcesrv_netr_LogonSamLogon_check(struct dcesrv_call_state *dce_call,
                                                const struct netr_LogonSamLogonEx *r)
 {
+       enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+
        switch (r->in.logon_level) {
        case NetlogonInteractiveInformation:
        case NetlogonServiceInformation:
@@ -888,9 +1151,11 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_check(struct dcesrv_call_state *dce_ca
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       dcesrv_call_auth_info(dce_call, NULL, &auth_level);
+
        switch (r->in.validation_level) {
        case NetlogonValidationSamInfo4: /* 6 */
-               if (dce_call->conn->auth_state.auth_level < DCERPC_AUTH_LEVEL_PRIVACY) {
+               if (auth_level < DCERPC_AUTH_LEVEL_PRIVACY) {
                        return NT_STATUS_INVALID_PARAMETER;
                }
                break;
@@ -938,6 +1203,8 @@ static void dcesrv_netr_LogonSamLogon_base_reply(
 static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamLogon_base_state *state)
 {
        struct dcesrv_call_state *dce_call = state->dce_call;
+       struct imessaging_context *imsg_ctx =
+               dcesrv_imessaging_context(dce_call->conn);
        TALLOC_CTX *mem_ctx = state->mem_ctx;
        struct netr_LogonSamLogonEx *r = &state->r;
        struct netlogon_creds_CredentialState *creds = state->creds;
@@ -947,6 +1214,35 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamL
        struct auth_usersupplied_info *user_info = NULL;
        NTSTATUS nt_status;
        struct tevent_req *subreq = NULL;
+       enum dcerpc_AuthType auth_type = DCERPC_AUTH_TYPE_NONE;
+       enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+
+       dcesrv_call_auth_info(dce_call, &auth_type, &auth_level);
+
+       switch (dce_call->pkt.u.request.opnum) {
+       case NDR_NETR_LOGONSAMLOGON:
+       case NDR_NETR_LOGONSAMLOGONWITHFLAGS:
+               /*
+                * These already called dcesrv_netr_check_schannel()
+                * via dcesrv_netr_creds_server_step_check()
+                */
+               break;
+       case NDR_NETR_LOGONSAMLOGONEX:
+       default:
+               if (auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+
+               nt_status = dcesrv_netr_check_schannel(dce_call,
+                                                      creds,
+                                                      auth_type,
+                                                      auth_level,
+                                                      dce_call->pkt.u.request.opnum);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       return nt_status;
+               }
+               break;
+       }
 
        *r->out.authoritative = 1;
 
@@ -969,9 +1265,10 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamL
 
        user_info->service_description = "SamLogon";
 
-       netlogon_creds_decrypt_samlogon_logon(creds,
-                                             r->in.logon_level,
-                                             r->in.logon);
+       nt_status = netlogon_creds_decrypt_samlogon_logon(creds,
+                                                         r->in.logon_level,
+                                                         r->in.logon);
+       NT_STATUS_NOT_OK_RETURN(nt_status);
 
        switch (r->in.logon_level) {
        case NetlogonInteractiveInformation:
@@ -982,7 +1279,8 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamL
        case NetlogonNetworkTransitiveInformation:
 
                nt_status = auth_context_create_for_netlogon(mem_ctx,
-                                       dce_call->event_ctx, dce_call->msg_ctx,
+                                       dce_call->event_ctx,
+                                       imsg_ctx,
                                        dce_call->conn->dce_ctx->lp_ctx,
                                        &auth_context);
                NT_STATUS_NOT_OK_RETURN(nt_status);
@@ -1008,6 +1306,7 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamL
                user_info->netlogon_trust_account.sid
                        = creds->sid;
 
+               break;
        default:
                /* We do not need to set up the user_info in this case */
                break;
@@ -1039,6 +1338,9 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamL
                NT_STATUS_HAVE_NO_MEMORY(user_info->password.hash.nt);
                *user_info->password.hash.nt = r->in.logon->password->ntpassword;
 
+               user_info->logon_id
+                   = r->in.logon->password->identity_info.logon_id;
+
                break;
        case NetlogonNetworkInformation:
        case NetlogonNetworkTransitiveInformation:
@@ -1063,6 +1365,9 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamL
                user_info->password.response.lanman = data_blob_talloc(mem_ctx, r->in.logon->network->lm.data, r->in.logon->network->lm.length);
                user_info->password.response.nt = data_blob_talloc(mem_ctx, r->in.logon->network->nt.data, r->in.logon->network->nt.length);
 
+               user_info->logon_id
+                   = r->in.logon->network->identity_info.logon_id;
+
                nt_status = NTLMv2_RESPONSE_verify_netlogon_creds(
                                        user_info->client.account_name,
                                        user_info->client.domain_name,
@@ -1091,8 +1396,11 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamL
 
                        r->out.validation->generic = generic;
 
+                       user_info->logon_id
+                           = r->in.logon->generic->identity_info.logon_id;
+
                        irpc_handle = irpc_binding_handle_by_name(mem_ctx,
-                                                                 dce_call->msg_ctx,
+                                                                 imsg_ctx,
                                                                  "kdc_server",
                                                                  &ndr_table_irpc);
                        if (irpc_handle == NULL) {
@@ -1120,7 +1428,7 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamL
                        return NT_STATUS_OK;
                }
 
-               /* Until we get an implemetnation of these other packages */
+               /* Until we get an implementation of these other packages */
                return NT_STATUS_INVALID_PARAMETER;
        }
        default:
@@ -1163,6 +1471,7 @@ static void dcesrv_netr_LogonSamLogon_base_auth_done(struct tevent_req *subreq)
        case 2:
                nt_status = auth_convert_user_info_dc_saminfo2(mem_ctx,
                                                               user_info_dc,
+                                                              AUTH_INCLUDE_RESOURCE_GROUPS,
                                                               &sam2);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        r->out.result = nt_status;
@@ -1176,7 +1485,8 @@ static void dcesrv_netr_LogonSamLogon_base_auth_done(struct tevent_req *subreq)
        case 3:
                nt_status = auth_convert_user_info_dc_saminfo3(mem_ctx,
                                                               user_info_dc,
-                                                              &sam3);
+                                                              AUTH_INCLUDE_RESOURCE_GROUPS,
+                                                              &sam3, NULL);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        r->out.result = nt_status;
                        dcesrv_netr_LogonSamLogon_base_reply(state);
@@ -1189,7 +1499,8 @@ static void dcesrv_netr_LogonSamLogon_base_auth_done(struct tevent_req *subreq)
        case 6:
                nt_status = auth_convert_user_info_dc_saminfo6(mem_ctx,
                                                               user_info_dc,
-                                                              &sam6);
+                                                              AUTH_INCLUDE_RESOURCE_GROUPS,
+                                                              &sam6, NULL);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        r->out.result = nt_status;
                        dcesrv_netr_LogonSamLogon_base_reply(state);
@@ -1252,9 +1563,14 @@ static void dcesrv_netr_LogonSamLogon_base_reply(
        NTSTATUS status;
 
        if (NT_STATUS_IS_OK(r->out.result)) {
-               netlogon_creds_encrypt_samlogon_validation(state->creds,
-                                                          r->in.validation_level,
-                                                          r->out.validation);
+               status = netlogon_creds_encrypt_samlogon_validation(state->creds,
+                                                                   r->in.validation_level,
+                                                                   r->out.validation);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DBG_ERR("netlogon_creds_encrypt_samlogon_validation() "
+                               "failed - %s\n",
+                               nt_errstr(status));
+               }
        }
 
        if (state->_r.lslex != NULL) {
@@ -1315,10 +1631,6 @@ static NTSTATUS dcesrv_netr_LogonSamLogonEx(struct dcesrv_call_state *dce_call,
                return nt_status;
        }
 
-       if (dce_call->conn->auth_state.auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
-               return NT_STATUS_ACCESS_DENIED;
-       }
-
        nt_status = dcesrv_netr_LogonSamLogon_base_call(state);
 
        if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
@@ -1539,8 +1851,6 @@ static NTSTATUS dcesrv_netr_AccountSync(struct dcesrv_call_state *dce_call, TALL
 static WERROR dcesrv_netr_GetDcName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
                       struct netr_GetDcName *r)
 {
-       struct auth_session_info *session_info =
-               dcesrv_call_session_info(dce_call);
        const char * const attrs[] = { NULL };
        struct ldb_context *sam_ctx;
        struct ldb_message **res;
@@ -1562,17 +1872,12 @@ static WERROR dcesrv_netr_GetDcName(struct dcesrv_call_state *dce_call, TALLOC_C
                }
 
                /*
-                * TODO: Should we also varify that only valid
+                * TODO: Should we also verify that only valid
                 *       netbios name characters are used?
                 */
        }
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               dce_call->conn->dce_ctx->lp_ctx,
-                               session_info,
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return WERR_DS_UNAVAILABLE;
        }
@@ -1621,6 +1926,8 @@ static WERROR dcesrv_netr_LogonControl_base_call(struct dcesrv_netr_LogonControl
        struct loadparm_context *lp_ctx = state->dce_call->conn->dce_ctx->lp_ctx;
        struct auth_session_info *session_info =
                dcesrv_call_session_info(state->dce_call);
+       struct imessaging_context *imsg_ctx =
+               dcesrv_imessaging_context(state->dce_call->conn);
        enum security_user_level security_level;
        struct dcerpc_binding_handle *irpc_handle;
        struct tevent_req *subreq;
@@ -1772,13 +2079,8 @@ static WERROR dcesrv_netr_LogonControl_base_call(struct dcesrv_netr_LogonControl
                if (!ok) {
                        struct ldb_context *sam_ctx;
 
-                       sam_ctx = samdb_connect(
-                               state,
-                               state->dce_call->event_ctx,
-                               lp_ctx,
-                               system_session(lp_ctx),
-                               state->dce_call->conn->remote_address,
-                               0);
+                       sam_ctx = dcesrv_samdb_connect_as_system(state,
+                                                                state->dce_call);
                        if (sam_ctx == NULL) {
                                return WERR_DS_UNAVAILABLE;
                        }
@@ -1800,7 +2102,7 @@ static WERROR dcesrv_netr_LogonControl_base_call(struct dcesrv_netr_LogonControl
        }
 
        irpc_handle = irpc_binding_handle_by_name(state,
-                                                 state->dce_call->msg_ctx,
+                                                 imsg_ctx,
                                                  "winbind_server",
                                                  &ndr_table_winbind);
        if (irpc_handle == NULL) {
@@ -1975,8 +2277,6 @@ static WERROR fill_trusted_domains_array(TALLOC_CTX *mem_ctx,
 static WERROR dcesrv_netr_GetAnyDCName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
                       struct netr_GetAnyDCName *r)
 {
-       struct auth_session_info *session_info =
-               dcesrv_call_session_info(dce_call);
        struct netr_DomainTrustList *trusts;
        struct ldb_context *sam_ctx;
        struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
@@ -1990,12 +2290,7 @@ static WERROR dcesrv_netr_GetAnyDCName(struct dcesrv_call_state *dce_call, TALLO
                r->in.domainname = lpcfg_workgroup(lp_ctx);
        }
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               lp_ctx,
-                               session_info,
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return WERR_DS_UNAVAILABLE;
        }
@@ -2069,6 +2364,30 @@ static NTSTATUS dcesrv_netr_LogonGetCapabilities(struct dcesrv_call_state *dce_c
        struct netlogon_creds_CredentialState *creds;
        NTSTATUS status;
 
+       switch (r->in.query_level) {
+       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;
+       default:
+               /*
+                * There would not be a way to marshall the
+                * the response. Which would mean our final
+                * ndr_push would fail an we would return
+                * an RPC-level fault with DCERPC_FAULT_BAD_STUB_DATA.
+                *
+                * But it's important to match a Windows server
+                * especially before KB5028166, see also our bug #15418
+                * Otherwise Windows client would stop talking to us.
+                */
+               DCESRV_FAULT(DCERPC_NCA_S_FAULT_INVALID_TAG);
+       }
+
        status = dcesrv_netr_creds_server_step_check(dce_call,
                                                     mem_ctx,
                                                     r->in.computer_name,
@@ -2080,10 +2399,6 @@ static NTSTATUS dcesrv_netr_LogonGetCapabilities(struct dcesrv_call_state *dce_c
        }
        NT_STATUS_NOT_OK_RETURN(status);
 
-       if (r->in.query_level != 1) {
-               return NT_STATUS_NOT_SUPPORTED;
-       }
-
        r->out.capabilities->server_capabilities = creds->negotiate_flags;
 
        return NT_STATUS_OK;
@@ -2137,17 +2452,9 @@ static WERROR dcesrv_netr_NETRLOGONCOMPUTECLIENTDIGEST(struct dcesrv_call_state
 static WERROR dcesrv_netr_DsRGetSiteName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
                                  struct netr_DsRGetSiteName *r)
 {
-       struct auth_session_info *session_info =
-               dcesrv_call_session_info(dce_call);
        struct ldb_context *sam_ctx;
-       struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               lp_ctx,
-                               session_info,
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return WERR_DS_UNAVAILABLE;
        }
@@ -2176,13 +2483,15 @@ static NTSTATUS fill_our_one_domain_info(TALLOC_CTX *mem_ctx,
        ZERO_STRUCTP(info);
 
        if (is_trust_list) {
-               struct netr_trust_extension *tei = NULL;
+               struct netr_trust_extension *te = NULL;
+               struct netr_trust_extension_info *tei = NULL;
 
                /* w2k8 only fills this on trusted domains */
-               tei = talloc_zero(mem_ctx, struct netr_trust_extension);
-               if (tei == NULL) {
+               te = talloc_zero(mem_ctx, struct netr_trust_extension);
+               if (te == NULL) {
                        return NT_STATUS_NO_MEMORY;
                }
+               tei = &te->info;
                tei->flags |= NETR_TRUST_FLAG_PRIMARY;
 
                /*
@@ -2203,8 +2512,7 @@ static NTSTATUS fill_our_one_domain_info(TALLOC_CTX *mem_ctx,
                 */
                tei->trust_attributes = 0;
 
-               info->trust_extension.info = tei;
-               info->trust_extension.length = 16;
+               info->trust_extension.info = te;
        }
 
        if (is_trust_list) {
@@ -2237,15 +2545,17 @@ static NTSTATUS fill_trust_one_domain_info(TALLOC_CTX *mem_ctx,
                                const struct lsa_TrustDomainInfoInfoEx *tdo,
                                struct netr_OneDomainInfo *info)
 {
-       struct netr_trust_extension *tei = NULL;
+       struct netr_trust_extension *te = NULL;
+       struct netr_trust_extension_info *tei = NULL;
 
        ZERO_STRUCTP(info);
 
        /* w2k8 only fills this on trusted domains */
-       tei = talloc_zero(mem_ctx, struct netr_trust_extension);
-       if (tei == NULL) {
+       te = talloc_zero(mem_ctx, struct netr_trust_extension);
+       if (te == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
+       tei = &te->info;
 
        if (tdo->trust_direction & LSA_TRUST_DIRECTION_INBOUND) {
                tei->flags |= NETR_TRUST_FLAG_INBOUND;
@@ -2267,8 +2577,7 @@ static NTSTATUS fill_trust_one_domain_info(TALLOC_CTX *mem_ctx,
        tei->trust_type = tdo->trust_type;
        tei->trust_attributes = tdo->trust_attributes;
 
-       info->trust_extension.info = tei;
-       info->trust_extension.length = 16;
+       info->trust_extension.info = te;
 
        info->domainname.string = tdo->netbios_name.string;
        if (tdo->trust_type != LSA_TRUST_TYPE_DOWNLEVEL) {
@@ -2307,7 +2616,7 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
        };
        const char * const attrs2[] = { "sAMAccountName", "dNSHostName",
                "msDS-SupportedEncryptionTypes", NULL };
-       const char *sam_account_name, *old_dns_hostname, *prefix1, *prefix2;
+       const char *sam_account_name, *old_dns_hostname;
        struct ldb_context *sam_ctx;
        const struct GUID *our_domain_guid = NULL;
        struct lsa_TrustDomainInfoInfoEx *our_tdo = NULL;
@@ -2316,6 +2625,7 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
        struct ldb_dn *workstation_dn;
        struct netr_DomainInformation *domain_info;
        struct netr_LsaPolicyInformation *lsa_policy_info;
+       struct auth_session_info *workstation_session_info = NULL;
        uint32_t default_supported_enc_types = 0xFFFFFFFF;
        bool update_dns_hostname = true;
        int ret, i;
@@ -2335,8 +2645,8 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
                                                frame);
                local  = tsocket_address_string(dce_call->conn->local_address,
                                                frame);
-               DBG_ERR(("Bad credentials - "
-                        "computer[%s] remote[%s] local[%s]\n"),
+               DBG_ERR("Bad credentials - "
+                       "computer[%s] remote[%s] local[%s]\n",
                        log_escape(frame, r->in.computer_name),
                        remote,
                        local);
@@ -2344,12 +2654,8 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
        }
        NT_STATUS_NOT_OK_RETURN(status);
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               dce_call->conn->dce_ctx->lp_ctx,
-                               system_session(dce_call->conn->dce_ctx->lp_ctx),
-                               dce_call->conn->remote_address,
-                               0);
+       /* We want to avoid connecting as system. */
+       sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
@@ -2366,6 +2672,33 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
                                                dom_sid_string(mem_ctx, creds->sid));
                NT_STATUS_HAVE_NO_MEMORY(workstation_dn);
 
+               /* Get the workstation's session info from the database. */
+               status = authsam_get_session_info_principal(mem_ctx,
+                                                           dce_call->conn->dce_ctx->lp_ctx,
+                                                           sam_ctx,
+                                                           NULL, /* principal */
+                                                           workstation_dn,
+                                                           0, /* session_info_flags */
+                                                           &workstation_session_info);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               /*
+                * Reconnect to samdb as the workstation, now that we have its
+                * session info. We do this so the database update can be
+                * attributed to the workstation account in the audit logs --
+                * otherwise it might be incorrectly attributed to
+                * SID_NT_ANONYMOUS.
+                */
+               sam_ctx = dcesrv_samdb_connect_session_info(mem_ctx,
+                                                           dce_call,
+                                                           workstation_session_info,
+                                                           workstation_session_info);
+               if (sam_ctx == NULL) {
+                       return NT_STATUS_INVALID_SYSTEM_SERVICE;
+               }
+
                /* Lookup for attributes in workstation object */
                ret = gendb_search_dn(sam_ctx, mem_ctx, workstation_dn, &res1,
                                      attrs2);
@@ -2382,24 +2715,7 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
                        return NT_STATUS_INTERNAL_DB_CORRUPTION;
                }
 
-               /*
-                * Checks that the sam account name without a possible "$"
-                * matches as prefix with the DNS hostname in the workstation
-                * info structure.
-                */
-               prefix1 = talloc_strndup(mem_ctx, sam_account_name,
-                                        strcspn(sam_account_name, "$"));
-               NT_STATUS_HAVE_NO_MEMORY(prefix1);
-               if (r->in.query->workstation_info->dns_hostname != NULL) {
-                       prefix2 = talloc_strndup(mem_ctx,
-                                                r->in.query->workstation_info->dns_hostname,
-                                                strcspn(r->in.query->workstation_info->dns_hostname, "."));
-                       NT_STATUS_HAVE_NO_MEMORY(prefix2);
-
-                       if (strcasecmp(prefix1, prefix2) != 0) {
-                               update_dns_hostname = false;
-                       }
-               } else {
+               if (r->in.query->workstation_info->dns_hostname == NULL) {
                        update_dns_hostname = false;
                }
 
@@ -2411,13 +2727,10 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
                /*
                 * Updates the DNS hostname when the client wishes that the
                 * server should handle this for him
-                * ("NETR_WS_FLAG_HANDLES_SPN_UPDATE" not set). And this is
-                * obviously only checked when we do already have a
-                * "dNSHostName".
+                * ("NETR_WS_FLAG_HANDLES_SPN_UPDATE" not set).
                 * See MS-NRPC section 3.5.4.3.9
                 */
-               if ((old_dns_hostname != NULL) &&
-                   (r->in.query->workstation_info->workstation_flags
+               if ((r->in.query->workstation_info->workstation_flags
                    & NETR_WS_FLAG_HANDLES_SPN_UPDATE) != 0) {
                        update_dns_hostname = false;
                }
@@ -2461,9 +2774,14 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
                                                         os_version->BuildNumber);
                        NT_STATUS_HAVE_NO_MEMORY(os_version_str);
 
-                       ret = ldb_msg_add_string(new_msg,
-                                                "operatingSystemServicePack",
-                                                os_version->CSDVersion);
+                       if (strlen(os_version->CSDVersion) != 0) {
+                               ret = ldb_msg_add_string(new_msg,
+                                                        "operatingSystemServicePack",
+                                                        os_version->CSDVersion);
+                       } else {
+                               ret = samdb_msg_add_delete(sam_ctx, mem_ctx, new_msg,
+                                                          "operatingSystemServicePack");
+                       }
                        if (ret != LDB_SUCCESS) {
                                return NT_STATUS_NO_MEMORY;
                        }
@@ -2521,7 +2839,7 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
                        }
                }
 
-               if (dsdb_replace(sam_ctx, new_msg, 0) != LDB_SUCCESS) {
+               if (dsdb_replace(sam_ctx, new_msg, DSDB_FLAG_FORCE_ALLOW_VALIDATED_DNS_HOSTNAME_SPN_WRITE) != LDB_SUCCESS) {
                        DEBUG(3,("Impossible to update samdb: %s\n",
                                ldb_errstring(sam_ctx)));
                }
@@ -2554,7 +2872,7 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
 
                ZERO_STRUCTP(domain_info);
 
-               /* Informations about the local and trusted domains */
+               /* Information about the local and trusted domains */
 
                status = fill_our_one_domain_info(mem_ctx,
                                                  our_tdo,
@@ -2663,21 +2981,25 @@ static bool sam_rodc_access_check(struct ldb_context *sam_ctx,
                                  struct dom_sid *user_sid,
                                  struct ldb_dn *obj_dn)
 {
-       const char *rodc_attrs[] = { "msDS-KrbTgtLink", "msDS-NeverRevealGroup", "msDS-RevealOnDemandGroup", "objectGUID", NULL };
+       const char *rodc_attrs[] = { "msDS-NeverRevealGroup",
+                                    "msDS-RevealOnDemandGroup",
+                                    "userAccountControl",
+                                    NULL };
        const char *obj_attrs[] = { "tokenGroups", "objectSid", "UserAccountControl", "msDS-KrbTgtLinkBL", NULL };
        struct ldb_dn *rodc_dn;
        int ret;
        struct ldb_result *rodc_res = NULL, *obj_res = NULL;
-       const struct dom_sid *additional_sids[] = { NULL, NULL };
        WERROR werr;
-       struct dom_sid *object_sid;
-       const struct dom_sid **never_reveal_sids, **reveal_sids, **token_sids;
 
        rodc_dn = ldb_dn_new_fmt(mem_ctx, sam_ctx, "<SID=%s>",
                                 dom_sid_string(mem_ctx, user_sid));
        if (!ldb_dn_validate(rodc_dn)) goto denied;
 
-       /* do the two searches we need */
+       /*
+        * do the two searches we need
+        * We need DSDB_SEARCH_SHOW_EXTENDED_DN as we get a SID list
+        * out of the extended DNs
+        */
        ret = dsdb_search_dn(sam_ctx, mem_ctx, &rodc_res, rodc_dn, rodc_attrs,
                             DSDB_SEARCH_SHOW_EXTENDED_DN);
        if (ret != LDB_SUCCESS || rodc_res->count != 1) goto denied;
@@ -2685,44 +3007,14 @@ static bool sam_rodc_access_check(struct ldb_context *sam_ctx,
        ret = dsdb_search_dn(sam_ctx, mem_ctx, &obj_res, obj_dn, obj_attrs, 0);
        if (ret != LDB_SUCCESS || obj_res->count != 1) goto denied;
 
-       object_sid = samdb_result_dom_sid(mem_ctx, obj_res->msgs[0], "objectSid");
+       werr = samdb_confirm_rodc_allowed_to_repl_to(sam_ctx,
+                                                    user_sid,
+                                                    rodc_res->msgs[0],
+                                                    obj_res->msgs[0]);
 
-       additional_sids[0] = object_sid;
-
-       werr = samdb_result_sid_array_dn(sam_ctx, rodc_res->msgs[0],
-                                        mem_ctx, "msDS-NeverRevealGroup", &never_reveal_sids);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto denied;
-       }
-
-       werr = samdb_result_sid_array_dn(sam_ctx, rodc_res->msgs[0],
-                                        mem_ctx, "msDS-RevealOnDemandGroup", &reveal_sids);
-       if (!W_ERROR_IS_OK(werr)) {
-               goto denied;
-       }
-
-       /*
-        * The SID list needs to include itself as well as the tokenGroups.
-        *
-        * TODO determine if sIDHistory is required for this check
-        */
-       werr = samdb_result_sid_array_ndr(sam_ctx, obj_res->msgs[0],
-                                         mem_ctx, "tokenGroups", &token_sids,
-                                         additional_sids, 1);
-       if (!W_ERROR_IS_OK(werr) || token_sids==NULL) {
-               goto denied;
-       }
-
-       if (never_reveal_sids &&
-           sid_list_match(token_sids, never_reveal_sids)) {
-               goto denied;
-       }
-
-       if (reveal_sids &&
-           sid_list_match(token_sids, reveal_sids)) {
+       if (W_ERROR_IS_OK(werr)) {
                goto allowed;
        }
-
 denied:
        return false;
 allowed:
@@ -2767,21 +3059,23 @@ static NTSTATUS dcesrv_netr_NetrLogonSendToSam(struct dcesrv_call_state *dce_cal
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               dce_call->conn->dce_ctx->lp_ctx,
-                               system_session(dce_call->conn->dce_ctx->lp_ctx),
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
 
        /* Buffer is meant to be 16-bit aligned */
        if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
-               netlogon_creds_aes_decrypt(creds, r->in.opaque_buffer, r->in.buffer_len);
+               nt_status = netlogon_creds_aes_decrypt(creds,
+                                                      r->in.opaque_buffer,
+                                                      r->in.buffer_len);
        } else {
-               netlogon_creds_arcfour_crypt(creds, r->in.opaque_buffer, r->in.buffer_len);
+               nt_status = netlogon_creds_arcfour_crypt(creds,
+                                                        r->in.opaque_buffer,
+                                                        r->in.buffer_len);
+       }
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
        }
 
        decrypted_blob.data = r->in.opaque_buffer;
@@ -2873,11 +3167,36 @@ struct dcesrv_netr_DsRGetDCName_base_state {
 
 static void dcesrv_netr_DsRGetDCName_base_done(struct tevent_req *subreq);
 
+/* Returns a nonzero value if multiple bits in 'val' are set. */
+static bool multiple_bits_set(uint32_t val)
+{
+       /*
+        * Subtracting one from an integer has the effect of flipping all the
+        * bits from the least significant bit up to and including the least
+        * significant '1' bit. For example,
+        *
+        *   0b101000 - 1
+        * = 0b100111
+        *       ====
+        *
+        * If 'val' is zero, all the bits will be flipped and thus the bitwise
+        * AND of 'val' with 'val - 1' will be zero.
+        *
+        * If the integer is nonzero, the least significant '1' bit will be
+        * ANDed with a '0' bit and so will be reset in the final result, but
+        * all other '1' bits will remain set. In other words, the effect of
+        * this expression is to mask off the least significant bit that is
+        * set. Therefore iff the result of 'val & (val - 1)' is non-zero, 'val'
+        * must contain multiple set bits.
+        */
+       return val & (val - 1);
+}
+
 static WERROR dcesrv_netr_DsRGetDCName_base_call(struct dcesrv_netr_DsRGetDCName_base_state *state)
 {
        struct dcesrv_call_state *dce_call = state->dce_call;
-       struct auth_session_info *session_info =
-               dcesrv_call_session_info(dce_call);
+       struct imessaging_context *imsg_ctx =
+               dcesrv_imessaging_context(dce_call->conn);
        TALLOC_CTX *mem_ctx = state->mem_ctx;
        struct netr_DsRGetDCNameEx2 *r = &state->r;
        struct ldb_context *sam_ctx;
@@ -2895,15 +3214,14 @@ static WERROR dcesrv_netr_DsRGetDCName_base_call(struct dcesrv_netr_DsRGetDCName
        const char *domain_name = NULL;
        const char *pdc_ip;
        bool different_domain = true;
+       bool force_remote_lookup = false;
+       uint32_t valid_flags;
+       uint32_t this_dc_valid_flags;
+       int dc_level;
 
        ZERO_STRUCTP(r->out.info);
 
-       sam_ctx = samdb_connect(state,
-                               dce_call->event_ctx,
-                               lp_ctx,
-                               session_info,
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return WERR_DS_UNAVAILABLE;
        }
@@ -2922,28 +3240,91 @@ static WERROR dcesrv_netr_DsRGetDCName_base_call(struct dcesrv_netr_DsRGetDCName
 
        /* "server_unc" is ignored by w2k3 */
 
-       if (r->in.flags & ~(DSGETDC_VALID_FLAGS)) {
+       /*
+        * With the following flags:
+        * DS_FORCE_REDISCOVERY (Flag A)
+        * DS_DIRECTORY_SERVICE_REQUIRED (Flag B)
+        * DS_DIRECTORY_SERVICE_PREFERRED (Flag C)
+        * DS_GC_SERVER_REQUIRED (Flag D)
+        * DS_PDC_REQUIRED (Flag E)
+        * DS_BACKGROUND_ONLY (Flag F)
+        * DS_IP_REQUIRED (Flag G)
+        * DS_KDC_REQUIRED (Flag H)
+        * DS_TIMESERV_REQUIRED (Flag I)
+        * DS_WRITABLE_REQUIRED (Flag J)
+        * DS_GOOD_TIMESERV_PREFERRED (Flag K)
+        * DS_AVOID_SELF (Flag L)
+        * DS_ONLY_LDAP_NEEDED (Flag M)
+        * DS_IS_FLAT_NAME (Flag N)
+        * DS_IS_DNS_NAME (Flag O)
+        * DS_TRY_NEXTCLOSEST_SITE (Flag P)
+        * DS_DIRECTORY_SERVICE_6_REQUIRED  (Flag Q)
+        * DS_WEB_SERVICE_REQUIRED (Flag T)
+        * DS_DIRECTORY_SERVICE_8_REQUIRED  (Flag U)
+        * DS_DIRECTORY_SERVICE_9_REQUIRED  (Flag V)
+        * DS_DIRECTORY_SERVICE_10_REQUIRED (Flag W)
+        * DS_RETURN_DNS_NAME (Flag R)
+        * DS_RETURN_FLAT_NAME (Flag S)
+        *
+        * MS-NRPC 3.5.4.3.1 says:
+        * ...
+        * On receiving this call, the server MUST perform the following Flags
+        * parameter validations:
+        * - Flags D, E, and H MUST NOT be combined with each other.
+        * - Flag N MUST NOT be combined with the O flag.
+        * - Flag R MUST NOT be combined with the S flag.
+        * - Flags B, Q, U, V, and W MUST NOT be combined with each other.
+        * - Flag K MUST NOT be combined with any of the flags: B, C, D, E, or H.
+        * - Flag P MUST NOT be set when the SiteName parameter is provided.
+        * The server MUST return ERROR_INVALID_FLAGS for any of the previously
+        * mentioned conflicting combinations.
+        * ...
+        */
+
+       valid_flags = DSGETDC_VALID_FLAGS;
+
+       if (r->in.flags & ~valid_flags) {
+               /*
+                * TODO: add tests to prove this (maybe based on the
+                * msDS-Behavior-Version levels of dc, domain and/or forest
+                */
                return WERR_INVALID_FLAGS;
        }
 
-       if (r->in.flags & DS_GC_SERVER_REQUIRED &&
-           r->in.flags & DS_PDC_REQUIRED &&
-           r->in.flags & DS_KDC_REQUIRED) {
+       /* Flags D, E, and H MUST NOT be combined with each other. */
+#define _DEH (DS_GC_SERVER_REQUIRED|DS_PDC_REQUIRED|DS_KDC_REQUIRED)
+       if (multiple_bits_set(r->in.flags & _DEH)) {
                return WERR_INVALID_FLAGS;
        }
+
+       /* Flag N MUST NOT be combined with the O flag. */
        if (r->in.flags & DS_IS_FLAT_NAME &&
            r->in.flags & DS_IS_DNS_NAME) {
                return WERR_INVALID_FLAGS;
        }
+
+       /* Flag R MUST NOT be combined with the S flag. */
        if (r->in.flags & DS_RETURN_DNS_NAME &&
            r->in.flags & DS_RETURN_FLAT_NAME) {
                return WERR_INVALID_FLAGS;
        }
-       if (r->in.flags & DS_DIRECTORY_SERVICE_REQUIRED &&
-           r->in.flags & DS_DIRECTORY_SERVICE_6_REQUIRED) {
+
+       /* Flags B, Q, U, V, and W MUST NOT be combined with each other */
+#define _BQUVW ( \
+       DS_DIRECTORY_SERVICE_REQUIRED | \
+       DS_DIRECTORY_SERVICE_6_REQUIRED | \
+       DS_DIRECTORY_SERVICE_8_REQUIRED | \
+       DS_DIRECTORY_SERVICE_9_REQUIRED | \
+       DS_DIRECTORY_SERVICE_10_REQUIRED | \
+0)
+       if (multiple_bits_set(r->in.flags & _BQUVW)) {
                return WERR_INVALID_FLAGS;
        }
 
+       /*
+        * Flag K MUST NOT be combined with any of the flags:
+        * B, C, D, E, or H.
+        */
        if (r->in.flags & DS_GOOD_TIMESERV_PREFERRED &&
            r->in.flags &
            (DS_DIRECTORY_SERVICE_REQUIRED |
@@ -2954,6 +3335,7 @@ static WERROR dcesrv_netr_DsRGetDCName_base_call(struct dcesrv_netr_DsRGetDCName
                return WERR_INVALID_FLAGS;
        }
 
+       /* Flag P MUST NOT be set when the SiteName parameter is provided. */
        if (r->in.flags & DS_TRY_NEXTCLOSEST_SITE &&
            r->in.site_name) {
                return WERR_INVALID_FLAGS;
@@ -2995,12 +3377,44 @@ static WERROR dcesrv_netr_DsRGetDCName_base_call(struct dcesrv_netr_DsRGetDCName
                different_domain = false;
        }
 
+       if (!different_domain) {
+               dc_level = dsdb_dc_functional_level(sam_ctx);
+
+               /*
+                * Do not return a local response if we do not support the
+                * functional level or feature (eg web services)
+                */
+               this_dc_valid_flags = valid_flags;
+
+               /* Samba does not implement this */
+               this_dc_valid_flags &= ~DS_WEB_SERVICE_REQUIRED;
+
+               if (dc_level < DS_DOMAIN_FUNCTION_2012) {
+                       this_dc_valid_flags &= ~DS_DIRECTORY_SERVICE_8_REQUIRED;
+               }
+               if (dc_level < DS_DOMAIN_FUNCTION_2012_R2) {
+                       this_dc_valid_flags &= ~DS_DIRECTORY_SERVICE_9_REQUIRED;
+               }
+               if (dc_level < DS_DOMAIN_FUNCTION_2016) {
+                       this_dc_valid_flags &= ~DS_DIRECTORY_SERVICE_10_REQUIRED;
+               }
+               if (r->in.flags & ~this_dc_valid_flags) {
+                       DBG_INFO("Forcing remote lookup to find another DC "
+                                "in this domain %s with more features, "
+                                "as this Samba DC is Functional level %d but flags are 0x08%x\n",
+                                r->in.domain_name, dc_level, (unsigned int)r->in.flags);
+                       force_remote_lookup = true;
+               }
+       }
+
        /* Proof server site parameter "site_name" if it was specified */
        server_site_name = samdb_server_site_name(sam_ctx, state);
        W_ERROR_HAVE_NO_MEMORY(server_site_name);
-       if (different_domain || (r->in.site_name != NULL &&
-                                (strcasecmp_m(r->in.site_name,
-                                            server_site_name) != 0))) {
+       if (force_remote_lookup
+           || different_domain
+           || (r->in.site_name != NULL &&
+               (strcasecmp_m(r->in.site_name,
+                             server_site_name) != 0))) {
 
                struct dcerpc_binding_handle *irpc_handle = NULL;
                struct tevent_req *subreq = NULL;
@@ -3022,7 +3436,7 @@ static WERROR dcesrv_netr_DsRGetDCName_base_call(struct dcesrv_netr_DsRGetDCName
                                                            false);
 
                irpc_handle = irpc_binding_handle_by_name(state,
-                                                         dce_call->msg_ctx,
+                                                         imsg_ctx,
                                                          "winbind_server",
                                                          &ndr_table_winbind);
                if (irpc_handle == NULL) {
@@ -3358,11 +3772,8 @@ static WERROR dcesrv_netr_NetrEnumerateTrustedDomainsEx(struct dcesrv_call_state
 static WERROR dcesrv_netr_DsRAddressToSitenamesExW(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
                                                   struct netr_DsRAddressToSitenamesExW *r)
 {
-       struct auth_session_info *session_info =
-               dcesrv_call_session_info(dce_call);
        struct ldb_context *sam_ctx;
        struct netr_DsRAddressToSitenamesExWCtr *ctr;
-       struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
        sa_family_t sin_family;
        struct sockaddr_in *addr;
 #ifdef HAVE_IPV6
@@ -3375,12 +3786,7 @@ static WERROR dcesrv_netr_DsRAddressToSitenamesExW(struct dcesrv_call_state *dce
        const char *res;
        uint32_t i;
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               lp_ctx,
-                               session_info,
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return WERR_DS_UNAVAILABLE;
        }
@@ -3492,18 +3898,10 @@ static WERROR dcesrv_netr_DsRAddressToSitenamesW(struct dcesrv_call_state *dce_c
 static WERROR dcesrv_netr_DsrGetDcSiteCoverageW(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
                       struct netr_DsrGetDcSiteCoverageW *r)
 {
-       struct auth_session_info *session_info =
-               dcesrv_call_session_info(dce_call);
        struct ldb_context *sam_ctx;
        struct DcSitesCtr *ctr;
-       struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               lp_ctx,
-                               session_info,
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return WERR_DS_UNAVAILABLE;
        }
@@ -3543,11 +3941,9 @@ static WERROR fill_trusted_domains_array(TALLOC_CTX *mem_ctx,
                return WERR_INVALID_FLAGS;
        }
 
-       system_dn = samdb_search_dn(sam_ctx, mem_ctx,
-                                   ldb_get_default_basedn(sam_ctx),
-                                   "(&(objectClass=container)(cn=System))");
-       if (!system_dn) {
-               return WERR_GEN_FAILURE;
+       system_dn = samdb_system_container_dn(sam_ctx, mem_ctx);
+       if (system_dn == NULL) {
+               return WERR_NOT_ENOUGH_MEMORY;
        }
 
        ret = gendb_search(sam_ctx, mem_ctx, system_dn,
@@ -3629,8 +4025,6 @@ static WERROR dcesrv_netr_DsrEnumerateDomainTrusts(struct dcesrv_call_state *dce
                                                   TALLOC_CTX *mem_ctx,
                                                   struct netr_DsrEnumerateDomainTrusts *r)
 {
-       struct auth_session_info *session_info =
-               dcesrv_call_session_info(dce_call);
        struct netr_DomainTrustList *trusts;
        struct ldb_context *sam_ctx;
        int ret;
@@ -3672,12 +4066,7 @@ static WERROR dcesrv_netr_DsrEnumerateDomainTrusts(struct dcesrv_call_state *dce
        trusts->count = 0;
        r->out.trusts = trusts;
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               lp_ctx,
-                               session_info,
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return WERR_GEN_FAILURE;
        }
@@ -3787,9 +4176,10 @@ static WERROR dcesrv_netr_DsRGetForestTrustInformation(struct dcesrv_call_state
                                                       TALLOC_CTX *mem_ctx,
                                                       struct netr_DsRGetForestTrustInformation *r)
 {
-       struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
        struct auth_session_info *session_info =
                dcesrv_call_session_info(dce_call);
+       struct imessaging_context *imsg_ctx =
+               dcesrv_imessaging_context(dce_call->conn);
        enum security_user_level security_level;
        struct ldb_context *sam_ctx = NULL;
        struct dcesrv_netr_DsRGetForestTrustInformation_state *state = NULL;
@@ -3809,12 +4199,7 @@ static WERROR dcesrv_netr_DsRGetForestTrustInformation(struct dcesrv_call_state
                return WERR_INVALID_FLAGS;
        }
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               lp_ctx,
-                               session_info,
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return WERR_GEN_FAILURE;
        }
@@ -3878,7 +4263,7 @@ static WERROR dcesrv_netr_DsRGetForestTrustInformation(struct dcesrv_call_state
        state->r = r;
 
        irpc_handle = irpc_binding_handle_by_name(state,
-                                                 state->dce_call->msg_ctx,
+                                                 imsg_ctx,
                                                  "winbind_server",
                                                  &ndr_table_winbind);
        if (irpc_handle == NULL) {
@@ -3941,9 +4326,6 @@ static NTSTATUS dcesrv_netr_GetForestTrustInformation(struct dcesrv_call_state *
                                                      TALLOC_CTX *mem_ctx,
                                                      struct netr_GetForestTrustInformation *r)
 {
-       struct auth_session_info *session_info =
-               dcesrv_call_session_info(dce_call);
-       struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
        struct netlogon_creds_CredentialState *creds = NULL;
        struct ldb_context *sam_ctx = NULL;
        struct ldb_dn *domain_dn = NULL;
@@ -3967,12 +4349,7 @@ static NTSTATUS dcesrv_netr_GetForestTrustInformation(struct dcesrv_call_state *
                return NT_STATUS_NOT_IMPLEMENTED;
        }
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               lp_ctx,
-                               session_info,
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return NT_STATUS_INTERNAL_ERROR;
        }
@@ -4066,12 +4443,7 @@ static NTSTATUS dcesrv_netr_ServerGetTrustInfo(struct dcesrv_call_state *dce_cal
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       sam_ctx = samdb_connect(mem_ctx,
-                               dce_call->event_ctx,
-                               lp_ctx,
-                               system_session(lp_ctx),
-                               dce_call->conn->remote_address,
-                               0);
+       sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
@@ -4143,7 +4515,7 @@ static NTSTATUS dcesrv_netr_ServerGetTrustInfo(struct dcesrv_call_state *dce_cal
        default:
                nt_status = samdb_result_passwords_no_lockout(mem_ctx, lp_ctx,
                                                              res[0],
-                                                             NULL, &curNtHash);
+                                                             &curNtHash);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        return nt_status;
                }
@@ -4159,11 +4531,17 @@ static NTSTATUS dcesrv_netr_ServerGetTrustInfo(struct dcesrv_call_state *dce_cal
 
        if (curNtHash != NULL) {
                *r->out.new_owf_password = *curNtHash;
-               netlogon_creds_des_encrypt(creds, r->out.new_owf_password);
+               nt_status = netlogon_creds_des_encrypt(creds, r->out.new_owf_password);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       return nt_status;
+               }
        }
        if (prevNtHash != NULL) {
                *r->out.old_owf_password = *prevNtHash;
-               netlogon_creds_des_encrypt(creds, r->out.old_owf_password);
+               nt_status = netlogon_creds_des_encrypt(creds, r->out.old_owf_password);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       return nt_status;
+               }
        }
 
        if (trust_info != NULL) {
@@ -4226,6 +4604,8 @@ static NTSTATUS dcesrv_netr_DsrUpdateReadOnlyServerDnsRecords(struct dcesrv_call
        struct dcerpc_binding_handle *binding_handle;
        struct netr_dnsupdate_RODC_state *st;
        struct tevent_req *subreq;
+       struct imessaging_context *imsg_ctx =
+               dcesrv_imessaging_context(dce_call->conn);
 
        nt_status = dcesrv_netr_creds_server_step_check(dce_call,
                                                        mem_ctx,
@@ -4253,8 +4633,10 @@ static NTSTATUS dcesrv_netr_DsrUpdateReadOnlyServerDnsRecords(struct dcesrv_call
        st->r2->in.dns_names = r->in.dns_names;
        st->r2->out.dns_names = r->out.dns_names;
 
-       binding_handle = irpc_binding_handle_by_name(st, dce_call->msg_ctx,
-                                                    "dnsupdate", &ndr_table_irpc);
+       binding_handle = irpc_binding_handle_by_name(st,
+                                                    imsg_ctx,
+                                                    "dnsupdate",
+                                                    &ndr_table_irpc);
        if (binding_handle == NULL) {
                DEBUG(0,("Failed to get binding_handle for dnsupdate task\n"));
                dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;