From 2ed603a3780dfd246f8c3cd2718f0561f77ca4be Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 26 Jun 2015 08:10:46 +0200 Subject: [PATCH] CVE-2015-5370: s4:rpc_server: verify the protocol headers before processing pdus MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit On protocol errors we should send BIND_NAK or FAULT and mark the connection as to be terminated. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11344 Signed-off-by: Stefan Metzmacher Reviewed-by: Günther Deschner --- source4/rpc_server/dcerpc_server.c | 106 +++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index 6b629e0b1e4..60b5b8374c4 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -452,6 +452,18 @@ static void dcesrv_call_set_list(struct dcesrv_call_state *call, } } +static void dcesrv_call_disconnect_after(struct dcesrv_call_state *call, + const char *reason) +{ + if (call->conn->terminate != NULL) { + return; + } + + call->terminate_reason = talloc_strdup(call, reason); + if (call->terminate_reason == NULL) { + call->terminate_reason = __location__; + } +} /* return a dcerpc bind_nak @@ -464,6 +476,12 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason) NTSTATUS status; static const uint8_t _pad[3] = { 0, }; + /* + * We add the call to the pending_call_list + * in order to defer the termination. + */ + dcesrv_call_disconnect_after(call, "dcesrv_bind_nak"); + /* setup a bind_nak */ dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)); pkt.auth_length = 0; @@ -501,6 +519,19 @@ static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason) return NT_STATUS_OK; } +static NTSTATUS dcesrv_fault_disconnect(struct dcesrv_call_state *call, + uint32_t fault_code) +{ + /* + * We add the call to the pending_call_list + * in order to defer the termination. + */ + dcesrv_call_disconnect_after(call, "dcesrv_fault_disconnect"); + + return dcesrv_fault_with_flags(call, fault_code, + DCERPC_PFC_FLAG_DID_NOT_EXECUTE); +} + static int dcesrv_connection_context_destructor(struct dcesrv_connection_context *c) { DLIST_REMOVE(c->conn->contexts, c); @@ -641,6 +672,23 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) const char *ep_prefix = ""; const char *endpoint = NULL; + status = dcerpc_verify_ncacn_packet_header(&call->pkt, + DCERPC_PKT_BIND, + call->pkt.u.bind.auth_info.length, + 0, /* required flags */ + DCERPC_PFC_FLAG_FIRST | + DCERPC_PFC_FLAG_LAST | + DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN | + 0x08 | /* this is not defined, but should be ignored */ + DCERPC_PFC_FLAG_CONC_MPX | + DCERPC_PFC_FLAG_DID_NOT_EXECUTE | + DCERPC_PFC_FLAG_MAYBE | + DCERPC_PFC_FLAG_OBJECT_UUID); + if (!NT_STATUS_IS_OK(status)) { + return dcesrv_bind_nak(call, + DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED); + } + /* max_recv_frag and max_xmit_frag result always in the same value! */ max_req = MIN(call->pkt.u.bind.max_xmit_frag, call->pkt.u.bind.max_recv_frag); @@ -864,6 +912,24 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) */ static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call) { + NTSTATUS status; + + status = dcerpc_verify_ncacn_packet_header(&call->pkt, + DCERPC_PKT_AUTH3, + call->pkt.u.auth3.auth_info.length, + 0, /* required flags */ + DCERPC_PFC_FLAG_FIRST | + DCERPC_PFC_FLAG_LAST | + DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN | + 0x08 | /* this is not defined, but should be ignored */ + DCERPC_PFC_FLAG_CONC_MPX | + DCERPC_PFC_FLAG_DID_NOT_EXECUTE | + DCERPC_PFC_FLAG_MAYBE | + DCERPC_PFC_FLAG_OBJECT_UUID); + if (!NT_STATUS_IS_OK(status)) { + return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); + } + /* handle the auth3 in the auth code */ if (!dcesrv_auth_auth3(call)) { return dcesrv_fault(call, DCERPC_FAULT_OTHER); @@ -1033,6 +1099,22 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call) NTSTATUS status; uint32_t context_id; + status = dcerpc_verify_ncacn_packet_header(&call->pkt, + DCERPC_PKT_ALTER, + call->pkt.u.alter.auth_info.length, + 0, /* required flags */ + DCERPC_PFC_FLAG_FIRST | + DCERPC_PFC_FLAG_LAST | + DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN | + 0x08 | /* this is not defined, but should be ignored */ + DCERPC_PFC_FLAG_CONC_MPX | + DCERPC_PFC_FLAG_DID_NOT_EXECUTE | + DCERPC_PFC_FLAG_MAYBE | + DCERPC_PFC_FLAG_OBJECT_UUID); + if (!NT_STATUS_IS_OK(status)) { + return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR); + } + /* handle any authentication that is being requested */ if (!dcesrv_auth_alter(call)) { /* TODO: work out the right reject code */ @@ -1310,9 +1392,27 @@ static NTSTATUS dcesrv_process_ncacn_packet(struct dcesrv_connection *dce_conn, /* we have to check the signing here, before combining the pdus */ - if (call->pkt.ptype == DCERPC_PKT_REQUEST && - !dcesrv_auth_request(call, &blob)) { - return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED); + if (call->pkt.ptype == DCERPC_PKT_REQUEST) { + status = dcerpc_verify_ncacn_packet_header(&call->pkt, + DCERPC_PKT_REQUEST, + call->pkt.u.request.stub_and_verifier.length, + 0, /* required_flags */ + DCERPC_PFC_FLAG_FIRST | + DCERPC_PFC_FLAG_LAST | + DCERPC_PFC_FLAG_PENDING_CANCEL | + 0x08 | /* this is not defined, but should be ignored */ + DCERPC_PFC_FLAG_CONC_MPX | + DCERPC_PFC_FLAG_DID_NOT_EXECUTE | + DCERPC_PFC_FLAG_MAYBE | + DCERPC_PFC_FLAG_OBJECT_UUID); + if (!NT_STATUS_IS_OK(status)) { + return dcesrv_fault_disconnect(call, + DCERPC_NCA_S_PROTO_ERROR); + } + + if (!dcesrv_auth_request(call, &blob)) { + return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED); + } } /* see if this is a continued packet */ -- 2.11.4.GIT