From 7b2b68846071adf2bf04c890899e9853595ef483 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 Apr 2013 19:16:42 +0200 Subject: [PATCH] libcli/auth/netlogon_creds_cli.c --- libcli/auth/netlogon_creds_cli.c | 2010 ++++++++++++++++++++++++++++++ libcli/auth/netlogon_creds_cli.h | 120 ++ libcli/auth/wscript_build | 4 +- 3 files changed, 2132 insertions(+), 2 deletions(-) create mode 100644 libcli/auth/netlogon_creds_cli.c create mode 100644 libcli/auth/netlogon_creds_cli.h diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c new file mode 100644 index 000000000000..085f734417c3 --- /dev/null +++ b/libcli/auth/netlogon_creds_cli.c @@ -0,0 +1,2010 @@ +/* + 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 . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include +#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: + */ + 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: + */ + 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, ""); + + 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; +} diff --git a/libcli/auth/netlogon_creds_cli.h b/libcli/auth/netlogon_creds_cli.h new file mode 100644 index 000000000000..25848c33bc97 --- /dev/null +++ b/libcli/auth/netlogon_creds_cli.h @@ -0,0 +1,120 @@ +/* + 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 . +*/ + +#ifndef NETLOGON_CREDS_CLI_H +#define NETLOGON_CREDS_CLI_H + +struct netlogon_creds_cli_context; + +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); +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 netlogon_creds_cli_get(struct netlogon_creds_cli_context *context, + TALLOC_CTX *mem_ctx, + struct netlogon_creds_CredentialState **_creds); +bool netlogon_creds_cli_validate(struct netlogon_creds_cli_context *context, + const struct netlogon_creds_CredentialState *creds1); + +NTSTATUS netlogon_creds_cli_store(struct netlogon_creds_cli_context *context, + struct netlogon_creds_CredentialState **_creds); +NTSTATUS netlogon_creds_cli_delete(struct netlogon_creds_cli_context *context, + 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); +NTSTATUS netlogon_creds_cli_lock_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct netlogon_creds_CredentialState **creds); +NTSTATUS netlogon_creds_cli_lock(struct netlogon_creds_cli_context *context, + TALLOC_CTX *mem_ctx, + struct netlogon_creds_CredentialState **creds); + +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); +NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req); +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); + +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); +NTSTATUS netlogon_creds_cli_check_recv(struct tevent_req *req); +NTSTATUS netlogon_creds_cli_check(struct netlogon_creds_cli_context *context, + struct dcerpc_binding_handle *b); + +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); +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); + +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); +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); +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); + +#endif /* NETLOGON_CREDS_CLI_H */ diff --git a/libcli/auth/wscript_build b/libcli/auth/wscript_build index df23058f6e74..b4d88741cd1a 100755 --- a/libcli/auth/wscript_build +++ b/libcli/auth/wscript_build @@ -24,8 +24,8 @@ bld.SAMBA_SUBSYSTEM('LIBCLI_AUTH', bld.SAMBA_SUBSYSTEM('COMMON_SCHANNEL', - source='schannel_state_tdb.c schannel_sign.c', - deps='dbwrap util_tdb samba-hostconfig NDR_NETLOGON' + source='schannel_state_tdb.c schannel_sign.c netlogon_creds_cli.c', + deps='dbwrap util_tdb tevent-util samba-hostconfig RPC_NDR_NETLOGON NDR_NETLOGON' ) -- 2.34.1