libcli/auth: pass the cleartext blob to netlogon_creds_cli_ServerPasswordSet*()
[gd/samba-autobuild/.git] / libcli / auth / netlogon_creds_cli.c
index 7e882552c94f629caaded512a228725d3c32514e..367bf6caaff2adcfb4b6c2fae973917fb3f07857 100644 (file)
 #include "../libcli/auth/schannel.h"
 #include "../librpc/gen_ndr/ndr_schannel.h"
 #include "../librpc/gen_ndr/ndr_netlogon_c.h"
+#include "../librpc/gen_ndr/ndr_netlogon.h"
 #include "../librpc/gen_ndr/server_id.h"
 #include "netlogon_creds_cli.h"
 #include "source3/include/messages.h"
 #include "source3/include/g_lock.h"
+#include "libds/common/roles.h"
+#include "lib/crypto/crypto.h"
 
 struct netlogon_creds_cli_locked_state;
 
@@ -483,6 +486,14 @@ NTSTATUS netlogon_creds_cli_context_tmp(const char *client_computer,
        return NT_STATUS_OK;
 }
 
+char *netlogon_creds_cli_debug_string(
+               const struct netlogon_creds_cli_context *context,
+               TALLOC_CTX *mem_ctx)
+{
+       return talloc_asprintf(mem_ctx, "netlogon_creds_cli:%s",
+                              context->db.key_name);
+}
+
 enum dcerpc_AuthLevel netlogon_creds_cli_auth_level(
                struct netlogon_creds_cli_context *context)
 {
@@ -933,9 +944,10 @@ struct netlogon_creds_cli_auth_state {
        struct tevent_context *ev;
        struct netlogon_creds_cli_context *context;
        struct dcerpc_binding_handle *binding_handle;
-       struct samr_Password current_nt_hash;
-       struct samr_Password previous_nt_hash;
-       struct samr_Password used_nt_hash;
+       uint8_t num_nt_hashes;
+       uint8_t idx_nt_hashes;
+       const struct samr_Password * const *nt_hashes;
+       const struct samr_Password *used_nt_hash;
        char *srv_name_slash;
        uint32_t current_flags;
        struct netr_Credential client_challenge;
@@ -947,7 +959,6 @@ struct netlogon_creds_cli_auth_state {
        bool try_auth3;
        bool try_auth2;
        bool require_auth2;
-       bool try_previous_nt_hash;
        struct netlogon_creds_cli_locked_state *locked_state;
 };
 
@@ -958,8 +969,8 @@ struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx,
                                struct tevent_context *ev,
                                struct netlogon_creds_cli_context *context,
                                struct dcerpc_binding_handle *b,
-                               struct samr_Password current_nt_hash,
-                               const struct samr_Password *previous_nt_hash)
+                               uint8_t num_nt_hashes,
+                               const struct samr_Password * const *nt_hashes)
 {
        struct tevent_req *req;
        struct netlogon_creds_cli_auth_state *state;
@@ -975,12 +986,19 @@ struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx,
        state->ev = ev;
        state->context = context;
        state->binding_handle = b;
-       state->current_nt_hash = current_nt_hash;
-       if (previous_nt_hash != NULL) {
-               state->previous_nt_hash = *previous_nt_hash;
-               state->try_previous_nt_hash = true;
+       if (num_nt_hashes < 1) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+       if (num_nt_hashes > 4) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
        }
 
+       state->num_nt_hashes = num_nt_hashes;
+       state->idx_nt_hashes = 0;
+       state->nt_hashes = nt_hashes;
+
        if (context->db.locked_state != NULL) {
                tevent_req_nterror(req, NT_STATUS_LOCK_NOT_GRANTED);
                return tevent_req_post(req, ev);
@@ -1010,7 +1028,7 @@ struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx,
                state->require_auth2 = true;
        }
 
-       state->used_nt_hash = state->current_nt_hash;
+       state->used_nt_hash = state->nt_hashes[state->idx_nt_hashes];
        state->current_flags = context->client.proposed_flags;
 
        if (context->db.g_ctx != NULL) {
@@ -1030,11 +1048,8 @@ struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx,
                return req;
        }
 
-       status = dbwrap_delete(state->context->db.ctx,
-                              state->context->db.key_data);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
-               status = NT_STATUS_OK;
-       }
+       status = dbwrap_purge(state->context->db.ctx,
+                             state->context->db.key_data);
        if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, ev);
        }
@@ -1064,11 +1079,8 @@ static void netlogon_creds_cli_auth_locked(struct tevent_req *subreq)
        }
        state->locked_state->is_glocked = true;
 
-       status = dbwrap_delete(state->context->db.ctx,
-                              state->context->db.key_data);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
-               status = NT_STATUS_OK;
-       }
+       status = dbwrap_purge(state->context->db.ctx,
+                             state->context->db.key_data);
        if (tevent_req_nterror(req, status)) {
                return;
        }
@@ -1138,7 +1150,7 @@ static void netlogon_creds_cli_auth_challenge_done(struct tevent_req *subreq)
                                                  state->context->client.type,
                                                  &state->client_challenge,
                                                  &state->server_challenge,
-                                                 &state->used_nt_hash,
+                                                 state->used_nt_hash,
                                                  &state->client_credential,
                                                  state->current_flags);
        if (tevent_req_nomem(state->creds, req)) {
@@ -1280,7 +1292,8 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq)
                        return;
                }
 
-               if (!state->try_previous_nt_hash) {
+               state->idx_nt_hashes += 1;
+               if (state->idx_nt_hashes >= state->num_nt_hashes) {
                        /*
                         * we already retried, giving up...
                         */
@@ -1291,8 +1304,7 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq)
                /*
                 * lets retry with the old nt hash.
                 */
-               state->try_previous_nt_hash = false;
-               state->used_nt_hash = state->previous_nt_hash;
+               state->used_nt_hash = state->nt_hashes[state->idx_nt_hashes];
                state->current_flags = state->context->client.proposed_flags;
                netlogon_creds_cli_auth_challenge_start(req);
                return;
@@ -1327,43 +1339,52 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq)
        tevent_req_done(req);
 }
 
-NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req)
+NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req,
+                                     uint8_t *idx_nt_hashes)
 {
+       struct netlogon_creds_cli_auth_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_auth_state);
        NTSTATUS status;
 
+       *idx_nt_hashes = 0;
+
        if (tevent_req_is_nterror(req, &status)) {
                tevent_req_received(req);
                return status;
        }
 
+       *idx_nt_hashes = state->idx_nt_hashes;
        tevent_req_received(req);
        return NT_STATUS_OK;
 }
 
 NTSTATUS netlogon_creds_cli_auth(struct netlogon_creds_cli_context *context,
                                 struct dcerpc_binding_handle *b,
-                                struct samr_Password current_nt_hash,
-                                const struct samr_Password *previous_nt_hash)
+                                uint8_t num_nt_hashes,
+                                const struct samr_Password * const *nt_hashes,
+                                uint8_t *idx_nt_hashes)
 {
        TALLOC_CTX *frame = talloc_stackframe();
        struct tevent_context *ev;
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
+       *idx_nt_hashes = 0;
+
        ev = samba_tevent_context_init(frame);
        if (ev == NULL) {
                goto fail;
        }
        req = netlogon_creds_cli_auth_send(frame, ev, context, b,
-                                          current_nt_hash,
-                                          previous_nt_hash);
+                                          num_nt_hashes, nt_hashes);
        if (req == NULL) {
                goto fail;
        }
        if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
-       status = netlogon_creds_cli_auth_recv(req);
+       status = netlogon_creds_cli_auth_recv(req, idx_nt_hashes);
  fail:
        TALLOC_FREE(frame);
        return status;
@@ -1731,7 +1752,7 @@ struct tevent_req *netlogon_creds_cli_ServerPasswordSet_send(TALLOC_CTX *mem_ctx
                                struct tevent_context *ev,
                                struct netlogon_creds_cli_context *context,
                                struct dcerpc_binding_handle *b,
-                               const char *new_password,
+                               const DATA_BLOB *new_password,
                                const uint32_t *new_version)
 {
        struct tevent_req *req;
@@ -1749,16 +1770,21 @@ struct tevent_req *netlogon_creds_cli_ServerPasswordSet_send(TALLOC_CTX *mem_ctx
        state->context = context;
        state->binding_handle = b;
 
+       if (new_password->length < 14) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return tevent_req_post(req, ev);
+       }
+
        /*
         * netr_ServerPasswordSet
         */
-       E_md4hash(new_password, state->samr_password.hash);
+       mdfour(state->samr_password.hash, new_password->data, new_password->length);
 
        /*
         * netr_ServerPasswordSet2
         */
-       ok = encode_pw_buffer(state->samr_crypt_password.data,
-                             new_password, STR_UNICODE);
+       ok = set_pw_in_buffer(state->samr_crypt_password.data,
+                             new_password);
        if (!ok) {
                tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
                return tevent_req_post(req, ev);
@@ -1766,11 +1792,11 @@ struct tevent_req *netlogon_creds_cli_ServerPasswordSet_send(TALLOC_CTX *mem_ctx
 
        if (new_version != NULL) {
                struct NL_PASSWORD_VERSION version;
-               int32_t len = IVAL(state->samr_crypt_password.data, 512);
-               int32_t ofs = 512 - len;
+               uint32_t len = IVAL(state->samr_crypt_password.data, 512);
+               uint32_t ofs = 512 - len;
                uint8_t *p;
 
-               if (ofs < 12) {
+               if (len > 500) {
                        tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
                        return tevent_req_post(req, ev);
                }
@@ -2028,7 +2054,7 @@ NTSTATUS netlogon_creds_cli_ServerPasswordSet_recv(struct tevent_req *req)
 NTSTATUS netlogon_creds_cli_ServerPasswordSet(
                                struct netlogon_creds_cli_context *context,
                                struct dcerpc_binding_handle *b,
-                               const char *new_password,
+                               const DATA_BLOB *new_password,
                                const uint32_t *new_version)
 {
        TALLOC_CTX *frame = talloc_stackframe();
@@ -2080,11 +2106,24 @@ struct netlogon_creds_cli_LogonSamLogon_state {
 
        /*
         * the read only credentials before we started the operation
+        * used for netr_LogonSamLogonEx() if required (validation_level = 3).
         */
        struct netlogon_creds_CredentialState *ro_creds;
 
+       /*
+        * The (locked) credentials used for the credential chain
+        * used for netr_LogonSamLogonWithFlags() or
+        * netr_LogonSamLogonWith().
+        */
        struct netlogon_creds_CredentialState *lk_creds;
 
+       /*
+        * While we have locked the global credentials (lk_creds above)
+        * we operate an a temporary copy, because a server
+        * may not support netr_LogonSamLogonWithFlags() and
+        * didn't process our netr_Authenticator, so we need to
+        * restart from lk_creds.
+        */
        struct netlogon_creds_CredentialState tmp_creds;
        struct netr_Authenticator req_auth;
        struct netr_Authenticator rep_auth;
@@ -2316,7 +2355,7 @@ static void netlogon_creds_cli_LogonSamLogon_start(struct tevent_req *req)
                return;
        }
 
-       netlogon_creds_encrypt_samlogon_logon(state->ro_creds,
+       netlogon_creds_encrypt_samlogon_logon(&state->tmp_creds,
                                              state->logon_level,
                                              state->logon);
 
@@ -2419,8 +2458,10 @@ static void netlogon_creds_cli_LogonSamLogon_done(struct tevent_req *subreq)
                        /*
                         * We got a race, lets retry with on authenticator
                         * protection.
+                        *
+                        * netlogon_creds_cli_LogonSamLogon_start()
+                        * will TALLOC_FREE(state->ro_creds);
                         */
-                       TALLOC_FREE(state->ro_creds);
                        state->try_logon_ex = false;
                        netlogon_creds_cli_LogonSamLogon_start(req);
                        return;
@@ -2568,3 +2609,1087 @@ NTSTATUS netlogon_creds_cli_LogonSamLogon(
        TALLOC_FREE(frame);
        return status;
 }
+
+struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state {
+       struct tevent_context *ev;
+       struct netlogon_creds_cli_context *context;
+       struct dcerpc_binding_handle *binding_handle;
+
+       char *srv_name_slash;
+       enum dcerpc_AuthType auth_type;
+       enum dcerpc_AuthLevel auth_level;
+
+       const char *site_name;
+       uint32_t dns_ttl;
+       struct NL_DNS_NAME_INFO_ARRAY *dns_names;
+
+       struct netlogon_creds_CredentialState *creds;
+       struct netlogon_creds_CredentialState tmp_creds;
+       struct netr_Authenticator req_auth;
+       struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(struct tevent_req *req,
+                                                    NTSTATUS status);
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_send(TALLOC_CTX *mem_ctx,
+                                                                            struct tevent_context *ev,
+                                                                            struct netlogon_creds_cli_context *context,
+                                                                            struct dcerpc_binding_handle *b,
+                                                                            const char *site_name,
+                                                                            uint32_t dns_ttl,
+                                                                            struct NL_DNS_NAME_INFO_ARRAY *dns_names)
+{
+       struct tevent_req *req;
+       struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state;
+       struct tevent_req *subreq;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->context = context;
+       state->binding_handle = b;
+
+       state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+                                               context->server.computer);
+       if (tevent_req_nomem(state->srv_name_slash, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->site_name = site_name;
+       state->dns_ttl = dns_ttl;
+       state->dns_names = dns_names;
+
+       dcerpc_binding_handle_auth_info(state->binding_handle,
+                                       &state->auth_type,
+                                       &state->auth_level);
+
+       subreq = netlogon_creds_cli_lock_send(state, state->ev,
+                                             state->context);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       tevent_req_set_callback(subreq,
+                               netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked,
+                               req);
+
+       return req;
+}
+
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(struct tevent_req *req,
+                                                        NTSTATUS status)
+{
+       struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state);
+
+       if (state->creds == NULL) {
+               return;
+       }
+
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+               TALLOC_FREE(state->creds);
+               return;
+       }
+
+       netlogon_creds_cli_delete(state->context, &state->creds);
+}
+
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state);
+       NTSTATUS status;
+
+       status = netlogon_creds_cli_lock_recv(subreq, state,
+                                             &state->creds);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+               switch (state->auth_level) {
+               case DCERPC_AUTH_LEVEL_INTEGRITY:
+               case DCERPC_AUTH_LEVEL_PRIVACY:
+                       break;
+               default:
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return;
+               }
+       } else {
+               uint32_t tmp = state->creds->negotiate_flags;
+
+               if (tmp & NETLOGON_NEG_AUTHENTICATED_RPC) {
+                       /*
+                        * if DCERPC_AUTH_TYPE_SCHANNEL is supported
+                        * it should be used, which means
+                        * we had a chance to verify no downgrade
+                        * happened.
+                        *
+                        * This relies on netlogon_creds_cli_check*
+                        * being called before, as first request after
+                        * the DCERPC bind.
+                        */
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return;
+               }
+       }
+
+       /*
+        * we defer all callbacks in order to cleanup
+        * the database record.
+        */
+       tevent_req_defer_callback(req, state->ev);
+
+       state->tmp_creds = *state->creds;
+       netlogon_creds_client_authenticator(&state->tmp_creds,
+                                           &state->req_auth);
+       ZERO_STRUCT(state->rep_auth);
+
+       subreq = dcerpc_netr_DsrUpdateReadOnlyServerDnsRecords_send(state, state->ev,
+                                                                   state->binding_handle,
+                                                                   state->srv_name_slash,
+                                                                   state->tmp_creds.computer_name,
+                                                                   &state->req_auth,
+                                                                   &state->rep_auth,
+                                                                   state->site_name,
+                                                                   state->dns_ttl,
+                                                                   state->dns_names);
+       if (tevent_req_nomem(subreq, req)) {
+               status = NT_STATUS_NO_MEMORY;
+               netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status);
+               return;
+       }
+
+       tevent_req_set_callback(subreq,
+                               netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done,
+                               req);
+}
+
+static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state);
+       NTSTATUS status;
+       NTSTATUS result;
+       bool ok;
+
+       /*
+        * We use state->dns_names as the memory context, as this is
+        * the only in/out variable and it has been overwritten by the
+        * out parameter from the server.
+        *
+        * We need to preserve the return value until the caller can use it.
+        */
+       status = dcerpc_netr_DsrUpdateReadOnlyServerDnsRecords_recv(subreq, state->dns_names,
+                                                                   &result);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status);
+               return;
+       }
+
+       ok = netlogon_creds_client_check(&state->tmp_creds,
+                                        &state->rep_auth.cred);
+       if (!ok) {
+               status = NT_STATUS_ACCESS_DENIED;
+               tevent_req_nterror(req, status);
+               netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status);
+               return;
+       }
+
+       *state->creds = state->tmp_creds;
+       status = netlogon_creds_cli_store(state->context,
+                                         &state->creds);
+
+       if (tevent_req_nterror(req, status)) {
+               netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status);
+               return;
+       }
+
+       if (tevent_req_nterror(req, result)) {
+               netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, result);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_recv(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup(req, status);
+               tevent_req_received(req);
+               return status;
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords(
+                               struct netlogon_creds_cli_context *context,
+                               struct dcerpc_binding_handle *b,
+                               const char *site_name,
+                               uint32_t dns_ttl,
+                               struct NL_DNS_NAME_INFO_ARRAY *dns_names)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_send(frame, ev, context, b,
+                                                                       site_name,
+                                                                       dns_ttl,
+                                                                       dns_names);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_recv(req);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+struct netlogon_creds_cli_ServerGetTrustInfo_state {
+       struct tevent_context *ev;
+       struct netlogon_creds_cli_context *context;
+       struct dcerpc_binding_handle *binding_handle;
+
+       char *srv_name_slash;
+       enum dcerpc_AuthType auth_type;
+       enum dcerpc_AuthLevel auth_level;
+
+       struct samr_Password new_owf_password;
+       struct samr_Password old_owf_password;
+       struct netr_TrustInfo *trust_info;
+
+       struct netlogon_creds_CredentialState *creds;
+       struct netlogon_creds_CredentialState tmp_creds;
+       struct netr_Authenticator req_auth;
+       struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_ServerGetTrustInfo_cleanup(struct tevent_req *req,
+                                                    NTSTATUS status);
+static void netlogon_creds_cli_ServerGetTrustInfo_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_ServerGetTrustInfo_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct netlogon_creds_cli_context *context,
+                                       struct dcerpc_binding_handle *b)
+{
+       struct tevent_req *req;
+       struct netlogon_creds_cli_ServerGetTrustInfo_state *state;
+       struct tevent_req *subreq;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct netlogon_creds_cli_ServerGetTrustInfo_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->context = context;
+       state->binding_handle = b;
+
+       state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+                                               context->server.computer);
+       if (tevent_req_nomem(state->srv_name_slash, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       dcerpc_binding_handle_auth_info(state->binding_handle,
+                                       &state->auth_type,
+                                       &state->auth_level);
+
+       subreq = netlogon_creds_cli_lock_send(state, state->ev,
+                                             state->context);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       tevent_req_set_callback(subreq,
+                               netlogon_creds_cli_ServerGetTrustInfo_locked,
+                               req);
+
+       return req;
+}
+
+static void netlogon_creds_cli_ServerGetTrustInfo_cleanup(struct tevent_req *req,
+                                                        NTSTATUS status)
+{
+       struct netlogon_creds_cli_ServerGetTrustInfo_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_ServerGetTrustInfo_state);
+
+       if (state->creds == NULL) {
+               return;
+       }
+
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+               TALLOC_FREE(state->creds);
+               return;
+       }
+
+       netlogon_creds_cli_delete(state->context, &state->creds);
+}
+
+static void netlogon_creds_cli_ServerGetTrustInfo_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_ServerGetTrustInfo_locked(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct netlogon_creds_cli_ServerGetTrustInfo_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_ServerGetTrustInfo_state);
+       NTSTATUS status;
+
+       status = netlogon_creds_cli_lock_recv(subreq, state,
+                                             &state->creds);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+               switch (state->auth_level) {
+               case DCERPC_AUTH_LEVEL_PRIVACY:
+                       break;
+               default:
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return;
+               }
+       } else {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+               return;
+       }
+
+       /*
+        * we defer all callbacks in order to cleanup
+        * the database record.
+        */
+       tevent_req_defer_callback(req, state->ev);
+
+       state->tmp_creds = *state->creds;
+       netlogon_creds_client_authenticator(&state->tmp_creds,
+                                           &state->req_auth);
+       ZERO_STRUCT(state->rep_auth);
+
+       subreq = dcerpc_netr_ServerGetTrustInfo_send(state, state->ev,
+                                                    state->binding_handle,
+                                                    state->srv_name_slash,
+                                                    state->tmp_creds.account_name,
+                                                    state->tmp_creds.secure_channel_type,
+                                                    state->tmp_creds.computer_name,
+                                                    &state->req_auth,
+                                                    &state->rep_auth,
+                                                    &state->new_owf_password,
+                                                    &state->old_owf_password,
+                                                    &state->trust_info);
+       if (tevent_req_nomem(subreq, req)) {
+               status = NT_STATUS_NO_MEMORY;
+               netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+               return;
+       }
+
+       tevent_req_set_callback(subreq,
+                               netlogon_creds_cli_ServerGetTrustInfo_done,
+                               req);
+}
+
+static void netlogon_creds_cli_ServerGetTrustInfo_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct netlogon_creds_cli_ServerGetTrustInfo_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_ServerGetTrustInfo_state);
+       NTSTATUS status;
+       NTSTATUS result;
+       const struct samr_Password zero = {};
+       int cmp;
+       bool ok;
+
+       /*
+        * We use state->dns_names as the memory context, as this is
+        * the only in/out variable and it has been overwritten by the
+        * out parameter from the server.
+        *
+        * We need to preserve the return value until the caller can use it.
+        */
+       status = dcerpc_netr_ServerGetTrustInfo_recv(subreq, state, &result);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+               return;
+       }
+
+       ok = netlogon_creds_client_check(&state->tmp_creds,
+                                        &state->rep_auth.cred);
+       if (!ok) {
+               status = NT_STATUS_ACCESS_DENIED;
+               tevent_req_nterror(req, status);
+               netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+               return;
+       }
+
+       cmp = memcmp(state->new_owf_password.hash,
+                    zero.hash, sizeof(zero.hash));
+       if (cmp != 0) {
+               netlogon_creds_des_decrypt(&state->tmp_creds,
+                                          &state->new_owf_password);
+       }
+       cmp = memcmp(state->old_owf_password.hash,
+                    zero.hash, sizeof(zero.hash));
+       if (cmp != 0) {
+               netlogon_creds_des_decrypt(&state->tmp_creds,
+                                          &state->old_owf_password);
+       }
+
+       *state->creds = state->tmp_creds;
+       status = netlogon_creds_cli_store(state->context,
+                                         &state->creds);
+       if (tevent_req_nterror(req, status)) {
+               netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+               return;
+       }
+
+       if (tevent_req_nterror(req, result)) {
+               netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, result);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_ServerGetTrustInfo_recv(struct tevent_req *req,
+                                       TALLOC_CTX *mem_ctx,
+                                       struct samr_Password *new_owf_password,
+                                       struct samr_Password *old_owf_password,
+                                       struct netr_TrustInfo **trust_info)
+{
+       struct netlogon_creds_cli_ServerGetTrustInfo_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_ServerGetTrustInfo_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               netlogon_creds_cli_ServerGetTrustInfo_cleanup(req, status);
+               tevent_req_received(req);
+               return status;
+       }
+
+       if (new_owf_password != NULL) {
+               *new_owf_password = state->new_owf_password;
+       }
+       if (old_owf_password != NULL) {
+               *old_owf_password = state->old_owf_password;
+       }
+       if (trust_info != NULL) {
+               *trust_info = talloc_move(mem_ctx, &state->trust_info);
+       }
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_ServerGetTrustInfo(
+                               struct netlogon_creds_cli_context *context,
+                               struct dcerpc_binding_handle *b,
+                               TALLOC_CTX *mem_ctx,
+                               struct samr_Password *new_owf_password,
+                               struct samr_Password *old_owf_password,
+                               struct netr_TrustInfo **trust_info)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = netlogon_creds_cli_ServerGetTrustInfo_send(frame, ev, context, b);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = netlogon_creds_cli_ServerGetTrustInfo_recv(req,
+                                                           mem_ctx,
+                                                           new_owf_password,
+                                                           old_owf_password,
+                                                           trust_info);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+struct netlogon_creds_cli_GetForestTrustInformation_state {
+       struct tevent_context *ev;
+       struct netlogon_creds_cli_context *context;
+       struct dcerpc_binding_handle *binding_handle;
+
+       char *srv_name_slash;
+       enum dcerpc_AuthType auth_type;
+       enum dcerpc_AuthLevel auth_level;
+
+       uint32_t flags;
+       struct lsa_ForestTrustInformation *forest_trust_info;
+
+       struct netlogon_creds_CredentialState *creds;
+       struct netlogon_creds_CredentialState tmp_creds;
+       struct netr_Authenticator req_auth;
+       struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_GetForestTrustInformation_cleanup(struct tevent_req *req,
+                                                    NTSTATUS status);
+static void netlogon_creds_cli_GetForestTrustInformation_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_GetForestTrustInformation_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct netlogon_creds_cli_context *context,
+                                       struct dcerpc_binding_handle *b)
+{
+       struct tevent_req *req;
+       struct netlogon_creds_cli_GetForestTrustInformation_state *state;
+       struct tevent_req *subreq;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct netlogon_creds_cli_GetForestTrustInformation_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->context = context;
+       state->binding_handle = b;
+
+       state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+                                               context->server.computer);
+       if (tevent_req_nomem(state->srv_name_slash, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->flags = 0;
+
+       dcerpc_binding_handle_auth_info(state->binding_handle,
+                                       &state->auth_type,
+                                       &state->auth_level);
+
+       subreq = netlogon_creds_cli_lock_send(state, state->ev,
+                                             state->context);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       tevent_req_set_callback(subreq,
+                               netlogon_creds_cli_GetForestTrustInformation_locked,
+                               req);
+
+       return req;
+}
+
+static void netlogon_creds_cli_GetForestTrustInformation_cleanup(struct tevent_req *req,
+                                                        NTSTATUS status)
+{
+       struct netlogon_creds_cli_GetForestTrustInformation_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_GetForestTrustInformation_state);
+
+       if (state->creds == NULL) {
+               return;
+       }
+
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+               TALLOC_FREE(state->creds);
+               return;
+       }
+
+       netlogon_creds_cli_delete(state->context, &state->creds);
+}
+
+static void netlogon_creds_cli_GetForestTrustInformation_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_GetForestTrustInformation_locked(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct netlogon_creds_cli_GetForestTrustInformation_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_GetForestTrustInformation_state);
+       NTSTATUS status;
+
+       status = netlogon_creds_cli_lock_recv(subreq, state,
+                                             &state->creds);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+               switch (state->auth_level) {
+               case DCERPC_AUTH_LEVEL_INTEGRITY:
+               case DCERPC_AUTH_LEVEL_PRIVACY:
+                       break;
+               default:
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return;
+               }
+       } else {
+               uint32_t tmp = state->creds->negotiate_flags;
+
+               if (tmp & NETLOGON_NEG_AUTHENTICATED_RPC) {
+                       /*
+                        * if DCERPC_AUTH_TYPE_SCHANNEL is supported
+                        * it should be used, which means
+                        * we had a chance to verify no downgrade
+                        * happened.
+                        *
+                        * This relies on netlogon_creds_cli_check*
+                        * being called before, as first request after
+                        * the DCERPC bind.
+                        */
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return;
+               }
+       }
+
+       /*
+        * we defer all callbacks in order to cleanup
+        * the database record.
+        */
+       tevent_req_defer_callback(req, state->ev);
+
+       state->tmp_creds = *state->creds;
+       netlogon_creds_client_authenticator(&state->tmp_creds,
+                                           &state->req_auth);
+       ZERO_STRUCT(state->rep_auth);
+
+       subreq = dcerpc_netr_GetForestTrustInformation_send(state, state->ev,
+                                               state->binding_handle,
+                                               state->srv_name_slash,
+                                               state->tmp_creds.computer_name,
+                                               &state->req_auth,
+                                               &state->rep_auth,
+                                               state->flags,
+                                               &state->forest_trust_info);
+       if (tevent_req_nomem(subreq, req)) {
+               status = NT_STATUS_NO_MEMORY;
+               netlogon_creds_cli_GetForestTrustInformation_cleanup(req, status);
+               return;
+       }
+
+       tevent_req_set_callback(subreq,
+                               netlogon_creds_cli_GetForestTrustInformation_done,
+                               req);
+}
+
+static void netlogon_creds_cli_GetForestTrustInformation_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct netlogon_creds_cli_GetForestTrustInformation_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_GetForestTrustInformation_state);
+       NTSTATUS status;
+       NTSTATUS result;
+       bool ok;
+
+       /*
+        * We use state->dns_names as the memory context, as this is
+        * the only in/out variable and it has been overwritten by the
+        * out parameter from the server.
+        *
+        * We need to preserve the return value until the caller can use it.
+        */
+       status = dcerpc_netr_GetForestTrustInformation_recv(subreq, state, &result);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               netlogon_creds_cli_GetForestTrustInformation_cleanup(req, status);
+               return;
+       }
+
+       ok = netlogon_creds_client_check(&state->tmp_creds,
+                                        &state->rep_auth.cred);
+       if (!ok) {
+               status = NT_STATUS_ACCESS_DENIED;
+               tevent_req_nterror(req, status);
+               netlogon_creds_cli_GetForestTrustInformation_cleanup(req, status);
+               return;
+       }
+
+       *state->creds = state->tmp_creds;
+       status = netlogon_creds_cli_store(state->context,
+                                         &state->creds);
+
+       if (tevent_req_nterror(req, status)) {
+               netlogon_creds_cli_GetForestTrustInformation_cleanup(req, status);
+               return;
+       }
+
+       if (tevent_req_nterror(req, result)) {
+               netlogon_creds_cli_GetForestTrustInformation_cleanup(req, result);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_GetForestTrustInformation_recv(struct tevent_req *req,
+                       TALLOC_CTX *mem_ctx,
+                       struct lsa_ForestTrustInformation **forest_trust_info)
+{
+       struct netlogon_creds_cli_GetForestTrustInformation_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_GetForestTrustInformation_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               netlogon_creds_cli_GetForestTrustInformation_cleanup(req, status);
+               tevent_req_received(req);
+               return status;
+       }
+
+       *forest_trust_info = talloc_move(mem_ctx, &state->forest_trust_info);
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_GetForestTrustInformation(
+                       struct netlogon_creds_cli_context *context,
+                       struct dcerpc_binding_handle *b,
+                       TALLOC_CTX *mem_ctx,
+                       struct lsa_ForestTrustInformation **forest_trust_info)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = netlogon_creds_cli_GetForestTrustInformation_send(frame, ev, context, b);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = netlogon_creds_cli_GetForestTrustInformation_recv(req,
+                                                       mem_ctx,
+                                                       forest_trust_info);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+struct netlogon_creds_cli_SendToSam_state {
+       struct tevent_context *ev;
+       struct netlogon_creds_cli_context *context;
+       struct dcerpc_binding_handle *binding_handle;
+
+       char *srv_name_slash;
+       enum dcerpc_AuthType auth_type;
+       enum dcerpc_AuthLevel auth_level;
+
+       DATA_BLOB opaque;
+
+       struct netlogon_creds_CredentialState *creds;
+       struct netlogon_creds_CredentialState tmp_creds;
+       struct netr_Authenticator req_auth;
+       struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_SendToSam_cleanup(struct tevent_req *req,
+                                                                NTSTATUS status);
+static void netlogon_creds_cli_SendToSam_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_SendToSam_send(TALLOC_CTX *mem_ctx,
+                                                    struct tevent_context *ev,
+                                                    struct netlogon_creds_cli_context *context,
+                                                    struct dcerpc_binding_handle *b,
+                                                    struct netr_SendToSamBase *message)
+{
+       struct tevent_req *req;
+       struct netlogon_creds_cli_SendToSam_state *state;
+       struct tevent_req *subreq;
+       enum ndr_err_code ndr_err;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct netlogon_creds_cli_SendToSam_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->context = context;
+       state->binding_handle = b;
+
+       state->srv_name_slash = talloc_asprintf(state, "\\\\%s",
+                                               context->server.computer);
+       if (tevent_req_nomem(state->srv_name_slash, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       ndr_err = ndr_push_struct_blob(&state->opaque, mem_ctx, message,
+                                      (ndr_push_flags_fn_t)ndr_push_netr_SendToSamBase);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+       dcerpc_binding_handle_auth_info(state->binding_handle,
+                                       &state->auth_type,
+                                       &state->auth_level);
+
+       subreq = netlogon_creds_cli_lock_send(state, state->ev,
+                                             state->context);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       tevent_req_set_callback(subreq,
+                               netlogon_creds_cli_SendToSam_locked,
+                               req);
+
+       return req;
+}
+
+static void netlogon_creds_cli_SendToSam_cleanup(struct tevent_req *req,
+                                                        NTSTATUS status)
+{
+       struct netlogon_creds_cli_SendToSam_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_SendToSam_state);
+
+       if (state->creds == NULL) {
+               return;
+       }
+
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR)) {
+               TALLOC_FREE(state->creds);
+               return;
+       }
+
+       netlogon_creds_cli_delete(state->context, &state->creds);
+}
+
+static void netlogon_creds_cli_SendToSam_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_SendToSam_locked(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct netlogon_creds_cli_SendToSam_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_SendToSam_state);
+       NTSTATUS status;
+
+       status = netlogon_creds_cli_lock_recv(subreq, state,
+                                             &state->creds);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (state->auth_type == DCERPC_AUTH_TYPE_SCHANNEL) {
+               switch (state->auth_level) {
+               case DCERPC_AUTH_LEVEL_INTEGRITY:
+               case DCERPC_AUTH_LEVEL_PRIVACY:
+                       break;
+               default:
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return;
+               }
+       } else {
+               uint32_t tmp = state->creds->negotiate_flags;
+
+               if (tmp & NETLOGON_NEG_AUTHENTICATED_RPC) {
+                       /*
+                        * if DCERPC_AUTH_TYPE_SCHANNEL is supported
+                        * it should be used, which means
+                        * we had a chance to verify no downgrade
+                        * happened.
+                        *
+                        * This relies on netlogon_creds_cli_check*
+                        * being called before, as first request after
+                        * the DCERPC bind.
+                        */
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+                       return;
+               }
+       }
+
+       /*
+        * we defer all callbacks in order to cleanup
+        * the database record.
+        */
+       tevent_req_defer_callback(req, state->ev);
+
+       state->tmp_creds = *state->creds;
+       netlogon_creds_client_authenticator(&state->tmp_creds,
+                                           &state->req_auth);
+       ZERO_STRUCT(state->rep_auth);
+
+       if (state->tmp_creds.negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+               netlogon_creds_aes_encrypt(&state->tmp_creds,
+                                          state->opaque.data,
+                                          state->opaque.length);
+       } else {
+               netlogon_creds_arcfour_crypt(&state->tmp_creds,
+                                            state->opaque.data,
+                                            state->opaque.length);
+       }
+
+       subreq = dcerpc_netr_NetrLogonSendToSam_send(state, state->ev,
+                                                    state->binding_handle,
+                                                    state->srv_name_slash,
+                                                    state->tmp_creds.computer_name,
+                                                    &state->req_auth,
+                                                    &state->rep_auth,
+                                                    state->opaque.data,
+                                                    state->opaque.length);
+       if (tevent_req_nomem(subreq, req)) {
+               status = NT_STATUS_NO_MEMORY;
+               netlogon_creds_cli_SendToSam_cleanup(req, status);
+               return;
+       }
+
+       tevent_req_set_callback(subreq,
+                               netlogon_creds_cli_SendToSam_done,
+                               req);
+}
+
+static void netlogon_creds_cli_SendToSam_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct netlogon_creds_cli_SendToSam_state *state =
+               tevent_req_data(req,
+               struct netlogon_creds_cli_SendToSam_state);
+       NTSTATUS status;
+       NTSTATUS result;
+       bool ok;
+
+       status = dcerpc_netr_NetrLogonSendToSam_recv(subreq, state, &result);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               netlogon_creds_cli_SendToSam_cleanup(req, status);
+               return;
+       }
+
+       ok = netlogon_creds_client_check(&state->tmp_creds,
+                                        &state->rep_auth.cred);
+       if (!ok) {
+               status = NT_STATUS_ACCESS_DENIED;
+               tevent_req_nterror(req, status);
+               netlogon_creds_cli_SendToSam_cleanup(req, status);
+               return;
+       }
+
+       *state->creds = state->tmp_creds;
+       status = netlogon_creds_cli_store(state->context,
+                                         &state->creds);
+
+       if (tevent_req_nterror(req, status)) {
+               netlogon_creds_cli_SendToSam_cleanup(req, status);
+               return;
+       }
+
+       /*
+        * Creds must be stored before we send back application errors
+        * e.g. NT_STATUS_NOT_IMPLEMENTED
+        */
+       if (tevent_req_nterror(req, result)) {
+               netlogon_creds_cli_SendToSam_cleanup(req, result);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_SendToSam(struct netlogon_creds_cli_context *context,
+                                     struct dcerpc_binding_handle *b,
+                                     struct netr_SendToSamBase *message)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_OK;
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = netlogon_creds_cli_SendToSam_send(frame, ev, context, b, message);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+
+       /* Ignore the result */
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}