diff options
author | Jeremy Allison <jra@samba.org> | 2013-10-22 15:34:12 -0700 |
---|---|---|
committer | Karolin Seeger <kseeger@samba.org> | 2013-12-05 11:11:52 +0100 |
commit | 6434d492578b37c7c97bd3f55d4fc14958bbd080 (patch) | |
tree | 3a71251c8e512578440b16dc22fbd3854d6a5bdd | |
parent | f6d2b22ec51e025a309548224e8354bce52ea648 (diff) | |
download | samba-6434d492578b37c7c97bd3f55d4fc14958bbd080.tar.gz |
CVE-2013-4408:s3:Ensure we always check call_id when validating an RPC reply.
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>
-rw-r--r-- | librpc/idl/dcerpc.idl | 1 | ||||
-rw-r--r-- | librpc/rpc/dcerpc_util.c | 9 | ||||
-rw-r--r-- | librpc/rpc/rpc_common.h | 1 | ||||
-rw-r--r-- | source3/rpc_client/cli_pipe.c | 34 |
4 files changed, 39 insertions, 6 deletions
diff --git a/librpc/idl/dcerpc.idl b/librpc/idl/dcerpc.idl index 86f22a4b8c8..89498364081 100644 --- a/librpc/idl/dcerpc.idl +++ b/librpc/idl/dcerpc.idl @@ -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; diff --git a/librpc/rpc/dcerpc_util.c b/librpc/rpc/dcerpc_util.c index b8bf64d655e..cb21312152f 100644 --- a/librpc/rpc/dcerpc_util.c +++ b/librpc/rpc/dcerpc_util.c @@ -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) { diff --git a/librpc/rpc/rpc_common.h b/librpc/rpc/rpc_common.h index 44c3cfd39b5..924645dd896 100644 --- a/librpc/rpc/rpc_common.h +++ b/librpc/rpc/rpc_common.h @@ -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); diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c index 57dcddc66d8..96364793de3 100644 --- a/source3/rpc_client/cli_pipe.c +++ b/source3/rpc_client/cli_pipe.c @@ -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; } |