From ddd7ac68ccae8b4df6c6a65b3dad20e21924f538 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 20:44:40 +0200 Subject: [PATCH] libcli/auth: pass an array of nt_hashes to netlogon_creds_cli_auth*() This way the caller can pass more than 2 hashes and can only know which hash was used for a successful connection. We allow up to 4 hashes (next, current, old, older). BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider --- libcli/auth/netlogon_creds_cli.c | 58 ++++++++++++++++++++----------- libcli/auth/netlogon_creds_cli.h | 12 ++++--- source3/libsmb/trusts_util.c | 19 +++++++--- source3/rpc_client/cli_netlogon.c | 15 ++++++-- 4 files changed, 71 insertions(+), 33 deletions(-) diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c index fcab81491c6..fabb2653483 100644 --- a/libcli/auth/netlogon_creds_cli.c +++ b/libcli/auth/netlogon_creds_cli.c @@ -943,9 +943,10 @@ struct netlogon_creds_cli_auth_state { struct tevent_context *ev; struct netlogon_creds_cli_context *context; struct dcerpc_binding_handle *binding_handle; - struct samr_Password current_nt_hash; - struct samr_Password previous_nt_hash; - struct samr_Password used_nt_hash; + uint8_t num_nt_hashes; + uint8_t idx_nt_hashes; + const struct samr_Password * const *nt_hashes; + const struct samr_Password *used_nt_hash; char *srv_name_slash; uint32_t current_flags; struct netr_Credential client_challenge; @@ -957,7 +958,6 @@ struct netlogon_creds_cli_auth_state { bool try_auth3; bool try_auth2; bool require_auth2; - bool try_previous_nt_hash; struct netlogon_creds_cli_locked_state *locked_state; }; @@ -968,8 +968,8 @@ struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct netlogon_creds_cli_context *context, struct dcerpc_binding_handle *b, - struct samr_Password current_nt_hash, - const struct samr_Password *previous_nt_hash) + uint8_t num_nt_hashes, + const struct samr_Password * const *nt_hashes) { struct tevent_req *req; struct netlogon_creds_cli_auth_state *state; @@ -985,11 +985,18 @@ struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx, state->ev = ev; state->context = context; state->binding_handle = b; - state->current_nt_hash = current_nt_hash; - if (previous_nt_hash != NULL) { - state->previous_nt_hash = *previous_nt_hash; - state->try_previous_nt_hash = true; + if (num_nt_hashes < 1) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); + return tevent_req_post(req, ev); } + if (num_nt_hashes > 4) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); + return tevent_req_post(req, ev); + } + + state->num_nt_hashes = num_nt_hashes; + state->idx_nt_hashes = 0; + state->nt_hashes = nt_hashes; if (context->db.locked_state != NULL) { tevent_req_nterror(req, NT_STATUS_LOCK_NOT_GRANTED); @@ -1020,7 +1027,7 @@ struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx, state->require_auth2 = true; } - state->used_nt_hash = state->current_nt_hash; + state->used_nt_hash = state->nt_hashes[state->idx_nt_hashes]; state->current_flags = context->client.proposed_flags; if (context->db.g_ctx != NULL) { @@ -1142,7 +1149,7 @@ static void netlogon_creds_cli_auth_challenge_done(struct tevent_req *subreq) state->context->client.type, &state->client_challenge, &state->server_challenge, - &state->used_nt_hash, + state->used_nt_hash, &state->client_credential, state->current_flags); if (tevent_req_nomem(state->creds, req)) { @@ -1284,7 +1291,8 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq) return; } - if (!state->try_previous_nt_hash) { + state->idx_nt_hashes += 1; + if (state->idx_nt_hashes >= state->num_nt_hashes) { /* * we already retried, giving up... */ @@ -1295,8 +1303,7 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq) /* * lets retry with the old nt hash. */ - state->try_previous_nt_hash = false; - state->used_nt_hash = state->previous_nt_hash; + state->used_nt_hash = state->nt_hashes[state->idx_nt_hashes]; state->current_flags = state->context->client.proposed_flags; netlogon_creds_cli_auth_challenge_start(req); return; @@ -1331,43 +1338,52 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq) tevent_req_done(req); } -NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req) +NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req, + uint8_t *idx_nt_hashes) { + struct netlogon_creds_cli_auth_state *state = + tevent_req_data(req, + struct netlogon_creds_cli_auth_state); NTSTATUS status; + *idx_nt_hashes = 0; + if (tevent_req_is_nterror(req, &status)) { tevent_req_received(req); return status; } + *idx_nt_hashes = state->idx_nt_hashes; tevent_req_received(req); return NT_STATUS_OK; } NTSTATUS netlogon_creds_cli_auth(struct netlogon_creds_cli_context *context, struct dcerpc_binding_handle *b, - struct samr_Password current_nt_hash, - const struct samr_Password *previous_nt_hash) + uint8_t num_nt_hashes, + const struct samr_Password * const *nt_hashes, + uint8_t *idx_nt_hashes) { TALLOC_CTX *frame = talloc_stackframe(); struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_NO_MEMORY; + *idx_nt_hashes = 0; + ev = samba_tevent_context_init(frame); if (ev == NULL) { goto fail; } req = netlogon_creds_cli_auth_send(frame, ev, context, b, - current_nt_hash, - previous_nt_hash); + num_nt_hashes, nt_hashes); if (req == NULL) { goto fail; } if (!tevent_req_poll_ntstatus(req, ev, &status)) { goto fail; } - status = netlogon_creds_cli_auth_recv(req); + status = netlogon_creds_cli_auth_recv(req, idx_nt_hashes); fail: TALLOC_FREE(frame); return status; diff --git a/libcli/auth/netlogon_creds_cli.h b/libcli/auth/netlogon_creds_cli.h index 7c737dd920a..dc274203d7d 100644 --- a/libcli/auth/netlogon_creds_cli.h +++ b/libcli/auth/netlogon_creds_cli.h @@ -84,13 +84,15 @@ 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); + uint8_t num_nt_hashes, + const struct samr_Password * const *nt_hashes); +NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req, + uint8_t *idx_nt_hashes); NTSTATUS netlogon_creds_cli_auth(struct netlogon_creds_cli_context *context, struct dcerpc_binding_handle *b, - struct samr_Password current_nt_hash, - const struct samr_Password *previous_nt_hash); + uint8_t num_nt_hashes, + const struct samr_Password * const *nt_hashes, + uint8_t *idx_nt_hashes); struct tevent_req *netlogon_creds_cli_check_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, diff --git a/source3/libsmb/trusts_util.c b/source3/libsmb/trusts_util.c index 47b79b73369..128beb73cc1 100644 --- a/source3/libsmb/trusts_util.c +++ b/source3/libsmb/trusts_util.c @@ -115,7 +115,9 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, struct trust_pw_change_state *state; struct cli_credentials *creds = NULL; const struct samr_Password *current_nt_hash = NULL; - const struct samr_Password *previous_nt_hash = NULL; + uint8_t num_nt_hashes = 0; + const struct samr_Password *nt_hashes[1] = { NULL, }; + uint8_t idx_nt_hashes = 0; enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL; time_t pass_last_set_time; uint32_t old_version = 0; @@ -245,6 +247,9 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, return NT_STATUS_NO_MEMORY; } + nt_hashes[0] = current_nt_hash; + num_nt_hashes = 1; + /* * We could use cli_credentials_get_old_nt_hash(creds, frame) to * set previous_nt_hash. @@ -259,8 +264,9 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, * local secrets before doing the change. */ status = netlogon_creds_cli_auth(context, b, - *current_nt_hash, - previous_nt_hash); + num_nt_hashes, + nt_hashes, + &idx_nt_hashes); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old password - %s!\n", context_name, nt_errstr(status))); @@ -349,9 +355,12 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, /* * Now we verify the new password. */ + nt_hashes[0] = current_nt_hash; + num_nt_hashes = 1; status = netlogon_creds_cli_auth(context, b, - *current_nt_hash, - NULL); /* previous_nt_hash */ + num_nt_hashes, + nt_hashes, + &idx_nt_hashes); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n", context_name, nt_errstr(status))); diff --git a/source3/rpc_client/cli_netlogon.c b/source3/rpc_client/cli_netlogon.c index 634c78bfe45..d17c6c0c1ae 100644 --- a/source3/rpc_client/cli_netlogon.c +++ b/source3/rpc_client/cli_netlogon.c @@ -160,6 +160,9 @@ NTSTATUS rpccli_setup_netlogon_creds(struct cli_state *cli, TALLOC_CTX *frame = talloc_stackframe(); struct rpc_pipe_client *netlogon_pipe = NULL; struct netlogon_creds_CredentialState *creds = NULL; + uint8_t num_nt_hashes = 0; + const struct samr_Password *nt_hashes[2] = { NULL, NULL }; + uint8_t idx_nt_hashes = 0; NTSTATUS status; status = netlogon_creds_cli_get(netlogon_creds, @@ -196,10 +199,18 @@ NTSTATUS rpccli_setup_netlogon_creds(struct cli_state *cli, } talloc_steal(frame, netlogon_pipe); + nt_hashes[0] = ¤t_nt_hash; + num_nt_hashes = 1; + if (previous_nt_hash != NULL) { + nt_hashes[1] = previous_nt_hash; + num_nt_hashes = 2; + } + status = netlogon_creds_cli_auth(netlogon_creds, netlogon_pipe->binding_handle, - current_nt_hash, - previous_nt_hash); + num_nt_hashes, + nt_hashes, + &idx_nt_hashes); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return status;