#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;
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)
{
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;
bool try_auth3;
bool try_auth2;
bool require_auth2;
- bool try_previous_nt_hash;
struct netlogon_creds_cli_locked_state *locked_state;
};
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;
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);
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) {
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);
}
}
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;
}
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)) {
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...
*/
/*
* 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;
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;
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;
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);
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);
}
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();
/*
* 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;
return;
}
- netlogon_creds_encrypt_samlogon_logon(state->ro_creds,
+ netlogon_creds_encrypt_samlogon_logon(&state->tmp_creds,
state->logon_level,
state->logon);
/*
* 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;
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;
+}