CVE-2013-4408:s3:Ensure we always check call_id when validating an RPC reply.
authorJeremy Allison <jra@samba.org>
Tue, 22 Oct 2013 22:34:12 +0000 (15:34 -0700)
committerKarolin Seeger <kseeger@samba.org>
Thu, 5 Dec 2013 10:11:52 +0000 (11:11 +0100)
Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
librpc/idl/dcerpc.idl
librpc/rpc/dcerpc_util.c
librpc/rpc/rpc_common.h
source3/rpc_client/cli_pipe.c

index 86f22a4b8c8a2c8361f78059b07220e80d1cd9b8..8949836408198e10e3a512d7634653b86f73a904 100644 (file)
@@ -467,6 +467,7 @@ interface dcerpc
        const uint8 DCERPC_DREP_OFFSET     =  4;
        const uint8 DCERPC_FRAG_LEN_OFFSET =  8;
        const uint8 DCERPC_AUTH_LEN_OFFSET = 10;
+       const uint8 DCERPC_CALL_ID_OFFSET  = 12;
 
        /* little-endian flag */
        const uint8 DCERPC_DREP_LE  = 0x10;
index b8bf64d655e9e40a75ef4bf7165861e5ab634dfb..cb21312152f137cec840dee811f1eef499709fab 100644 (file)
@@ -48,6 +48,15 @@ uint16_t dcerpc_get_frag_length(const DATA_BLOB *blob)
        }
 }
 
+uint32_t dcerpc_get_call_id(const DATA_BLOB *blob)
+{
+       if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
+               return IVAL(blob->data, DCERPC_CALL_ID_OFFSET);
+       } else {
+               return RIVAL(blob->data, DCERPC_CALL_ID_OFFSET);
+       }
+}
+
 void dcerpc_set_auth_length(DATA_BLOB *blob, uint16_t v)
 {
        if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
index 44c3cfd39b58a652e6c3bbe51fdf89281f6352c3..924645dd8966618eb749be2e43b9c2da9d56b854 100644 (file)
@@ -135,6 +135,7 @@ enum dcerpc_transport_t dcerpc_transport_by_tower(const struct epm_tower *tower)
 
 void dcerpc_set_frag_length(DATA_BLOB *blob, uint16_t v);
 uint16_t dcerpc_get_frag_length(const DATA_BLOB *blob);
+uint32_t dcerpc_get_call_id(const DATA_BLOB *blob);
 void dcerpc_set_auth_length(DATA_BLOB *blob, uint16_t v);
 uint8_t dcerpc_get_endian_flag(DATA_BLOB *blob);
 
index 57dcddc66d80238bde05e17fb1ee59962695bae0..96364793de3aa34719ebae9342b699f14f87a356 100644 (file)
@@ -235,6 +235,7 @@ struct get_complete_frag_state {
        struct event_context *ev;
        struct rpc_pipe_client *cli;
        uint16_t frag_len;
+       uint32_t call_id;
        DATA_BLOB *pdu;
 };
 
@@ -244,6 +245,7 @@ static void get_complete_frag_got_rest(struct tevent_req *subreq);
 static struct tevent_req *get_complete_frag_send(TALLOC_CTX *mem_ctx,
                                                 struct event_context *ev,
                                                 struct rpc_pipe_client *cli,
+                                                uint32_t call_id,
                                                 DATA_BLOB *pdu)
 {
        struct tevent_req *req, *subreq;
@@ -259,6 +261,7 @@ static struct tevent_req *get_complete_frag_send(TALLOC_CTX *mem_ctx,
        state->ev = ev;
        state->cli = cli;
        state->frag_len = RPC_HEADER_LEN;
+       state->call_id = call_id;
        state->pdu = pdu;
 
        received = pdu->length;
@@ -286,6 +289,11 @@ static struct tevent_req *get_complete_frag_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (state->call_id != dcerpc_get_call_id(pdu)) {
+               tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
+               return tevent_req_post(req, ev);
+       }
+
        /*
         * Ensure we have frag_len bytes of data.
         */
@@ -338,6 +346,11 @@ static void get_complete_frag_got_header(struct tevent_req *subreq)
                return;
        }
 
+       if (state->call_id != dcerpc_get_call_id(state->pdu)) {
+               tevent_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR);
+               return;
+       }
+
        if (!data_blob_realloc(NULL, state->pdu, state->frag_len)) {
                tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
                return;
@@ -698,6 +711,7 @@ struct rpc_api_pipe_state {
        struct event_context *ev;
        struct rpc_pipe_client *cli;
        uint8_t expected_pkt_type;
+       uint32_t call_id;
 
        DATA_BLOB incoming_frag;
        struct ncacn_packet *pkt;
@@ -716,7 +730,8 @@ static struct tevent_req *rpc_api_pipe_send(TALLOC_CTX *mem_ctx,
                                            struct event_context *ev,
                                            struct rpc_pipe_client *cli,
                                            DATA_BLOB *data, /* Outgoing PDU */
-                                           uint8_t expected_pkt_type)
+                                           uint8_t expected_pkt_type,
+                                           uint32_t call_id)
 {
        struct tevent_req *req, *subreq;
        struct rpc_api_pipe_state *state;
@@ -730,6 +745,7 @@ static struct tevent_req *rpc_api_pipe_send(TALLOC_CTX *mem_ctx,
        state->ev = ev;
        state->cli = cli;
        state->expected_pkt_type = expected_pkt_type;
+       state->call_id = call_id;
        state->incoming_frag = data_blob_null;
        state->reply_pdu = data_blob_null;
        state->reply_pdu_offset = 0;
@@ -829,6 +845,7 @@ static void rpc_api_pipe_trans_done(struct tevent_req *subreq)
 
        /* Ensure we have enough data for a pdu. */
        subreq = get_complete_frag_send(state, state->ev, state->cli,
+                                       state->call_id,
                                        &state->incoming_frag);
        if (tevent_req_nomem(subreq, req)) {
                return;
@@ -948,6 +965,7 @@ static void rpc_api_pipe_got_pdu(struct tevent_req *subreq)
        }
 
        subreq = get_complete_frag_send(state, state->ev, state->cli,
+                                       state->call_id,
                                        &state->incoming_frag);
        if (tevent_req_nomem(subreq, req)) {
                return;
@@ -1300,7 +1318,8 @@ struct tevent_req *rpc_api_pipe_req_send(TALLOC_CTX *mem_ctx,
        if (is_last_frag) {
                subreq = rpc_api_pipe_send(state, ev, state->cli,
                                           &state->rpc_out,
-                                          DCERPC_PKT_RESPONSE);
+                                          DCERPC_PKT_RESPONSE,
+                                          state->call_id);
                if (subreq == NULL) {
                        goto fail;
                }
@@ -1436,7 +1455,8 @@ static void rpc_api_pipe_req_write_done(struct tevent_req *subreq)
        if (is_last_frag) {
                subreq = rpc_api_pipe_send(state, state->ev, state->cli,
                                           &state->rpc_out,
-                                          DCERPC_PKT_RESPONSE);
+                                          DCERPC_PKT_RESPONSE,
+                                          state->call_id);
                if (tevent_req_nomem(subreq, req)) {
                        return;
                }
@@ -1675,7 +1695,7 @@ struct tevent_req *rpc_pipe_bind_send(TALLOC_CTX *mem_ctx,
        }
 
        subreq = rpc_api_pipe_send(state, ev, cli, &state->rpc_out,
-                                  DCERPC_PKT_BIND_ACK);
+                                  DCERPC_PKT_BIND_ACK, state->rpc_call_id);
        if (subreq == NULL) {
                goto fail;
        }
@@ -1873,7 +1893,8 @@ static NTSTATUS rpc_bind_next_send(struct tevent_req *req,
        }
 
        subreq = rpc_api_pipe_send(state, state->ev, state->cli,
-                                  &state->rpc_out, DCERPC_PKT_ALTER_RESP);
+                                  &state->rpc_out, DCERPC_PKT_ALTER_RESP,
+                                  state->rpc_call_id);
        if (subreq == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -1905,7 +1926,8 @@ static NTSTATUS rpc_bind_finish_send(struct tevent_req *req,
        }
 
        subreq = rpc_api_pipe_send(state, state->ev, state->cli,
-                                  &state->rpc_out, DCERPC_PKT_AUTH3);
+                                  &state->rpc_out, DCERPC_PKT_AUTH3,
+                                  state->rpc_call_id);
        if (subreq == NULL) {
                return NT_STATUS_NO_MEMORY;
        }