mirror of
https://github.com/samba-team/samba.git
synced 2025-03-20 22:50:26 +03:00
CVE-2016-2110: auth/gensec: require spnego mechListMIC exchange for new_spnego backends
This used to work more or less before, but only for krb5 with the server finishing first. With NTLMSSP and new_spnego the client will finish first. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11644 Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Günther Deschner <gd@samba.org>
This commit is contained in:
parent
3673533644
commit
f32ad5ca49
@ -53,6 +53,11 @@ struct spnego_state {
|
||||
const char *neg_oid;
|
||||
|
||||
DATA_BLOB mech_types;
|
||||
size_t num_targs;
|
||||
bool mic_requested;
|
||||
bool needs_mic_sign;
|
||||
bool needs_mic_check;
|
||||
bool done_mic_check;
|
||||
|
||||
/*
|
||||
* The following is used to implement
|
||||
@ -416,6 +421,11 @@ static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_
|
||||
spnego_state->neg_oid = all_sec[i].oid;
|
||||
*unwrapped_out = data_blob_null;
|
||||
nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
/*
|
||||
* Indicate the downgrade and request a
|
||||
* mic.
|
||||
*/
|
||||
spnego_state->mic_requested = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -674,22 +684,27 @@ static NTSTATUS gensec_spnego_server_negTokenTarg(struct spnego_state *spnego_st
|
||||
/* compose reply */
|
||||
spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
|
||||
spnego_out.negTokenTarg.responseToken = unwrapped_out;
|
||||
spnego_out.negTokenTarg.mechListMIC = null_data_blob;
|
||||
spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
|
||||
spnego_out.negTokenTarg.supportedMech = NULL;
|
||||
|
||||
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
|
||||
spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
|
||||
if (spnego_state->mic_requested) {
|
||||
spnego_out.negTokenTarg.negResult = SPNEGO_REQUEST_MIC;
|
||||
spnego_state->mic_requested = false;
|
||||
} else {
|
||||
spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
|
||||
}
|
||||
spnego_state->state_position = SPNEGO_SERVER_TARG;
|
||||
} else if (NT_STATUS_IS_OK(nt_status)) {
|
||||
if (unwrapped_out.data) {
|
||||
spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
|
||||
}
|
||||
spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
|
||||
spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
|
||||
spnego_state->state_position = SPNEGO_DONE;
|
||||
} else {
|
||||
spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
|
||||
spnego_out.negTokenTarg.mechListMIC = null_data_blob;
|
||||
DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
|
||||
spnego_state->state_position = SPNEGO_DONE;
|
||||
}
|
||||
@ -700,6 +715,7 @@ static NTSTATUS gensec_spnego_server_negTokenTarg(struct spnego_state *spnego_st
|
||||
}
|
||||
|
||||
spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
|
||||
spnego_state->num_targs++;
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
@ -892,18 +908,57 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
spnego_state->num_targs++;
|
||||
|
||||
if (!spnego_state->sub_sec_security) {
|
||||
DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
|
||||
spnego_free_data(&spnego);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (spnego_state->needs_mic_check) {
|
||||
if (spnego.negTokenTarg.responseToken.length != 0) {
|
||||
DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
|
||||
spnego_free_data(&spnego);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
nt_status = gensec_check_packet(spnego_state->sub_sec_security,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
&spnego.negTokenTarg.mechListMIC);
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
spnego_state->needs_mic_check = false;
|
||||
spnego_state->done_mic_check = true;
|
||||
} else {
|
||||
DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
|
||||
nt_errstr(nt_status)));
|
||||
}
|
||||
goto server_response;
|
||||
}
|
||||
|
||||
nt_status = gensec_update_ev(spnego_state->sub_sec_security,
|
||||
out_mem_ctx, ev,
|
||||
spnego.negTokenTarg.responseToken,
|
||||
&unwrapped_out);
|
||||
if (NT_STATUS_IS_OK(nt_status) && spnego.negTokenTarg.mechListMIC.length > 0) {
|
||||
out_mem_ctx, ev,
|
||||
spnego.negTokenTarg.responseToken,
|
||||
&unwrapped_out);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
goto server_response;
|
||||
}
|
||||
|
||||
new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
|
||||
GENSEC_FEATURE_NEW_SPNEGO);
|
||||
if (spnego.negTokenTarg.mechListMIC.length > 0) {
|
||||
new_spnego = true;
|
||||
}
|
||||
|
||||
if (new_spnego) {
|
||||
spnego_state->needs_mic_check = true;
|
||||
spnego_state->needs_mic_sign = true;
|
||||
}
|
||||
|
||||
if (spnego.negTokenTarg.mechListMIC.length > 0) {
|
||||
nt_status = gensec_check_packet(spnego_state->sub_sec_security,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
@ -913,9 +968,14 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
|
||||
nt_errstr(nt_status)));
|
||||
goto server_response;
|
||||
}
|
||||
|
||||
spnego_state->needs_mic_check = false;
|
||||
spnego_state->done_mic_check = true;
|
||||
}
|
||||
if (NT_STATUS_IS_OK(nt_status) && new_spnego) {
|
||||
|
||||
if (spnego_state->needs_mic_sign) {
|
||||
nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
|
||||
out_mem_ctx,
|
||||
spnego_state->mech_types.data,
|
||||
@ -926,9 +986,16 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
|
||||
nt_errstr(nt_status)));
|
||||
goto server_response;
|
||||
}
|
||||
spnego_state->needs_mic_sign = false;
|
||||
}
|
||||
|
||||
if (spnego_state->needs_mic_check) {
|
||||
nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
}
|
||||
|
||||
server_response:
|
||||
nt_status = gensec_spnego_server_negTokenTarg(spnego_state,
|
||||
out_mem_ctx,
|
||||
nt_status,
|
||||
@ -942,7 +1009,8 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
|
||||
}
|
||||
case SPNEGO_CLIENT_TARG:
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
NTSTATUS nt_status = NT_STATUS_INTERNAL_ERROR;
|
||||
|
||||
if (!in.length) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
@ -964,11 +1032,17 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
spnego_state->num_targs++;
|
||||
|
||||
if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
|
||||
spnego_free_data(&spnego);
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
|
||||
if (spnego.negTokenTarg.negResult == SPNEGO_REQUEST_MIC) {
|
||||
spnego_state->mic_requested = true;
|
||||
}
|
||||
|
||||
/* Server didn't like our choice of mech, and chose something else */
|
||||
if (((spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_INCOMPLETE) ||
|
||||
(spnego.negTokenTarg.negResult == SPNEGO_REQUEST_MIC)) &&
|
||||
@ -995,64 +1069,143 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = gensec_update_ev(spnego_state->sub_sec_security,
|
||||
out_mem_ctx, ev,
|
||||
spnego.negTokenTarg.responseToken,
|
||||
&unwrapped_out);
|
||||
spnego_state->neg_oid = talloc_strdup(spnego_state, spnego.negTokenTarg.supportedMech);
|
||||
} else if (spnego_state->no_response_expected) {
|
||||
if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
|
||||
DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
|
||||
nt_status = NT_STATUS_INVALID_PARAMETER;
|
||||
} else if (spnego.negTokenTarg.responseToken.length) {
|
||||
DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
|
||||
nt_status = NT_STATUS_INVALID_PARAMETER;
|
||||
} else {
|
||||
nt_status = NT_STATUS_OK;
|
||||
}
|
||||
if (NT_STATUS_IS_OK(nt_status) && spnego.negTokenTarg.mechListMIC.length > 0) {
|
||||
nt_status = gensec_check_packet(spnego_state->sub_sec_security,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
&spnego.negTokenTarg.mechListMIC);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
|
||||
nt_errstr(nt_status)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bool new_spnego = false;
|
||||
spnego_state->neg_oid = talloc_strdup(spnego_state,
|
||||
spnego.negTokenTarg.supportedMech);
|
||||
if (spnego_state->neg_oid == NULL) {
|
||||
spnego_free_data(&spnego);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
};
|
||||
}
|
||||
|
||||
if (spnego.negTokenTarg.mechListMIC.length > 0) {
|
||||
if (spnego_state->no_response_expected) {
|
||||
spnego_state->needs_mic_check = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (spnego_state->needs_mic_check) {
|
||||
if (spnego.negTokenTarg.responseToken.length != 0) {
|
||||
DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
|
||||
spnego_free_data(&spnego);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
nt_status = gensec_check_packet(spnego_state->sub_sec_security,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
&spnego.negTokenTarg.mechListMIC);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
|
||||
nt_errstr(nt_status)));
|
||||
spnego_free_data(&spnego);
|
||||
return nt_status;
|
||||
}
|
||||
spnego_state->needs_mic_check = false;
|
||||
spnego_state->done_mic_check = true;
|
||||
goto client_response;
|
||||
}
|
||||
|
||||
if (!spnego_state->no_response_expected) {
|
||||
nt_status = gensec_update_ev(spnego_state->sub_sec_security,
|
||||
out_mem_ctx, ev,
|
||||
spnego.negTokenTarg.responseToken,
|
||||
&unwrapped_out);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)
|
||||
&& spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
|
||||
new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
|
||||
GENSEC_FEATURE_NEW_SPNEGO);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
goto client_response;
|
||||
}
|
||||
if (NT_STATUS_IS_OK(nt_status) && new_spnego) {
|
||||
nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
|
||||
out_mem_ctx,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
&mech_list_mic);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
|
||||
nt_errstr(nt_status)));
|
||||
|
||||
spnego_state->no_response_expected = true;
|
||||
} else {
|
||||
nt_status = NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (spnego_state->no_response_expected &&
|
||||
!spnego_state->done_mic_check)
|
||||
{
|
||||
bool new_spnego = false;
|
||||
|
||||
new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
|
||||
GENSEC_FEATURE_NEW_SPNEGO);
|
||||
|
||||
switch (spnego.negTokenTarg.negResult) {
|
||||
case SPNEGO_ACCEPT_COMPLETED:
|
||||
case SPNEGO_NONE_RESULT:
|
||||
if (spnego_state->num_targs == 1) {
|
||||
/*
|
||||
* the first exchange doesn't require
|
||||
* verification
|
||||
*/
|
||||
new_spnego = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case SPNEGO_ACCEPT_INCOMPLETE:
|
||||
case SPNEGO_REQUEST_MIC:
|
||||
if (spnego.negTokenTarg.mechListMIC.length > 0) {
|
||||
new_spnego = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (spnego_state->mic_requested) {
|
||||
bool sign;
|
||||
|
||||
sign = gensec_have_feature(spnego_state->sub_sec_security,
|
||||
GENSEC_FEATURE_SIGN);
|
||||
if (sign) {
|
||||
new_spnego = true;
|
||||
}
|
||||
}
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
spnego_state->no_response_expected = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_spnego) {
|
||||
spnego_state->needs_mic_check = true;
|
||||
spnego_state->needs_mic_sign = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (spnego.negTokenTarg.mechListMIC.length > 0) {
|
||||
nt_status = gensec_check_packet(spnego_state->sub_sec_security,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
&spnego.negTokenTarg.mechListMIC);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
|
||||
nt_errstr(nt_status)));
|
||||
spnego_free_data(&spnego);
|
||||
return nt_status;
|
||||
}
|
||||
spnego_state->needs_mic_check = false;
|
||||
spnego_state->done_mic_check = true;
|
||||
}
|
||||
|
||||
if (spnego_state->needs_mic_sign) {
|
||||
nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
|
||||
out_mem_ctx,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
spnego_state->mech_types.data,
|
||||
spnego_state->mech_types.length,
|
||||
&mech_list_mic);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
|
||||
nt_errstr(nt_status)));
|
||||
spnego_free_data(&spnego);
|
||||
return nt_status;
|
||||
}
|
||||
spnego_state->needs_mic_sign = false;
|
||||
}
|
||||
|
||||
if (spnego_state->needs_mic_check) {
|
||||
nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
}
|
||||
|
||||
client_response:
|
||||
spnego_free_data(&spnego);
|
||||
|
||||
if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
|
||||
@ -1076,6 +1229,7 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
spnego_state->num_targs++;
|
||||
spnego_state->state_position = SPNEGO_CLIENT_TARG;
|
||||
nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user