diff options
author | Stefan Metzmacher <metze@samba.org> | 2020-11-16 14:15:06 +0100 |
---|---|---|
committer | Jule Anger <janger@samba.org> | 2021-11-08 10:46:45 +0100 |
commit | e6a1fbbf605bef900954d1cd120ac1abce0bb7e9 (patch) | |
tree | 258c76c581b378a3281bdf0ca5a525cfc49a48ef /librpc | |
parent | 396b19acac7771ea4d3cbcaa050cecbbc0ddbf81 (diff) | |
download | samba-e6a1fbbf605bef900954d1cd120ac1abce0bb7e9.tar.gz |
CVE-2021-23192: dcesrv_core: only the first fragment specifies the auth_contexts
All other fragments blindly inherit it.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14875
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Samuel Cabrero <scabrero@samba.org>
Diffstat (limited to 'librpc')
-rw-r--r-- | librpc/rpc/dcerpc_pkt_auth.c | 19 | ||||
-rw-r--r-- | librpc/rpc/dcerpc_pkt_auth.h | 1 | ||||
-rw-r--r-- | librpc/rpc/dcesrv_auth.c | 28 | ||||
-rw-r--r-- | librpc/rpc/dcesrv_core.c | 86 |
4 files changed, 108 insertions, 26 deletions
diff --git a/librpc/rpc/dcerpc_pkt_auth.c b/librpc/rpc/dcerpc_pkt_auth.c index 322d7497893..1cb191468b5 100644 --- a/librpc/rpc/dcerpc_pkt_auth.c +++ b/librpc/rpc/dcerpc_pkt_auth.c @@ -39,6 +39,7 @@ NTSTATUS dcerpc_ncacn_pull_pkt_auth(const struct dcerpc_auth *auth_state, struct gensec_security *gensec, + bool check_pkt_auth_fields, TALLOC_CTX *mem_ctx, enum dcerpc_pkt_type ptype, uint8_t required_flags, @@ -115,16 +116,18 @@ NTSTATUS dcerpc_ncacn_pull_pkt_auth(const struct dcerpc_auth *auth_state, return NT_STATUS_INTERNAL_ERROR; } - if (auth.auth_type != auth_state->auth_type) { - return NT_STATUS_ACCESS_DENIED; - } + if (check_pkt_auth_fields) { + if (auth.auth_type != auth_state->auth_type) { + return NT_STATUS_ACCESS_DENIED; + } - if (auth.auth_level != auth_state->auth_level) { - return NT_STATUS_ACCESS_DENIED; - } + if (auth.auth_level != auth_state->auth_level) { + return NT_STATUS_ACCESS_DENIED; + } - if (auth.auth_context_id != auth_state->auth_context_id) { - return NT_STATUS_ACCESS_DENIED; + if (auth.auth_context_id != auth_state->auth_context_id) { + return NT_STATUS_ACCESS_DENIED; + } } /* check signature or unseal the packet */ diff --git a/librpc/rpc/dcerpc_pkt_auth.h b/librpc/rpc/dcerpc_pkt_auth.h index c0d23b91c05..1dcee12f53c 100644 --- a/librpc/rpc/dcerpc_pkt_auth.h +++ b/librpc/rpc/dcerpc_pkt_auth.h @@ -31,6 +31,7 @@ NTSTATUS dcerpc_ncacn_pull_pkt_auth(const struct dcerpc_auth *auth_state, struct gensec_security *gensec, + bool check_pkt_auth_fields, TALLOC_CTX *mem_ctx, enum dcerpc_pkt_type ptype, uint8_t required_flags, diff --git a/librpc/rpc/dcesrv_auth.c b/librpc/rpc/dcesrv_auth.c index 8dda86d88e2..9d8df6c42e2 100644 --- a/librpc/rpc/dcesrv_auth.c +++ b/librpc/rpc/dcesrv_auth.c @@ -438,6 +438,10 @@ bool dcesrv_auth_prepare_auth3(struct dcesrv_call_state *call) return false; } + if (auth->auth_invalid) { + return false; + } + /* We can't work without an existing gensec state */ if (auth->gensec_security == NULL) { return false; @@ -524,6 +528,10 @@ bool dcesrv_auth_alter(struct dcesrv_call_state *call) return false; } + if (auth->auth_invalid) { + return false; + } + if (call->in_auth_info.auth_type != auth->auth_type) { return false; } @@ -590,6 +598,7 @@ bool dcesrv_auth_pkt_pull(struct dcesrv_call_state *call, .auth_level = auth->auth_level, .auth_context_id = auth->auth_context_id, }; + bool check_pkt_auth_fields; NTSTATUS status; if (!auth->auth_started) { @@ -605,8 +614,27 @@ bool dcesrv_auth_pkt_pull(struct dcesrv_call_state *call, return false; } + if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST) { + /* + * The caller most likely checked this + * already, but we better double check. + */ + check_pkt_auth_fields = true; + } else { + /* + * The caller already found first fragment + * and is passing the auth_state of it. + * A server is supposed to use the + * setting of the first fragment and + * completely ignore the values + * on the remaining fragments + */ + check_pkt_auth_fields = false; + } + status = dcerpc_ncacn_pull_pkt_auth(&tmp_auth, auth->gensec_security, + check_pkt_auth_fields, call, pkt->ptype, required_flags, diff --git a/librpc/rpc/dcesrv_core.c b/librpc/rpc/dcesrv_core.c index ffaa9019d4b..0bab4c10641 100644 --- a/librpc/rpc/dcesrv_core.c +++ b/librpc/rpc/dcesrv_core.c @@ -1805,6 +1805,10 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) struct ndr_pull *pull; NTSTATUS status; + if (auth->auth_invalid) { + return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); + } + if (!auth->auth_finished) { return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); } @@ -1968,6 +1972,7 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, enum dcerpc_AuthType auth_type = 0; enum dcerpc_AuthLevel auth_level = 0; uint32_t auth_context_id = 0; + bool auth_invalid = false; call = talloc_zero(dce_conn, struct dcesrv_call_state); if (!call) { @@ -1999,12 +2004,16 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, if (call->auth_state == NULL) { struct dcesrv_auth *a = NULL; + bool check_type_level = true; auth_type = dcerpc_get_auth_type(&blob); auth_level = dcerpc_get_auth_level(&blob); auth_context_id = dcerpc_get_auth_context_id(&blob); if (call->pkt.ptype == DCERPC_PKT_REQUEST) { + if (!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) { + check_type_level = false; + } dce_conn->default_auth_level_connect = NULL; if (auth_level == DCERPC_AUTH_LEVEL_CONNECT) { dce_conn->got_explicit_auth_level_connect = true; @@ -2014,14 +2023,19 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, for (a = dce_conn->auth_states; a != NULL; a = a->next) { num_auth_ctx++; - if (a->auth_type != auth_type) { + if (a->auth_context_id != auth_context_id) { continue; } - if (a->auth_finished && a->auth_level != auth_level) { - continue; + + if (a->auth_type != auth_type) { + auth_invalid = true; } - if (a->auth_context_id != auth_context_id) { - continue; + if (a->auth_level != auth_level) { + auth_invalid = true; + } + + if (check_type_level && auth_invalid) { + a->auth_invalid = true; } DLIST_PROMOTE(dce_conn->auth_states, a); @@ -2048,6 +2062,7 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, /* * This can never be valid. */ + auth_invalid = true; a->auth_invalid = true; } call->auth_state = a; @@ -2116,6 +2131,18 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, } /* only one request is possible in the fragmented list */ if (dce_conn->incoming_fragmented_call_list != NULL) { + call->fault_code = DCERPC_NCA_S_PROTO_ERROR; + + existing = dcesrv_find_fragmented_call(dce_conn, + call->pkt.call_id); + if (existing != NULL && call->auth_state != existing->auth_state) { + call->context = dcesrv_find_context(call->conn, + call->pkt.u.request.context_id); + + if (call->pkt.auth_length != 0 && existing->context == call->context) { + call->fault_code = DCERPC_FAULT_SEC_PKG_ERROR; + } + } if (!(dce_conn->state_flags & DCESRV_CALL_STATE_FLAG_MULTIPLEXED)) { /* * Without DCERPC_PFC_FLAG_CONC_MPX @@ -2125,11 +2152,14 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, * This is important to get the * call_id and context_id right. */ + dce_conn->incoming_fragmented_call_list->fault_code = call->fault_code; TALLOC_FREE(call); call = dce_conn->incoming_fragmented_call_list; } - return dcesrv_fault_disconnect0(call, - DCERPC_NCA_S_PROTO_ERROR); + if (existing != NULL) { + call->context = existing->context; + } + return dcesrv_fault_disconnect0(call, call->fault_code); } if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_PENDING_CANCEL) { return dcesrv_fault_disconnect(call, @@ -2142,17 +2172,43 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, DCERPC_PFC_FLAG_DID_NOT_EXECUTE); } } else { - const struct dcerpc_request *nr = &call->pkt.u.request; - const struct dcerpc_request *er = NULL; int cmp; existing = dcesrv_find_fragmented_call(dce_conn, call->pkt.call_id); if (existing == NULL) { + if (!(dce_conn->state_flags & DCESRV_CALL_STATE_FLAG_MULTIPLEXED)) { + /* + * Without DCERPC_PFC_FLAG_CONC_MPX + * we need to return the FAULT on the + * already existing call. + * + * This is important to get the + * call_id and context_id right. + */ + if (dce_conn->incoming_fragmented_call_list != NULL) { + TALLOC_FREE(call); + call = dce_conn->incoming_fragmented_call_list; + } + return dcesrv_fault_disconnect0(call, + DCERPC_NCA_S_PROTO_ERROR); + } + if (dce_conn->incoming_fragmented_call_list != NULL) { + return dcesrv_fault_disconnect0(call, DCERPC_NCA_S_PROTO_ERROR); + } + call->context = dcesrv_find_context(call->conn, + call->pkt.u.request.context_id); + if (call->context == NULL) { + return dcesrv_fault_with_flags(call, DCERPC_NCA_S_UNKNOWN_IF, + DCERPC_PFC_FLAG_DID_NOT_EXECUTE); + } + if (auth_invalid) { + return dcesrv_fault_disconnect0(call, + DCERPC_FAULT_ACCESS_DENIED); + } return dcesrv_fault_disconnect0(call, DCERPC_NCA_S_PROTO_ERROR); } - er = &existing->pkt.u.request; if (call->pkt.ptype != existing->pkt.ptype) { /* trying to play silly buggers are we? */ @@ -2165,14 +2221,8 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, return dcesrv_fault_disconnect(existing, DCERPC_NCA_S_PROTO_ERROR); } - if (nr->context_id != er->context_id) { - return dcesrv_fault_disconnect(existing, - DCERPC_NCA_S_PROTO_ERROR); - } - if (nr->opnum != er->opnum) { - return dcesrv_fault_disconnect(existing, - DCERPC_NCA_S_PROTO_ERROR); - } + call->auth_state = existing->auth_state; + call->context = existing->context; } } |