1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-03 01:18:10 +03:00

s4:librpc/rpc: use netr_LogonGetCapabilities query_level=2 to verify the proposed capabilities

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15425

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
(cherry picked from commit 0b6ac4b082)
This commit is contained in:
Stefan Metzmacher 2024-10-02 16:38:53 +02:00 committed by Jule Anger
parent 620065e13d
commit d0b2469385

View File

@ -40,6 +40,7 @@ struct schannel_key_state {
bool dcerpc_schannel_auto;
struct cli_credentials *credentials;
struct netlogon_creds_CredentialState *creds;
uint32_t requested_negotiate_flags;
uint32_t required_negotiate_flags;
uint32_t local_negotiate_flags;
uint32_t remote_negotiate_flags;
@ -56,7 +57,8 @@ static void continue_secondary_connection(struct composite_context *ctx);
static void continue_bind_auth_none(struct composite_context *ctx);
static void continue_srv_challenge(struct tevent_req *subreq);
static void continue_srv_auth2(struct tevent_req *subreq);
static void continue_get_capabilities(struct tevent_req *subreq);
static void continue_get_negotiated_capabilities(struct tevent_req *subreq);
static void continue_get_client_capabilities(struct tevent_req *subreq);
/*
@ -184,7 +186,7 @@ static void continue_srv_challenge(struct tevent_req *subreq)
s->a.in.secure_channel_type =
cli_credentials_get_secure_channel_type(s->credentials);
s->a.in.computer_name = cli_credentials_get_workstation(s->credentials);
s->a.in.negotiate_flags = &s->local_negotiate_flags;
s->a.in.negotiate_flags = &s->requested_negotiate_flags;
s->a.in.credentials = &s->credentials3;
s->a.out.negotiate_flags = &s->remote_negotiate_flags;
s->a.out.return_credentials = &s->credentials3;
@ -330,7 +332,22 @@ static void continue_srv_auth2(struct tevent_req *subreq)
return;
}
s->creds->negotiate_flags &= s->remote_negotiate_flags;
if (s->requested_negotiate_flags == s->local_negotiate_flags) {
/*
* Without a downgrade in the crypto we proposed
* we can adjust the otherwise downgraded flags
* before storing.
*/
s->creds->negotiate_flags &= s->remote_negotiate_flags;
} else if (s->local_negotiate_flags != s->remote_negotiate_flags) {
/*
* We downgraded our crypto once, we should not
* allow any additional downgrade!
*/
DBG_ERR("%s: NT_STATUS_DOWNGRADE_DETECTED\n", __location__);
composite_error(c, NT_STATUS_DOWNGRADE_DETECTED);
return;
}
composite_done(c);
}
@ -413,6 +430,8 @@ static struct composite_context *dcerpc_schannel_key_send(TALLOC_CTX *mem_ctx,
s->local_negotiate_flags |= NETLOGON_NEG_RODC_PASSTHROUGH;
}
s->requested_negotiate_flags = s->local_negotiate_flags;
epm_creds = cli_credentials_init_anon(s);
if (composite_nomem(epm_creds, c)) return c;
@ -438,7 +457,8 @@ static struct composite_context *dcerpc_schannel_key_send(TALLOC_CTX *mem_ctx,
*/
static NTSTATUS dcerpc_schannel_key_recv(struct composite_context *c,
TALLOC_CTX *mem_ctx,
struct netlogon_creds_CredentialState **creds)
struct netlogon_creds_CredentialState **creds,
uint32_t *requested_negotiate_flags)
{
NTSTATUS status = composite_wait(c);
@ -447,6 +467,7 @@ static NTSTATUS dcerpc_schannel_key_recv(struct composite_context *c,
talloc_get_type_abort(c->private_data,
struct schannel_key_state);
*creds = talloc_move(mem_ctx, &s->creds);
*requested_negotiate_flags = s->requested_negotiate_flags;
}
talloc_free(c);
@ -457,6 +478,7 @@ static NTSTATUS dcerpc_schannel_key_recv(struct composite_context *c,
struct auth_schannel_state {
struct dcerpc_pipe *pipe;
struct cli_credentials *credentials;
uint32_t requested_negotiate_flags;
const struct ndr_interface_table *table;
struct loadparm_context *lp_ctx;
uint8_t auth_level;
@ -465,6 +487,7 @@ struct auth_schannel_state {
struct netr_Authenticator auth;
struct netr_Authenticator return_auth;
union netr_Capabilities capabilities;
union netr_Capabilities client_caps;
struct netr_LogonGetCapabilities c;
union netr_CONTROL_QUERY_INFORMATION ctrl_info;
};
@ -487,7 +510,11 @@ static void continue_schannel_key(struct composite_context *ctx)
NTSTATUS status;
/* receive schannel key */
status = c->status = dcerpc_schannel_key_recv(ctx, s, &s->creds_state);
c->status = dcerpc_schannel_key_recv(ctx,
s,
&s->creds_state,
&s->requested_negotiate_flags);
status = c->status;
if (!composite_is_ok(c)) {
DEBUG(1, ("Failed to setup credentials: %s\n", nt_errstr(status)));
return;
@ -556,7 +583,9 @@ static void continue_bind_auth(struct composite_context *ctx)
&s->c);
if (composite_nomem(subreq, c)) return;
tevent_req_set_callback(subreq, continue_get_capabilities, c);
tevent_req_set_callback(subreq,
continue_get_negotiated_capabilities,
c);
return;
}
@ -568,10 +597,11 @@ static void continue_logon_control_do(struct composite_context *c);
/*
Stage 4 of auth_schannel: Get the Logon Capabilities and verify them.
*/
static void continue_get_capabilities(struct tevent_req *subreq)
static void continue_get_negotiated_capabilities(struct tevent_req *subreq)
{
struct composite_context *c;
struct auth_schannel_state *s;
NTSTATUS status;
c = tevent_req_callback_data(subreq, struct composite_context);
s = talloc_get_type(c->private_data, struct auth_schannel_state);
@ -635,7 +665,106 @@ static void continue_get_capabilities(struct tevent_req *subreq)
return;
}
/* TODO: Add downgrade detection. */
if ((s->requested_negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) &&
(!(s->creds_state->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES)))
{
DBG_ERR("%s: NT_STATUS_DOWNGRADE_DETECTED\n", __location__);
composite_error(c, NT_STATUS_DOWNGRADE_DETECTED);
return;
}
ZERO_STRUCT(s->return_auth);
s->save_creds_state = *s->creds_state;
status = netlogon_creds_client_authenticator(&s->save_creds_state,
&s->auth);
if (!NT_STATUS_IS_OK(status)) {
composite_error(c, status);
return;
}
s->c.in.credential = &s->auth;
s->c.in.return_authenticator = &s->return_auth;
s->c.in.query_level = 2;
s->c.out.capabilities = &s->client_caps;
s->c.out.return_authenticator = &s->return_auth;
subreq = dcerpc_netr_LogonGetCapabilities_r_send(s,
c->event_ctx,
s->pipe->binding_handle,
&s->c);
if (composite_nomem(subreq, c)) return;
tevent_req_set_callback(subreq, continue_get_client_capabilities, c);
return;
}
static void continue_get_client_capabilities(struct tevent_req *subreq)
{
struct composite_context *c;
struct auth_schannel_state *s;
c = tevent_req_callback_data(subreq, struct composite_context);
s = talloc_get_type(c->private_data, struct auth_schannel_state);
/* receive rpc request result */
c->status = dcerpc_netr_LogonGetCapabilities_r_recv(subreq, s);
TALLOC_FREE(subreq);
if (NT_STATUS_EQUAL(c->status, NT_STATUS_RPC_BAD_STUB_DATA)) {
/*
* unpatched Samba server, see
* https://bugzilla.samba.org/show_bug.cgi?id=15418
*/
c->status = NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE;
}
if (NT_STATUS_EQUAL(c->status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) {
/*
* Here we know the negotiated flags were already
* verified with query_level=1, which means
* the server supported NETLOGON_NEG_SUPPORTS_AES
* and also NETLOGON_NEG_AUTHENTICATED_RPC
*
* As we're using DCERPC_AUTH_TYPE_SCHANNEL with
* DCERPC_AUTH_LEVEL_INTEGRITY or DCERPC_AUTH_LEVEL_PRIVACY
* we should detect a faked
* NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE
* with the next request as the sequence number processing
* gets out of sync.
*
* So we'll do a LogonControl message to check that...
*/
continue_logon_control_do(c);
return;
}
if (!composite_is_ok(c)) {
return;
}
/* verify credentials */
if (!netlogon_creds_client_check(&s->save_creds_state,
&s->c.out.return_authenticator->cred)) {
composite_error(c, NT_STATUS_UNSUCCESSFUL);
return;
}
if (!NT_STATUS_IS_OK(s->c.out.result)) {
composite_error(c, s->c.out.result);
return;
}
/* compare capabilities */
if (s->requested_negotiate_flags != s->client_caps.requested_flags) {
DBG_ERR("The client requested capabilities did not reach"
"the server! local[0x%08X] remote[0x%08X]\n",
s->requested_negotiate_flags,
s->client_caps.requested_flags);
composite_error(c, NT_STATUS_DOWNGRADE_DETECTED);
return;
}
*s->creds_state = s->save_creds_state;
cli_credentials_set_netlogon_creds(s->credentials, s->creds_state);
composite_done(c);
}