summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2013-10-22 15:34:12 -0700
committerKarolin Seeger <kseeger@samba.org>2013-12-05 11:11:52 +0100
commit6434d492578b37c7c97bd3f55d4fc14958bbd080 (patch)
tree3a71251c8e512578440b16dc22fbd3854d6a5bdd
parentf6d2b22ec51e025a309548224e8354bce52ea648 (diff)
downloadsamba-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.idl1
-rw-r--r--librpc/rpc/dcerpc_util.c9
-rw-r--r--librpc/rpc/rpc_common.h1
-rw-r--r--source3/rpc_client/cli_pipe.c34
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;
}