mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
libcli/auth: 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>
This commit is contained in:
parent
276137e950
commit
25a2105ca7
@ -1215,6 +1215,7 @@ struct netlogon_creds_cli_auth_state {
|
||||
struct netlogon_creds_CredentialState *creds;
|
||||
struct netr_Credential client_credential;
|
||||
struct netr_Credential server_credential;
|
||||
uint32_t negotiate_flags;
|
||||
uint32_t rid;
|
||||
bool try_auth3;
|
||||
bool try_auth2;
|
||||
@ -1360,6 +1361,17 @@ static void netlogon_creds_cli_auth_challenge_done(struct tevent_req *subreq)
|
||||
}
|
||||
|
||||
if (state->try_auth3) {
|
||||
/*
|
||||
* We always need to send our proposed flags,
|
||||
* even if state->current_flags we passed to
|
||||
* netlogon_creds_client_init() is already downgraded,
|
||||
*
|
||||
* An old server will just ignore the bits it doesn't
|
||||
* know about, but LogonGetCapabilities(level=2) will
|
||||
* report what we proposed.
|
||||
*/
|
||||
state->negotiate_flags = state->context->client.proposed_flags;
|
||||
|
||||
subreq = dcerpc_netr_ServerAuthenticate3_send(state, state->ev,
|
||||
state->binding_handle,
|
||||
state->srv_name_slash,
|
||||
@ -1368,12 +1380,22 @@ static void netlogon_creds_cli_auth_challenge_done(struct tevent_req *subreq)
|
||||
state->context->client.computer,
|
||||
&state->client_credential,
|
||||
&state->server_credential,
|
||||
&state->creds->negotiate_flags,
|
||||
&state->negotiate_flags,
|
||||
&state->rid);
|
||||
if (tevent_req_nomem(subreq, req)) {
|
||||
return;
|
||||
}
|
||||
} else if (state->try_auth2) {
|
||||
/*
|
||||
* We always need to send our proposed flags,
|
||||
* even if state->current_flags we passed to
|
||||
* netlogon_creds_client_init() is already downgraded,
|
||||
*
|
||||
* An old server will just ignore the bits it doesn't
|
||||
* know about, but LogonGetCapabilities(level=2) will
|
||||
* report what we proposed.
|
||||
*/
|
||||
state->negotiate_flags = state->context->client.proposed_flags;
|
||||
state->rid = 0;
|
||||
|
||||
subreq = dcerpc_netr_ServerAuthenticate2_send(state, state->ev,
|
||||
@ -1384,11 +1406,12 @@ static void netlogon_creds_cli_auth_challenge_done(struct tevent_req *subreq)
|
||||
state->context->client.computer,
|
||||
&state->client_credential,
|
||||
&state->server_credential,
|
||||
&state->creds->negotiate_flags);
|
||||
&state->negotiate_flags);
|
||||
if (tevent_req_nomem(subreq, req)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
state->negotiate_flags = 0;
|
||||
state->rid = 0;
|
||||
|
||||
subreq = dcerpc_netr_ServerAuthenticate_send(state, state->ev,
|
||||
@ -1467,7 +1490,7 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq)
|
||||
}
|
||||
|
||||
downgraded = netlogon_creds_cli_downgraded(
|
||||
state->creds->negotiate_flags,
|
||||
state->negotiate_flags,
|
||||
state->context->client.proposed_flags,
|
||||
state->context->client.required_flags);
|
||||
if (downgraded) {
|
||||
@ -1482,7 +1505,7 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq)
|
||||
if (NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
|
||||
uint32_t prop_f = state->context->client.proposed_flags;
|
||||
uint32_t cli_f = state->current_flags;
|
||||
uint32_t srv_f = state->creds->negotiate_flags;
|
||||
uint32_t srv_f = state->negotiate_flags;
|
||||
uint32_t nego_f = cli_f & srv_f;
|
||||
|
||||
if (cli_f == prop_f && nego_f != prop_f) {
|
||||
@ -1519,6 +1542,22 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq)
|
||||
return;
|
||||
}
|
||||
|
||||
if (state->current_flags == state->context->client.proposed_flags) {
|
||||
/*
|
||||
* Without a downgrade in the crypto we proposed
|
||||
* we can adjust the otherwise downgraded flags
|
||||
* before storing.
|
||||
*/
|
||||
state->creds->negotiate_flags &= state->negotiate_flags;
|
||||
} else if (state->current_flags != state->negotiate_flags) {
|
||||
/*
|
||||
* We downgraded our crypto once, we should not
|
||||
* allow any additional downgrade!
|
||||
*/
|
||||
tevent_req_nterror(req, NT_STATUS_DOWNGRADE_DETECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
status = netlogon_creds_cli_store_internal(state->context,
|
||||
state->creds);
|
||||
if (tevent_req_nterror(req, status)) {
|
||||
@ -1587,6 +1626,7 @@ struct netlogon_creds_cli_check_state {
|
||||
char *srv_name_slash;
|
||||
|
||||
union netr_Capabilities caps;
|
||||
union netr_Capabilities client_caps;
|
||||
|
||||
struct netlogon_creds_CredentialState *creds;
|
||||
struct netr_Authenticator req_auth;
|
||||
@ -1597,7 +1637,8 @@ struct netlogon_creds_cli_check_state {
|
||||
|
||||
static void netlogon_creds_cli_check_cleanup(struct tevent_req *req,
|
||||
NTSTATUS status);
|
||||
static void netlogon_creds_cli_check_caps(struct tevent_req *subreq);
|
||||
static void netlogon_creds_cli_check_negotiate_caps(struct tevent_req *subreq);
|
||||
static void netlogon_creds_cli_check_client_caps(struct tevent_req *subreq);
|
||||
|
||||
struct tevent_req *netlogon_creds_cli_check_send(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev,
|
||||
@ -1681,7 +1722,7 @@ struct tevent_req *netlogon_creds_cli_check_send(TALLOC_CTX *mem_ctx,
|
||||
}
|
||||
|
||||
tevent_req_set_callback(subreq,
|
||||
netlogon_creds_cli_check_caps,
|
||||
netlogon_creds_cli_check_negotiate_caps,
|
||||
req);
|
||||
|
||||
return req;
|
||||
@ -1713,7 +1754,7 @@ static void netlogon_creds_cli_check_cleanup(struct tevent_req *req,
|
||||
|
||||
static void netlogon_creds_cli_check_control_do(struct tevent_req *req);
|
||||
|
||||
static void netlogon_creds_cli_check_caps(struct tevent_req *subreq)
|
||||
static void netlogon_creds_cli_check_negotiate_caps(struct tevent_req *subreq)
|
||||
{
|
||||
struct tevent_req *req =
|
||||
tevent_req_callback_data(subreq,
|
||||
@ -1856,6 +1897,110 @@ static void netlogon_creds_cli_check_caps(struct tevent_req *subreq)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now try to verify our client proposed flags
|
||||
* arrived at the server, using query_level = 2
|
||||
*/
|
||||
|
||||
status = netlogon_creds_client_authenticator(state->creds,
|
||||
&state->req_auth);
|
||||
if (tevent_req_nterror(req, status)) {
|
||||
return;
|
||||
}
|
||||
ZERO_STRUCT(state->rep_auth);
|
||||
|
||||
subreq = dcerpc_netr_LogonGetCapabilities_send(state, state->ev,
|
||||
state->binding_handle,
|
||||
state->srv_name_slash,
|
||||
state->context->client.computer,
|
||||
&state->req_auth,
|
||||
&state->rep_auth,
|
||||
2,
|
||||
&state->client_caps);
|
||||
if (tevent_req_nomem(subreq, req)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tevent_req_set_callback(subreq,
|
||||
netlogon_creds_cli_check_client_caps,
|
||||
req);
|
||||
return;
|
||||
}
|
||||
|
||||
static void netlogon_creds_cli_check_client_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_BAD_STUB_DATA)) {
|
||||
/*
|
||||
* unpatched Samba server, see
|
||||
* https://bugzilla.samba.org/show_bug.cgi?id=15418
|
||||
*/
|
||||
status = NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
if (NT_STATUS_EQUAL(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...
|
||||
*/
|
||||
netlogon_creds_cli_check_control_do(req);
|
||||
return;
|
||||
}
|
||||
if (tevent_req_nterror(req, status)) {
|
||||
netlogon_creds_cli_check_cleanup(req, status);
|
||||
return;
|
||||
}
|
||||
|
||||
ok = netlogon_creds_client_check(state->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->client_caps.requested_flags !=
|
||||
state->context->client.proposed_flags)
|
||||
{
|
||||
status = NT_STATUS_DOWNGRADE_DETECTED;
|
||||
tevent_req_nterror(req, status);
|
||||
netlogon_creds_cli_check_cleanup(req, status);
|
||||
return;
|
||||
}
|
||||
|
||||
status = netlogon_creds_cli_store_internal(state->context,
|
||||
state->creds);
|
||||
if (tevent_req_nterror(req, status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tevent_req_done(req);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user