--- /dev/null
+/*
+ Unix SMB/CIFS implementation.
+
+ module to store/fetch session keys for the schannel client
+
+ Copyright (C) Stefan Metzmacher 2013
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "lib/util/util_tdb.h"
+#include "libcli/security/security.h"
+#include "../lib/param/param.h"
+#include "../libcli/auth/schannel.h"
+#include "../librpc/gen_ndr/ndr_schannel.h"
+#include "../librpc/gen_ndr/ndr_netlogon_c.h"
+#include "netlogon_creds_cli.h"
+
+struct netlogon_creds_cli_context {
+ struct {
+ const char *computer;
+ const char *account;
+ uint32_t proposed_flags;
+ uint32_t required_flags;
+ enum netr_SchannelType type;
+ } client;
+
+ struct {
+ const char *computer;
+ const char *netbios_domain;
+ uint32_t cached_flags;
+ bool try_validation6;
+ bool try_logon_ex;
+ bool try_logon_with;
+ } server;
+
+ struct {
+ const char *key_name;
+ TDB_DATA key_data;
+ struct db_context *ctx;
+ struct g_lock_ctx *g_ctx;
+ } db;
+};
+
+static NTSTATUS netlogon_creds_cli_context_common(
+ const char *client_computer,
+ const char *client_account,
+ enum netr_SchannelType type,
+ uint32_t proposed_flags,
+ uint32_t required_flags,
+ const char *server_computer,
+ const char *server_netbios_domain,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_context **_context)
+{
+ struct netlogon_creds_cli_context *context = NULL;
+
+ *_context = NULL;
+
+ context = talloc_zero(mem_ctx, struct netlogon_creds_cli_context);
+ if (context == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->client.computer = talloc_strdup(context, client_computer);
+ if (context->client.computer == NULL) {
+ talloc_free(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->client.account = talloc_strdup(context, client_account);
+ if (context->client.account == NULL) {
+ talloc_free(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->client.proposed_flags = proposed_flags;
+ context->client.required_flags = required_flags;
+ context->client.type = type;
+
+ context->server.computer = talloc_strdup(context, server_computer);
+ if (context->server.computer == NULL) {
+ talloc_free(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->server.netbios_domain = talloc_strdup(context, server_netbios_domain);
+ if (context->server.netbios_domain == NULL) {
+ talloc_free(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->db.key_name = talloc_asprintf(context, "CLI[%s/%s]/SRV[%s/%s]",
+ client_computer,
+ client_account,
+ server_computer,
+ server_netbios_domain);
+ if (context->db.key_name == NULL) {
+ talloc_free(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ context->db.key_data = string_term_tdb_data(context->db.key_name);
+
+ *_context = context;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_context_global(struct loadparm_context *lp_ctx,
+ const char *client_account,
+ enum netr_SchannelType type,
+ const char *server_computer,
+ const char *server_netbios_domain,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_context **_context)
+{
+ static struct db_context *global_db = NULL;
+ char *fname = NULL;
+ NTSTATUS status;
+ struct netlogon_creds_cli_context *context = NULL;
+ const char *client_computer;
+ uint32_t proposed_flags;
+ uint32_t required_flags = 0;
+ bool reject_md5_servers = false;
+ bool require_strong_key = false;
+ int require_sign_or_seal = lpcfg_client_schannel(lp_ctx);
+
+ *_context = NULL;
+
+ client_computer = lpcfg_netbios_name(lp_ctx);
+
+ //TODO: add lpcfp_reject_md5_servers()
+ reject_md5_servers = lpcfg_parm_bool(lp_ctx, NULL,
+ "__default__",
+ "reject md5 servers",
+ reject_md5_servers);
+ /*
+ * allow overwrite per domain
+ * reject md5 servers:<netbios_domain>
+ */
+ reject_md5_servers = lpcfg_parm_bool(lp_ctx, NULL,
+ "reject md5 servers",
+ server_netbios_domain,
+ reject_md5_servers);
+
+ //TODO: add lpcfp_require_strong_key()
+ require_strong_key = lpcfg_parm_bool(lp_ctx, NULL,
+ "__default__",
+ "require strong key",
+ require_strong_key);
+ /*
+ * allow overwrite per domain
+ * require strong key:<netbios_domain>
+ */
+ require_strong_key = lpcfg_parm_bool(lp_ctx, NULL,
+ "require strong key",
+ server_netbios_domain,
+ require_strong_key);
+
+ proposed_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ proposed_flags |= NETLOGON_NEG_SUPPORTS_AES;
+
+ if (reject_md5_servers) {
+ required_flags |= NETLOGON_NEG_SUPPORTS_AES;
+ required_flags |= NETLOGON_NEG_PASSWORD_SET2;
+ required_flags |= NETLOGON_NEG_AUTHENTICATED_RPC;
+ }
+
+ if (require_strong_key) {
+ required_flags |= NETLOGON_NEG_STRONG_KEYS;
+ required_flags |= NETLOGON_NEG_ARCFOUR;
+ required_flags |= NETLOGON_NEG_AUTHENTICATED_RPC;
+ }
+
+ if (require_sign_or_seal == true) {
+ required_flags |= NETLOGON_NEG_AUTHENTICATED_RPC;
+ }
+
+ status = netlogon_creds_cli_context_common(client_account,
+ client_computer,
+ type,
+ proposed_flags,
+ required_flags,
+ server_computer,
+ server_netbios_domain,
+ mem_ctx,
+ &context);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (global_db != NULL) {
+ context->db.ctx = global_db;
+ *_context = context;
+ return NT_STATUS_OK;
+ }
+
+ fname = lpcfg_private_db_path(context, lp_ctx, "netlogon_creds_cli");
+ if (fname == NULL) {
+ talloc_free(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ global_db = dbwrap_local_open(talloc_autofree_context(), lp_ctx,
+ fname, 0,
+ TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
+ O_RDWR|O_CREAT,
+ 0600, DBWRAP_LOCK_ORDER_2);
+ if (global_db == NULL) {
+ DEBUG(0,("netlogon_creds_cli_context_global: Failed to open %s - %s\n",
+ fname, strerror(errno)));
+ talloc_free(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+ TALLOC_FREE(fname);
+
+ context->db.ctx = global_db;
+ *_context = context;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_context_tmp(const char *client_account,
+ const char *client_computer,
+ enum netr_SchannelType type,
+ uint32_t proposed_flags,
+ uint32_t required_flags,
+ const char *server_computer,
+ const char *server_netbios_domain,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_cli_context **_context)
+{
+ NTSTATUS status;
+ struct netlogon_creds_cli_context *context = NULL;
+
+ *_context = NULL;
+
+ status = netlogon_creds_cli_context_common(client_account,
+ client_computer,
+ type,
+ proposed_flags,
+ required_flags,
+ server_computer,
+ server_netbios_domain,
+ mem_ctx,
+ &context);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ context->db.ctx = db_open_rbt(context);
+ if (context->db.ctx == NULL) {
+ talloc_free(context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *_context = context;
+ return NT_STATUS_OK;
+}
+
+struct netlogon_creds_cli_fetch_state {
+ TALLOC_CTX *mem_ctx;
+ struct netlogon_creds_CredentialState *creds;
+ NTSTATUS status;
+};
+
+static void netlogon_creds_cli_fetch_parser(TDB_DATA key, TDB_DATA data,
+ void *private_data)
+{
+ struct netlogon_creds_cli_fetch_state *state =
+ (struct netlogon_creds_cli_fetch_state *)private_data;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+
+ state->creds = talloc_zero(state->mem_ctx,
+ struct netlogon_creds_CredentialState);
+ if (state->creds == NULL) {
+ state->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ blob.data = data.dptr;
+ blob.length = data.dsize;
+
+ ndr_err = ndr_pull_struct_blob(&blob, state->creds, state->creds,
+ (ndr_pull_flags_fn_t)ndr_pull_netlogon_creds_CredentialState);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(state->creds);
+ state->status = ndr_map_error2ntstatus(ndr_err);
+ return;
+ }
+
+ state->status = NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_get(struct netlogon_creds_cli_context *context,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState **_creds)
+{
+ NTSTATUS status;
+ struct netlogon_creds_cli_fetch_state fstate = {
+ .mem_ctx = mem_ctx,
+ .status = NT_STATUS_INTERNAL_ERROR,
+ };
+ static const struct netr_Credential zero_creds;
+
+ *_creds = NULL;
+
+ status = dbwrap_parse_record(context->db.ctx,
+ context->db.key_data,
+ netlogon_creds_cli_fetch_parser,
+ &fstate);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = fstate.status;
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * mark it as invalid for step operations.
+ */
+ fstate.creds->sequence = 0;
+ fstate.creds->seed = zero_creds;
+ fstate.creds->client = zero_creds;
+ fstate.creds->server = zero_creds;
+
+ if (context->server.cached_flags == fstate.creds->negotiate_flags) {
+ *_creds = fstate.creds;
+ return NT_STATUS_OK;
+ }
+
+ context->server.cached_flags = fstate.creds->negotiate_flags;
+ context->server.try_validation6 = true;
+ context->server.try_logon_ex = true;
+ context->server.try_logon_with = true;
+
+ if (!(context->server.cached_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
+ context->server.try_validation6 = false;
+ context->server.try_logon_ex = false;
+ }
+ if (!(context->server.cached_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
+ context->server.try_validation6 = false;
+ }
+
+ *_creds = fstate.creds;
+ return NT_STATUS_OK;
+}
+
+bool netlogon_creds_cli_validate(struct netlogon_creds_cli_context *context,
+ const struct netlogon_creds_CredentialState *creds1)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct netlogon_creds_CredentialState *creds2;
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_get(context, frame, &creds2);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (creds1->negotiate_flags != creds2->negotiate_flags) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ if (memcmp(creds1->session_key, creds2->session_key, 16) != 0) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (creds1->sequence != creds2->sequence) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (memcmp(&creds1->seed, &creds2->seed, sizeof(creds2->seed)) != 0) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (memcmp(&creds1->client, &creds2->client, sizeof(creds2->client)) != 0) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (memcmp(&creds1->server, &creds2->server, sizeof(creds2->server)) != 0) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (creds1->secure_channel_type != creds2->secure_channel_type) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (!strequal(creds1->computer_name, creds2->computer_name)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (!strequal(creds1->account_name, creds2->account_name)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (dom_sid_compare(creds1->sid, creds2->sid) != 0) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ TALLOC_FREE(frame);
+ return true;
+}
+
+NTSTATUS netlogon_creds_cli_store(struct netlogon_creds_cli_context *context,
+ struct netlogon_creds_CredentialState **_creds)
+{
+ struct netlogon_creds_CredentialState *creds = *_creds;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TDB_DATA data;
+ static const struct netr_Credential zero_creds;
+ bool valid = false;
+
+ *_creds = NULL;
+
+ if (creds->sequence != 0) {
+ valid = true;
+ } else if (memcmp(&creds->seed, &zero_creds, sizeof(zero_creds)) != 0) {
+ valid = true;
+ } else if (memcmp(&creds->client, &zero_creds, sizeof(zero_creds)) != 0) {
+ valid = true;
+ } else if (memcmp(&creds->server, &zero_creds, sizeof(zero_creds)) != 0) {
+ valid = true;
+ }
+
+ if (!valid) {
+ /*
+ * this was the result of netlogon_creds_cli_get()
+ * which only gives a valid read only session key.
+ */
+ TALLOC_FREE(creds);
+ return NT_STATUS_INVALID_PAGE_PROTECTION;
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, creds, creds,
+ (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(creds);
+ status = ndr_map_error2ntstatus(ndr_err);
+ return status;
+ }
+
+ data.dptr = blob.data;
+ data.dsize = blob.length;
+
+ status = dbwrap_store(context->db.ctx,
+ context->db.key_data,
+ data, TDB_REPLACE);
+ TALLOC_FREE(creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_delete(struct netlogon_creds_cli_context *context,
+ struct netlogon_creds_CredentialState **_creds)
+{
+ struct netlogon_creds_CredentialState *creds = *_creds;
+ NTSTATUS status;
+ static const struct netr_Credential zero_creds;
+ bool valid = false;
+
+ *_creds = NULL;
+
+ if (creds->sequence != 0) {
+ valid = true;
+ } else if (memcmp(&creds->seed, &zero_creds, sizeof(zero_creds)) != 0) {
+ valid = true;
+ } else if (memcmp(&creds->client, &zero_creds, sizeof(zero_creds)) != 0) {
+ valid = true;
+ } else if (memcmp(&creds->server, &zero_creds, sizeof(zero_creds)) != 0) {
+ valid = true;
+ }
+
+ if (!valid) {
+ /*
+ * this was the result of netlogon_creds_cli_get()
+ * which only gives a valid read only session key.
+ */
+ TALLOC_FREE(creds);
+ return NT_STATUS_INVALID_PAGE_PROTECTION;
+ }
+
+ status = dbwrap_delete(context->db.ctx,
+ context->db.key_data);
+ TALLOC_FREE(creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct netlogon_creds_cli_lock_state {
+ struct netlogon_creds_CredentialState *creds;
+};
+
+struct tevent_req *netlogon_creds_cli_lock_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_lock_state *state;
+ struct netlogon_creds_cli_fetch_state fstate = {
+ .status = NT_STATUS_INTERNAL_ERROR,
+ };
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_lock_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ fstate.mem_ctx = state;
+ status = dbwrap_parse_record(context->db.ctx,
+ context->db.key_data,
+ netlogon_creds_cli_fetch_parser,
+ &fstate);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ status = fstate.status;
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (context->server.cached_flags == fstate.creds->negotiate_flags) {
+ state->creds = fstate.creds;
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ context->server.cached_flags = fstate.creds->negotiate_flags;
+ context->server.try_validation6 = true;
+ context->server.try_logon_ex = true;
+ context->server.try_logon_with = true;
+
+ if (!(context->server.cached_flags & NETLOGON_NEG_AUTHENTICATED_RPC)) {
+ context->server.try_validation6 = false;
+ context->server.try_logon_ex = false;
+ }
+ if (!(context->server.cached_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS)) {
+ context->server.try_validation6 = false;
+ }
+
+ state->creds = fstate.creds;
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+NTSTATUS netlogon_creds_cli_lock_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState **creds)
+{
+ struct netlogon_creds_cli_lock_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_lock_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *creds = talloc_move(mem_ctx, &state->creds);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_lock(struct netlogon_creds_cli_context *context,
+ TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState **creds)
+{
+ 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_lock_send(frame, ev, context);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_lock_recv(req, mem_ctx, creds);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+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;
+ char *srv_name_slash;
+ uint32_t current_flags;
+ struct netr_Credential client_challenge;
+ struct netr_Credential server_challenge;
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_Credential client_credential;
+ struct netr_Credential server_credential;
+ uint32_t rid;
+ bool try_auth3;
+ bool try_auth2;
+ bool require_auth2;
+ bool try_previous_nt_hash;
+};
+
+static void netlogon_creds_cli_auth_challenge_start(struct tevent_req *req);
+
+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)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_auth_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_auth_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ 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;
+ }
+
+ state->used_nt_hash = state->current_nt_hash;
+
+ if (context->client.required_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ state->try_auth2 = true;
+ state->require_auth2 = true;
+ }
+
+ if (context->client.required_flags & NETLOGON_NEG_STRONG_KEYS) {
+ state->try_auth2 = true;
+ state->require_auth2 = true;
+ }
+
+ state->current_flags = context->client.proposed_flags;
+
+ if (state->current_flags & NETLOGON_NEG_AUTHENTICATED_RPC) {
+ state->try_auth3 = true;
+ state->try_auth2 = true;
+ }
+
+ 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);
+ }
+
+ 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;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ netlogon_creds_cli_auth_challenge_start(req);
+
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void netlogon_creds_cli_auth_challenge_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_auth_challenge_start(struct tevent_req *req)
+{
+ struct netlogon_creds_cli_auth_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_auth_state);
+ struct tevent_req *subreq;
+
+ generate_random_buffer(state->client_challenge.data,
+ sizeof(state->client_challenge.data));
+
+ subreq = dcerpc_netr_ServerReqChallenge_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.computer,
+ &state->client_challenge,
+ &state->server_challenge);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_auth_challenge_done,
+ req);
+}
+
+static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_auth_challenge_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_auth_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_auth_state);
+ NTSTATUS status;
+ NTSTATUS result;
+
+ status = dcerpc_netr_ServerReqChallenge_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (tevent_req_nterror(req, result)) {
+ return;
+ }
+
+ /* Calculate the session key and client credentials */
+
+ state->creds = netlogon_creds_client_init(state,
+ state->context->client.account,
+ state->context->client.computer,
+ &state->client_challenge,
+ &state->server_challenge,
+ &state->used_nt_hash,
+ &state->client_credential,
+ state->current_flags);
+ if (tevent_req_nomem(state->creds, req)) {
+ return;
+ }
+
+ if (state->try_auth3) {
+ subreq = dcerpc_netr_ServerAuthenticate3_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.account,
+ state->context->client.type,
+ state->context->client.computer,
+ &state->client_credential,
+ &state->server_credential,
+ &state->creds->negotiate_flags,
+ &state->rid);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ } else if (state->try_auth2) {
+ state->rid = 0;
+
+ subreq = dcerpc_netr_ServerAuthenticate2_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.account,
+ state->context->client.type,
+ state->context->client.computer,
+ &state->client_credential,
+ &state->server_credential,
+ &state->creds->negotiate_flags);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ } else {
+ state->rid = 0;
+ /*
+ * keep old samba versions happy.
+ */
+ state->creds->negotiate_flags = NETLOGON_NEG_AUTHENTICATED_RPC;
+
+ subreq = dcerpc_netr_ServerAuthenticate_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->context->client.account,
+ state->context->client.type,
+ state->context->client.computer,
+ &state->client_credential,
+ &state->server_credential);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ }
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_auth_srvauth_done,
+ req);
+}
+
+static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_auth_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_auth_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TDB_DATA data;
+ uint32_t tmp_flags;
+
+ if (state->try_auth3) {
+ status = dcerpc_netr_ServerAuthenticate3_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ state->try_auth3 = false;
+ TALLOC_FREE(state->creds);
+ netlogon_creds_cli_auth_challenge_start(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ } else if (state->try_auth2) {
+ status = dcerpc_netr_ServerAuthenticate2_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ state->try_auth2 = false;
+ if (state->require_auth2) {
+ status = NT_STATUS_DOWNGRADE_DETECTED;
+ tevent_req_nterror(req, status);
+ return;
+ }
+ TALLOC_FREE(state->creds);
+ netlogon_creds_cli_auth_challenge_start(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ } else {
+ status = dcerpc_netr_ServerAuthenticate_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+
+ tmp_flags = state->creds->negotiate_flags;
+ tmp_flags &= state->context->client.required_flags;
+ if (tmp_flags != state->context->client.required_flags) {
+ if (NT_STATUS_IS_OK(result)) {
+ tevent_req_nterror(req, NT_STATUS_DOWNGRADE_DETECTED);
+ return;
+ }
+ tevent_req_nterror(req, result);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
+ tmp_flags = state->context->client.proposed_flags;
+ if (state->current_flags != tmp_flags) {
+ if (!state->try_previous_nt_hash) {
+ /*
+ * we already retried, giving up...
+ */
+ tevent_req_nterror(req, result);
+ return;
+ }
+
+ /*
+ * lets retry with the old nt hash.
+ */
+ state->used_nt_hash = state->previous_nt_hash;
+ state->try_previous_nt_hash = false;
+ state->current_flags = tmp_flags;
+ TALLOC_FREE(state->creds);
+ netlogon_creds_cli_auth_challenge_start(req);
+ return;
+ }
+
+ /*
+ * lets retry with the negotiated flags
+ */
+ state->current_flags = state->creds->negotiate_flags;
+ TALLOC_FREE(state->creds);
+ netlogon_creds_cli_auth_challenge_start(req);
+ return;
+ }
+
+ ok = netlogon_creds_client_check(state->creds,
+ &state->server_credential);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, state, state->creds,
+ (ndr_push_flags_fn_t)ndr_push_netlogon_creds_CredentialState);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ data.dptr = blob.data;
+ data.dsize = blob.length;
+
+ status = dbwrap_store(state->context->db.ctx,
+ state->context->db.key_data,
+ data, TDB_REPLACE);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ 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)
+{
+ 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_auth_send(frame, ev, context, b,
+ current_nt_hash,
+ previous_nt_hash);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_auth_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_check_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+
+ char *srv_name_slash;
+ char *cli_name_slash;
+
+ union netr_Capabilities caps;
+
+ struct netlogon_creds_CredentialState *creds;
+ struct netlogon_creds_CredentialState tmp_creds;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_check_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+static void netlogon_creds_cli_check_locked(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_creds_cli_check_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_check_state *state;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_check_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->cli_name_slash = talloc_asprintf(state, "\\%s",
+ context->client.computer);
+ if (tevent_req_nomem(state->cli_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * TODO
+ * verify DCERPC_AUTH_TYPE_SCHANNEL
+ */
+
+ 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_check_locked,
+ req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_check_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_check_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_check_state);
+
+ if (state->creds == NULL) {
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED)) {
+ TALLOC_FREE(state->creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete(state->context, &state->creds);
+}
+
+static void netlogon_creds_cli_check_caps(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_check_locked(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_check_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_check_state);
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lock_recv(subreq, state,
+ &state->creds);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ 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_LogonGetCapabilities_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->cli_name_slash,
+ &state->req_auth,
+ &state->rep_auth,
+ 1,
+ &state->caps);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_check_caps,
+ req);
+}
+
+static void netlogon_creds_cli_check_caps(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_check_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_check_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+
+ status = dcerpc_netr_LogonGetCapabilities_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ status = NT_STATUS_OK;
+ result = NT_STATUS_NOT_IMPLEMENTED;
+ }
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_check_cleanup(req, result);
+ return;
+ }
+ }
+
+ ok = netlogon_creds_client_check(&state->tmp_creds,
+ &state->rep_auth.cred);
+ if (!ok) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_check_cleanup(req, result);
+ return;
+ }
+
+ if (state->caps.server_capabilities != state->tmp_creds.negotiate_flags) {
+ status = NT_STATUS_DOWNGRADE_DETECTED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ if (!(state->caps.server_capabilities & NETLOGON_NEG_SUPPORTS_AES)) {
+ status = NT_STATUS_DOWNGRADE_DETECTED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ *state->creds = state->tmp_creds;
+ status = netlogon_creds_cli_store(state->context,
+ &state->creds);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_check_cleanup(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_check_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ netlogon_creds_cli_check_cleanup(req, status);
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_check(struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b)
+{
+ 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_check_send(frame, ev, context, b);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_check_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_ServerPasswordSet_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+ uint32_t old_timeout;
+
+ const char *new_password;
+
+ char *srv_name_slash;
+
+ struct netr_CryptPassword crypt_password;
+ struct samr_Password samr_password;
+
+ 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_ServerPasswordSet_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+static void netlogon_creds_cli_ServerPasswordSet_locked(struct tevent_req *subreq);
+
+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)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_ServerPasswordSet_state *state;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_ServerPasswordSet_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+
+ state->new_password = talloc_strdup(state, new_password);
+ if (tevent_req_nomem(state->new_password, req)) {
+ return tevent_req_post(req, ev);
+ }
+ talloc_set_name_const(state->new_password, "<new_password>");
+
+ 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);
+ }
+
+ /*
+ * TODO
+ * verify DCERPC_AUTH_TYPE_SCHANNEL
+ */
+
+ 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_ServerPasswordSet_locked,
+ req);
+
+ return req;
+}
+
+static void netlogon_creds_cli_ServerPasswordSet_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_ServerPasswordSet_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_ServerPasswordSet_state);
+
+ if (state->creds == NULL) {
+ return;
+ }
+
+ dcerpc_binding_handle_set_timeout(state->binding_handle,
+ state->old_timeout);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ TALLOC_FREE(state->creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete(state->context, &state->creds);
+}
+
+static void netlogon_creds_cli_ServerPasswordSet_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_ServerPasswordSet_locked(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_ServerPasswordSet_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_ServerPasswordSet_state);
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lock_recv(subreq, state,
+ &state->creds);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->old_timeout = dcerpc_binding_handle_set_timeout(
+ state->binding_handle, 600000);
+
+ /*
+ * 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_PASSWORD_SET2) {
+ struct samr_CryptPassword password_buf;
+
+ encode_pw_buffer(password_buf.data,
+ state->new_password, STR_UNICODE);
+
+ if (state->tmp_creds.negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(&state->tmp_creds,
+ password_buf.data, 516);
+ } else {
+ netlogon_creds_arcfour_crypt(&state->tmp_creds,
+ password_buf.data, 516);
+ }
+
+ memcpy(state->crypt_password.data, password_buf.data, 512);
+ state->crypt_password.length = IVAL(password_buf.data, 512);
+ ZERO_STRUCT(password_buf);
+
+ subreq = dcerpc_netr_ServerPasswordSet2_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->tmp_creds.account_name,
+ /* TODO: state->tmp_creds.secure_channel_type, */
+ state->context->client.type,
+ state->tmp_creds.computer_name,
+ &state->req_auth,
+ &state->rep_auth,
+ &state->crypt_password);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+ } else {
+ E_md4hash(state->new_password, state->samr_password.hash);
+ netlogon_creds_des_encrypt(&state->tmp_creds,
+ &state->samr_password);
+
+ subreq = dcerpc_netr_ServerPasswordSet_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->samr_password);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_ServerPasswordSet_done,
+ req);
+}
+
+static void netlogon_creds_cli_ServerPasswordSet_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_ServerPasswordSet_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_ServerPasswordSet_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+
+ if (state->tmp_creds.negotiate_flags & NETLOGON_NEG_PASSWORD_SET2) {
+ status = dcerpc_netr_ServerPasswordSet2_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+ } else {
+ status = dcerpc_netr_ServerPasswordSet_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerPasswordSet_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_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, result);
+ return;
+ }
+
+ dcerpc_binding_handle_set_timeout(state->binding_handle,
+ state->old_timeout);
+
+ *state->creds = state->tmp_creds;
+ status = netlogon_creds_cli_store(state->context,
+ &state->creds);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_ServerPasswordSet_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ netlogon_creds_cli_ServerPasswordSet_cleanup(req, status);
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_ServerPasswordSet(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ const char *new_password)
+{
+ 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_ServerPasswordSet_send(frame, ev, context, b,
+ new_password);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_ServerPasswordSet_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct netlogon_creds_cli_LogonSamLogon_state {
+ struct tevent_context *ev;
+ struct netlogon_creds_cli_context *context;
+ struct dcerpc_binding_handle *binding_handle;
+
+ char *srv_name_slash;
+ char *cli_name_slash;
+
+ enum netr_LogonInfoClass logon_level;
+ const union netr_LogonLevel *const_logon;
+ union netr_LogonLevel *logon;
+ uint32_t flags;
+
+ uint16_t validation_level;
+ union netr_Validation *validation;
+ uint8_t authoritative;
+
+ /*
+ * do we need encryption at the application layer?
+ */
+ bool user_encrypt;
+
+ /*
+ * the read only credentials before we started the operation
+ */
+ struct netlogon_creds_CredentialState *ro_creds;
+
+ struct netlogon_creds_CredentialState *lk_creds;
+
+ struct netlogon_creds_CredentialState tmp_creds;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+};
+
+static void netlogon_creds_cli_LogonSamLogon_start(struct tevent_req *req);
+static void netlogon_creds_cli_LogonSamLogon_cleanup(struct tevent_req *req,
+ NTSTATUS status);
+
+struct tevent_req *netlogon_creds_cli_LogonSamLogon_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ enum netr_LogonInfoClass logon_level,
+ const union netr_LogonLevel *logon,
+ uint32_t flags)
+{
+ struct tevent_req *req;
+ struct netlogon_creds_cli_LogonSamLogon_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct netlogon_creds_cli_LogonSamLogon_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->context = context;
+ state->binding_handle = b;
+
+ state->logon_level = logon_level;
+ state->const_logon = logon;
+ state->flags = flags;
+
+ 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->cli_name_slash = talloc_asprintf(state, "\\%s",
+ context->client.computer);
+ if (tevent_req_nomem(state->cli_name_slash, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ switch (logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonServiceTransitiveInformation:
+ case NetlogonGenericInformation:
+ state->user_encrypt = true;
+ break;
+
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ break;
+ }
+
+ state->validation = talloc_zero(state, union netr_Validation);
+ if (tevent_req_nomem(state->validation, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * TODO
+ * verify DCERPC_AUTH_TYPE_SCHANNEL
+ * for try_logon_ex.
+ *
+ * and DCERPC_AUTH_LEVEL_PRIVACY for
+ * try_validation6
+ */
+
+ netlogon_creds_cli_LogonSamLogon_start(req);
+
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * we defer all callbacks in order to cleanup
+ * the database record.
+ */
+ tevent_req_defer_callback(req, state->ev);
+ return req;
+}
+
+static void netlogon_creds_cli_LogonSamLogon_cleanup(struct tevent_req *req,
+ NTSTATUS status)
+{
+ struct netlogon_creds_cli_LogonSamLogon_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonSamLogon_state);
+
+ if (state->lk_creds == NULL) {
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ TALLOC_FREE(state->lk_creds);
+ return;
+ }
+
+ netlogon_creds_cli_delete(state->context, &state->lk_creds);
+}
+
+static void netlogon_creds_cli_LogonSamLogon_done(struct tevent_req *subreq);
+
+static void netlogon_creds_cli_LogonSamLogon_start(struct tevent_req *req)
+{
+ struct netlogon_creds_cli_LogonSamLogon_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonSamLogon_state);
+ struct tevent_req *subreq;
+ NTSTATUS status;
+
+ TALLOC_FREE(state->ro_creds);
+ TALLOC_FREE(state->logon);
+
+ if (state->context->server.try_logon_ex) {
+ if (state->context->server.try_validation6) {
+ state->validation_level = 6;
+ } else {
+ state->validation_level = 3;
+ state->user_encrypt = true;
+ }
+
+ state->logon = netlogon_creds_shallow_copy_logon(state,
+ state->logon_level,
+ state->const_logon);
+ if (tevent_req_nomem(state->logon, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ if (state->user_encrypt) {
+ status = netlogon_creds_cli_get(state->context,
+ state,
+ &state->ro_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ netlogon_creds_encrypt_samlogon_logon(state->ro_creds,
+ state->logon_level,
+ state->logon);
+ }
+
+ subreq = dcerpc_netr_LogonSamLogonEx_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->cli_name_slash,
+ state->logon_level,
+ state->logon,
+ state->validation_level,
+ state->validation,
+ &state->authoritative,
+ &state->flags);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_LogonSamLogon_done,
+ req);
+ return;
+ }
+
+ if (state->lk_creds == NULL) {
+ subreq = netlogon_creds_cli_lock_send(state, state->ev,
+ state->context);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_LogonSamLogon_done,
+ req);
+ return;
+ }
+
+ state->tmp_creds = *state->lk_creds;
+ netlogon_creds_client_authenticator(&state->tmp_creds,
+ &state->req_auth);
+ ZERO_STRUCT(state->rep_auth);
+
+ state->logon = netlogon_creds_shallow_copy_logon(state,
+ state->logon_level,
+ state->const_logon);
+ if (tevent_req_nomem(state->logon, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ netlogon_creds_encrypt_samlogon_logon(state->ro_creds,
+ state->logon_level,
+ state->logon);
+
+ state->validation_level = 3;
+
+ if (state->context->server.try_logon_with) {
+ subreq = dcerpc_netr_LogonSamLogonWithFlags_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->cli_name_slash,
+ &state->req_auth,
+ &state->rep_auth,
+ state->logon_level,
+ state->logon,
+ state->validation_level,
+ state->validation,
+ &state->authoritative,
+ &state->flags);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ } else {
+ state->flags = 0;
+
+ subreq = dcerpc_netr_LogonSamLogon_send(state, state->ev,
+ state->binding_handle,
+ state->srv_name_slash,
+ state->cli_name_slash,
+ &state->req_auth,
+ &state->rep_auth,
+ state->logon_level,
+ state->logon,
+ state->validation_level,
+ state->validation,
+ &state->authoritative);
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ }
+
+ tevent_req_set_callback(subreq,
+ netlogon_creds_cli_LogonSamLogon_done,
+ req);
+}
+
+static void netlogon_creds_cli_LogonSamLogon_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct netlogon_creds_cli_LogonSamLogon_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonSamLogon_state);
+ NTSTATUS status;
+ NTSTATUS result;
+ bool ok;
+
+ if (state->context->server.try_logon_ex) {
+ status = dcerpc_netr_LogonSamLogonEx_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ state->context->server.try_validation6 = false;
+ state->context->server.try_logon_ex = false;
+ netlogon_creds_cli_LogonSamLogon_start(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ if ((state->validation_level == 6) &&
+ (NT_STATUS_EQUAL(result, NT_STATUS_INVALID_INFO_CLASS) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_INVALID_PARAMETER) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)))
+ {
+ state->context->server.try_validation6 = false;
+ netlogon_creds_cli_LogonSamLogon_start(req);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, result);
+ return;
+ }
+
+ if (state->ro_creds == NULL) {
+ tevent_req_done(req);
+ return;
+ }
+
+ ok = netlogon_creds_cli_validate(state->context, state->ro_creds);
+ if (!ok) {
+ status = NT_STATUS_ACCESS_DENIED;
+ tevent_req_nterror(req, status);
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ netlogon_creds_decrypt_samlogon_validation(state->ro_creds,
+ state->validation_level,
+ state->validation);
+
+ tevent_req_done(req);
+ return;
+ }
+
+ if (state->lk_creds == NULL) {
+ status = netlogon_creds_cli_lock_recv(subreq, state,
+ &state->lk_creds);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ netlogon_creds_cli_LogonSamLogon_start(req);
+ return;
+ }
+
+ if (state->context->server.try_logon_with) {
+ status = dcerpc_netr_LogonSamLogonWithFlags_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ state->context->server.try_logon_with = false;
+ netlogon_creds_cli_LogonSamLogon_start(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+ } else {
+ status = dcerpc_netr_LogonSamLogon_recv(subreq, state,
+ &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_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_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ *state->lk_creds = state->tmp_creds;
+ status = netlogon_creds_cli_store(state->context,
+ &state->lk_creds);
+ if (tevent_req_nterror(req, status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, result);
+ return;
+ }
+
+ netlogon_creds_decrypt_samlogon_validation(&state->tmp_creds,
+ state->validation_level,
+ state->validation);
+
+ tevent_req_done(req);
+}
+
+NTSTATUS netlogon_creds_cli_LogonSamLogon_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint16_t *validation_level,
+ union netr_Validation **validation,
+ uint8_t *authoritative,
+ uint32_t *flags)
+{
+ struct netlogon_creds_cli_LogonSamLogon_state *state =
+ tevent_req_data(req,
+ struct netlogon_creds_cli_LogonSamLogon_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ netlogon_creds_cli_LogonSamLogon_cleanup(req, status);
+ tevent_req_received(req);
+ return status;
+ }
+
+ *validation_level = state->validation_level;
+ *validation = talloc_move(mem_ctx, &state->validation);
+ *authoritative = state->authoritative;
+ *flags = state->flags;
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_creds_cli_LogonSamLogon(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ enum netr_LogonInfoClass logon_level,
+ const union netr_LogonLevel *logon,
+ TALLOC_CTX *mem_ctx,
+ uint16_t *validation_level,
+ union netr_Validation **validation,
+ uint8_t *authoritative,
+ uint32_t *flags)
+{
+ 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_LogonSamLogon_send(frame, ev, context, b,
+ logon_level, logon,
+ *flags);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = netlogon_creds_cli_LogonSamLogon_recv(req, mem_ctx,
+ validation_level,
+ validation,
+ authoritative,
+ flags);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}