From 66ddbc5f246aa6595be1c018900c5ca1c945c4c1 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 24 Oct 2013 14:49:19 -0700 Subject: [PATCH 01/15] Add NT_STATUS_RPC_* codes Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- libcli/util/ntstatus.h | 16 ++++++++++++++-- source3/libsmb/nterr.c | 9 +++++++++ source4/libcli/util/nterr.c | 8 ++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/libcli/util/ntstatus.h b/libcli/util/ntstatus.h index 1608e28..d0fbb2d 100644 --- a/libcli/util/ntstatus.h +++ b/libcli/util/ntstatus.h @@ -605,7 +605,16 @@ typedef uint32_t NTSTATUS; #define NT_STATUS_NO_SUCH_JOB NT_STATUS(0xC0000000 | 0xEDE) /* scheduler */ #define NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED NT_STATUS(0xC0000000 | 0x20004) #define NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX NT_STATUS(0xC0000000 | 0x20026) -#define NT_STATUS_RPC_NT_CALL_FAILED NT_STATUS(0xC0000000 | 0x2001B) +#define NT_STATUS_RPC_UNKNOWN_IF NT_STATUS(0xC0000000 | 0x20012) +#define NT_STATUS_RPC_CALL_FAILED NT_STATUS(0xC0000000 | 0x2001B) +#define NT_STATUS_RPC_PROTOCOL_ERROR NT_STATUS(0xC0000000 | 0x2001D) +#define NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE NT_STATUS(0xC0000000 | 0x2002E) +#define NT_STATUS_RPC_CANNOT_SUPPORT NT_STATUS(0xC0000000 | 0x20041) +#define NT_STATUS_RPC_SS_CONTEXT_MISMATCH NT_STATUS(0xC0000000 | 0x30005) +#define NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE NT_STATUS(0xC000000 | 0x3000A) +#define NT_STATUS_RPC_BAD_STUB_DATA NT_STATUS(0xC0000000 | 0x3000C) +#define NT_STATUS_ERROR_DS_OBJ_STRING_NAME_EXISTS NT_STATUS(0xC0000000 | 0x2071) +#define NT_STATUS_ERROR_DS_INCOMPATIBLE_VERSION NT_STATUS(0xC0000000 | 0x00002177) /* I use NT_STATUS_FOOBAR when I have no idea what error code to use - @@ -688,6 +697,9 @@ extern bool ntstatus_check_dos_mapping; #define NT_STATUS_IS_LDAP(status) ((NT_STATUS_V(status) & 0xFF000000) == 0xF2000000) #define NT_STATUS_LDAP_CODE(status) (NT_STATUS_V(status) & ~0xFF000000) -#define NT_STATUS_RPC_CANNOT_SUPPORT NT_STATUS(0xC0000000 | 0x20041) +#define NT_STATUS_IS_RPC(status) \ + (((NT_STATUS_V(status) & 0xFFFF) == 0xC0020000) || \ + ((NT_STATUS_V(status) & 0xFFFF) == 0xC0030000)) + #endif /* _NTSTATUS_H */ diff --git a/source3/libsmb/nterr.c b/source3/libsmb/nterr.c index 52e81ac..fd0258c 100644 --- a/source3/libsmb/nterr.c +++ b/source3/libsmb/nterr.c @@ -541,6 +541,15 @@ static const nt_err_code_struct nt_errs[] = { "STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED }, { "STATUS_NO_MORE_FILES", STATUS_NO_MORE_FILES }, { "NT_STATUS_RPC_CANNOT_SUPPORT", NT_STATUS_RPC_CANNOT_SUPPORT }, + { "NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED", NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED }, + { "NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX", NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX }, + { "NT_STATUS_RPC_UNKNOWN_IF", NT_STATUS_RPC_UNKNOWN_IF }, + { "NT_STATUS_RPC_CALL_FAILED", NT_STATUS_RPC_CALL_FAILED }, + { "NT_STATUS_RPC_PROTOCOL_ERROR", NT_STATUS_RPC_PROTOCOL_ERROR }, + { "NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE", NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE }, + { "NT_STATUS_RPC_SS_CONTEXT_MISMATCH", NT_STATUS_RPC_SS_CONTEXT_MISMATCH }, + { "NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE", NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE }, + { "NT_STATUS_RPC_BAD_STUB_DATA", NT_STATUS_RPC_BAD_STUB_DATA }, { NULL, NT_STATUS(0) } }; diff --git a/source4/libcli/util/nterr.c b/source4/libcli/util/nterr.c index 4e7cdf5..d179a04 100644 --- a/source4/libcli/util/nterr.c +++ b/source4/libcli/util/nterr.c @@ -545,6 +545,14 @@ static const nt_err_code_struct nt_errs[] = { "NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES }, { "NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED", NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED }, { "NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX", NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX }, + { "NT_STATUS_RPC_UNKNOWN_IF", NT_STATUS_RPC_UNKNOWN_IF }, + { "NT_STATUS_RPC_CALL_FAILED", NT_STATUS_RPC_CALL_FAILED }, + { "NT_STATUS_RPC_PROTOCOL_ERROR", NT_STATUS_RPC_PROTOCOL_ERROR }, + { "NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE", NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE }, + { "NT_STATUS_RPC_CANNOT_SUPPORT", NT_STATUS_RPC_CANNOT_SUPPORT }, + { "NT_STATUS_RPC_SS_CONTEXT_MISMATCH", NT_STATUS_RPC_SS_CONTEXT_MISMATCH }, + { "NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE", NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE }, + { "NT_STATUS_RPC_BAD_STUB_DATA", NT_STATUS_RPC_BAD_STUB_DATA }, { "NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED", NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED }, { "NT_STATUS_OBJECTID_NOT_FOUND", NT_STATUS_OBJECTID_NOT_FOUND }, { "NT_STATUS_DOWNGRADE_DETECTED", NT_STATUS_DOWNGRADE_DETECTED }, -- 1.7.9.5 From d1b1e12255ac91636cc7aeac637d1b6c2b54415c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 25 Sep 2013 23:25:12 +0200 Subject: [PATCH 02/15] CVE-2013-4408:s3:rpc_client: verify frag_len at least contains the header size Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/rpc_client/cli_pipe.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c index b47bef8..bb72d8e 100644 --- a/source3/rpc_client/cli_pipe.c +++ b/source3/rpc_client/cli_pipe.c @@ -378,6 +378,12 @@ static NTSTATUS parse_rpc_header(struct rpc_pipe_client *cli, return NT_STATUS_BUFFER_TOO_SMALL; } + if (prhdr->frag_len < RPC_HEADER_LEN) { + DEBUG(0, ("parse_rpc_header: Server sent fraglen %d," + " < RPC_HEADER_LEN\n", (int)prhdr->frag_len)); + return NT_STATUS_RPC_PROTOCOL_ERROR; + } + if (prhdr->frag_len > cli->max_recv_frag) { DEBUG(0, ("cli_pipe_get_current_pdu: Server sent fraglen %d," " we only allow %d\n", (int)prhdr->frag_len, -- 1.7.9.5 From 28e3f10e37f32812cc8f9866008200cb49f9a307 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 25 Sep 2013 23:25:12 +0200 Subject: [PATCH 03/15] CVE-2013-4408:s4:dcerpc: check for invalid frag_len in ncacn_pull() Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source4/librpc/rpc/dcerpc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c index 7a568d3..f3b64ca 100644 --- a/source4/librpc/rpc/dcerpc.c +++ b/source4/librpc/rpc/dcerpc.c @@ -205,6 +205,10 @@ static NTSTATUS ncacn_pull(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_ return ndr_map_error2ntstatus(ndr_err); } + if (pkt->frag_length != blob->length) { + return NT_STATUS_RPC_PROTOCOL_ERROR; + } + return NT_STATUS_OK; } -- 1.7.9.5 From b2c4cdc6eb237fa449685ce53b7d3a3a1ff32412 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 25 Sep 2013 23:25:12 +0200 Subject: [PATCH 04/15] CVE-2013-4408:s4:dcerpc_smb: check for invalid frag_len in send_read_request_continue() Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source4/librpc/rpc/dcerpc_smb.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source4/librpc/rpc/dcerpc_smb.c b/source4/librpc/rpc/dcerpc_smb.c index 013a857..b10c617 100644 --- a/source4/librpc/rpc/dcerpc_smb.c +++ b/source4/librpc/rpc/dcerpc_smb.c @@ -159,6 +159,12 @@ static NTSTATUS send_read_request_continue(struct dcerpc_connection *c, DATA_BLO } else { uint32_t frag_length = blob->length>=16? dcerpc_get_frag_length(blob):0x2000; + + if (frag_length < state->data.length) { + talloc_free(state); + return NT_STATUS_RPC_PROTOCOL_ERROR; + } + state->received = blob->length; state->data = data_blob_talloc(state, NULL, frag_length); if (!state->data.data) { -- 1.7.9.5 From 246aafc6785717df4e369695807f08db550f323f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 25 Sep 2013 23:25:12 +0200 Subject: [PATCH 05/15] CVE-2013-4408:s4:dcerpc_smb2: check for invalid frag_len in send_read_request_continue() Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source4/librpc/rpc/dcerpc_smb2.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source4/librpc/rpc/dcerpc_smb2.c b/source4/librpc/rpc/dcerpc_smb2.c index 84ba811..8bdea67 100644 --- a/source4/librpc/rpc/dcerpc_smb2.c +++ b/source4/librpc/rpc/dcerpc_smb2.c @@ -169,6 +169,12 @@ static NTSTATUS send_read_request_continue(struct dcerpc_connection *c, DATA_BLO if (state->data.length >= 16) { uint16_t frag_length = dcerpc_get_frag_length(&state->data); + + if (frag_length < state->data.length) { + talloc_free(state); + return NT_STATUS_RPC_PROTOCOL_ERROR; + } + io.in.length = frag_length - state->data.length; } else { io.in.length = 0x2000; -- 1.7.9.5 From 7f0103b12ca04785800d248307fed919285e8969 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 24 Sep 2013 05:03:40 +0200 Subject: [PATCH 06/15] CVE-2013-4408:s4:dcerpc_sock: check for invalid frag_len within sock_complete_packet() Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source4/librpc/rpc/dcerpc_sock.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source4/librpc/rpc/dcerpc_sock.c b/source4/librpc/rpc/dcerpc_sock.c index 64a5b92..9259bb7 100644 --- a/source4/librpc/rpc/dcerpc_sock.c +++ b/source4/librpc/rpc/dcerpc_sock.c @@ -101,6 +101,12 @@ static NTSTATUS sock_complete_packet(void *private_data, DATA_BLOB blob, size_t return STATUS_MORE_ENTRIES; } *size = dcerpc_get_frag_length(&blob); + if (*size < blob.length) { + /* + * something is wrong, let the caller deal with it + */ + *size = blob.length; + } if (*size > blob.length) { return STATUS_MORE_ENTRIES; } -- 1.7.9.5 From 1d68a036ebd309e9fe216d532d46cf9eec9c014d Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 9 Oct 2013 13:05:03 -0700 Subject: [PATCH 07/15] CVE-2013-4408:s3:rpc_server: ensure frag_len is at least as large as RPC_HEADER_LEN. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/rpc_server/srv_pipe_hnd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c index 14b137b..a18f7ad 100644 --- a/source3/rpc_server/srv_pipe_hnd.c +++ b/source3/rpc_server/srv_pipe_hnd.c @@ -1298,6 +1298,10 @@ static ssize_t rpc_frag_more_fn(uint8_t *buf, size_t buflen, void *priv) return -1; } + if (hdr.frag_len < RPC_HEADER_LEN) { + return -1; + } + return (hdr.frag_len - RPC_HEADER_LEN); } -- 1.7.9.5 From 51ea748a80d53c3e05c634d35705cbcf8c781fcb Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 9 Oct 2013 11:20:53 -0700 Subject: [PATCH 08/15] CVE-2013-4408:s3:rpc_server: check a pipe with PIPE_AUTH_TYPE_NONE does not receive auth_len. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/rpc_server/srv_pipe_hnd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c index a18f7ad..2b76139 100644 --- a/source3/rpc_server/srv_pipe_hnd.c +++ b/source3/rpc_server/srv_pipe_hnd.c @@ -385,6 +385,11 @@ static bool process_request_pdu(pipes_struct *p, prs_struct *rpc_in_p) switch(p->auth.auth_type) { case PIPE_AUTH_TYPE_NONE: + if (p->hdr.auth_len != 0) { + DEBUG(0,("process_request_pdu: PIPE_AUTH_TYPE_NONE given auth_data.\n")); + set_incoming_fault(p); + return False; + } break; case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP: -- 1.7.9.5 From cc886f485b7f5c4bdfdb8841d27ff7320322d1d4 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 16 Oct 2013 12:21:51 -0700 Subject: [PATCH 09/15] CVE-2013-4408:s3:rpc_server: for auth_level DCERPC_AUTH_LEVEL_NONE or DCERPC_AUTH_LEVEL_CONNECT auth_len must be zero. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/rpc_server/srv_pipe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c index f3ee18d..d3046d3 100644 --- a/source3/rpc_server/srv_pipe.c +++ b/source3/rpc_server/srv_pipe.c @@ -2003,7 +2003,8 @@ bool api_pipe_ntlmssp_auth_process(pipes_struct *p, prs_struct *rpc_in, *pstatus = NT_STATUS_OK; if (p->auth.auth_level == PIPE_AUTH_LEVEL_NONE || p->auth.auth_level == PIPE_AUTH_LEVEL_CONNECT) { - return True; + /* For these auth_levels the auth_len must be zero. */ + return (auth_len == 0); } if (!a) { -- 1.7.9.5 From c9c986f1d2a1694bf3c7e650e61d96922605dcb9 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 9 Oct 2013 13:13:57 -0700 Subject: [PATCH 10/15] CVE-2013-4408:s3:rpc_server: integer wrap check on data_len. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/rpc_server/srv_pipe_hnd.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c index 2b76139..52c4747 100644 --- a/source3/rpc_server/srv_pipe_hnd.c +++ b/source3/rpc_server/srv_pipe_hnd.c @@ -359,8 +359,7 @@ static void free_pipe_context(pipes_struct *p) static bool process_request_pdu(pipes_struct *p, prs_struct *rpc_in_p) { uint32 ss_padding_len = 0; - size_t data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN - - (p->hdr.auth_len ? RPC_HDR_AUTH_LEN : 0) - p->hdr.auth_len; + size_t data_len = 0; if(!p->pipe_bound) { DEBUG(0,("process_request_pdu: rpc request with no bind.\n")); @@ -419,6 +418,16 @@ static bool process_request_pdu(pipes_struct *p, prs_struct *rpc_in_p) return False; } + data_len = p->hdr.frag_len - RPC_HEADER_LEN - RPC_HDR_REQ_LEN - + (p->hdr.auth_len ? RPC_HDR_AUTH_LEN : 0) - p->hdr.auth_len; + + if (data_len > p->hdr.frag_len) { + DEBUG(0,("process_request_pdu: rpc frag size too small (%u)\n", + (unsigned int)p->hdr.frag_len)); + set_incoming_fault(p); + return False; + } + /* Now we've done the sign/seal we can remove any padding data. */ if (data_len > ss_padding_len) { data_len -= ss_padding_len; -- 1.7.9.5 From 77003090bb4094c3fb79daf21a7243c8cb3ea13c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 16 Oct 2013 14:17:49 +0200 Subject: [PATCH 11/15] CVE-2013-4408:async_sock: add some overflow detection to read_packet_handler() Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- lib/async_req/async_sock.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/async_req/async_sock.c b/lib/async_req/async_sock.c index c428e3c..83432b8 100644 --- a/lib/async_req/async_sock.c +++ b/lib/async_req/async_sock.c @@ -595,6 +595,11 @@ static void read_packet_handler(struct tevent_context *ev, return; } + if (total + more < total) { + tevent_req_error(req, EMSGSIZE); + return; + } + tmp = TALLOC_REALLOC_ARRAY(state, state->buf, uint8_t, total+more); if (tevent_req_nomem(tmp, req)) { return; -- 1.7.9.5 From 93484c7d713f90b6052411c91699293bf8e1f306 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 24 Oct 2013 14:38:23 -0700 Subject: [PATCH 12/15] 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 Reviewed-by: Stefan Metzmacher --- source3/rpc_client/cli_pipe.c | 56 +++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c index bb72d8e..87c4430 100644 --- a/source3/rpc_client/cli_pipe.c +++ b/source3/rpc_client/cli_pipe.c @@ -366,7 +366,8 @@ static NTSTATUS rpc_write_recv(struct async_req *req) static NTSTATUS parse_rpc_header(struct rpc_pipe_client *cli, struct rpc_hdr_info *prhdr, - prs_struct *pdu) + prs_struct *pdu, + uint32_t call_id) { /* * This next call sets the endian bit correctly in current_pdu. We @@ -391,6 +392,14 @@ static NTSTATUS parse_rpc_header(struct rpc_pipe_client *cli, return NT_STATUS_BUFFER_TOO_SMALL; } + if (prhdr->call_id != call_id) { + DEBUG(0, ("parse_rpc_header: call_id was %u," + " expected %u\n", + (unsigned int)prhdr->call_id, + (unsigned int)call_id)); + return NT_STATUS_RPC_PROTOCOL_ERROR; + } + return NT_STATUS_OK; } @@ -403,6 +412,7 @@ struct get_complete_frag_state { struct event_context *ev; struct rpc_pipe_client *cli; struct rpc_hdr_info *prhdr; + uint32_t call_id; prs_struct *pdu; }; @@ -412,6 +422,7 @@ static void get_complete_frag_got_rest(struct async_req *subreq); static struct async_req *get_complete_frag_send(TALLOC_CTX *mem_ctx, struct event_context *ev, struct rpc_pipe_client *cli, + uint32_t call_id, struct rpc_hdr_info *prhdr, prs_struct *pdu) { @@ -427,6 +438,7 @@ static struct async_req *get_complete_frag_send(TALLOC_CTX *mem_ctx, state->ev = ev; state->cli = cli; state->prhdr = prhdr; + state->call_id = call_id; state->pdu = pdu; pdu_len = prs_data_size(pdu); @@ -449,7 +461,7 @@ static struct async_req *get_complete_frag_send(TALLOC_CTX *mem_ctx, return result; } - status = parse_rpc_header(cli, prhdr, pdu); + status = parse_rpc_header(cli, prhdr, pdu, call_id); if (!NT_STATUS_IS_OK(status)) { goto post_status; } @@ -499,7 +511,8 @@ static void get_complete_frag_got_header(struct async_req *subreq) return; } - status = parse_rpc_header(state->cli, state->prhdr, state->pdu); + status = parse_rpc_header(state->cli, state->prhdr, state->pdu, + state->call_id); if (!NT_STATUS_IS_OK(status)) { async_req_nterror(req, status); return; @@ -1229,6 +1242,7 @@ struct rpc_api_pipe_state { struct event_context *ev; struct rpc_pipe_client *cli; uint8_t expected_pkt_type; + uint32_t call_id; prs_struct incoming_frag; struct rpc_hdr_info rhdr; @@ -1251,7 +1265,8 @@ static struct async_req *rpc_api_pipe_send(TALLOC_CTX *mem_ctx, struct event_context *ev, struct rpc_pipe_client *cli, prs_struct *data, /* Outgoing PDU */ - uint8_t expected_pkt_type) + uint8_t expected_pkt_type, + uint32_t call_id) { struct async_req *result, *subreq; struct rpc_api_pipe_state *state; @@ -1265,6 +1280,7 @@ static struct async_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_pdu_offset = 0; prs_init_empty(&state->incoming_frag, state, UNMARSHALL); @@ -1350,6 +1366,7 @@ static void rpc_api_pipe_trans_done(struct async_req *subreq) /* Ensure we have enough data for a pdu. */ subreq = get_complete_frag_send(state, state->ev, state->cli, + state->call_id, &state->rhdr, &state->incoming_frag); if (async_req_nomem(subreq, req)) { return; @@ -1442,6 +1459,7 @@ static void rpc_api_pipe_got_pdu(struct async_req *subreq) } subreq = get_complete_frag_send(state, state->ev, state->cli, + state->call_id, &state->rhdr, &state->incoming_frag); if (async_req_nomem(subreq, req)) { return; @@ -2102,7 +2120,8 @@ struct async_req *rpc_api_pipe_req_send(TALLOC_CTX *mem_ctx, if (is_last_frag) { subreq = rpc_api_pipe_send(state, ev, state->cli, &state->outgoing_frag, - RPC_RESPONSE); + RPC_RESPONSE, + state->call_id); if (subreq == NULL) { status = NT_STATUS_NO_MEMORY; goto post_status; @@ -2241,7 +2260,8 @@ static void rpc_api_pipe_req_write_done(struct async_req *subreq) if (is_last_frag) { subreq = rpc_api_pipe_send(state, state->ev, state->cli, &state->outgoing_frag, - RPC_RESPONSE); + RPC_RESPONSE, + state->call_id); if (async_req_nomem(subreq, req)) { return; } @@ -2582,7 +2602,7 @@ struct async_req *rpc_pipe_bind_send(TALLOC_CTX *mem_ctx, } subreq = rpc_api_pipe_send(state, ev, cli, &state->rpc_out, - RPC_BINDACK); + RPC_BINDACK, state->rpc_call_id); if (subreq == NULL) { status = NT_STATUS_NO_MEMORY; goto post_status; @@ -2628,6 +2648,16 @@ static void rpc_pipe_bind_step_one_done(struct async_req *subreq) return; } + if (hdr.call_id != state->rpc_call_id) { + DEBUG(0, ("rpc_pipe_bind: Missmatched call_id " + "(was %u, should be %u).\n", + (unsigned int)hdr.call_id, + (unsigned int)state->rpc_call_id)); + prs_mem_free(&reply_pdu); + async_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR); + return; + } + if (!smb_io_rpc_hdr_ba("", &hdr_ba, &reply_pdu, 0)) { DEBUG(0, ("rpc_pipe_bind: Failed to unmarshall " "RPC_HDR_BA.\n")); @@ -2853,7 +2883,8 @@ static NTSTATUS rpc_finish_spnego_ntlmssp_bind_send(struct async_req *req, } subreq = rpc_api_pipe_send(state, state->ev, state->cli, - &state->rpc_out, RPC_ALTCONTRESP); + &state->rpc_out, RPC_ALTCONTRESP, + state->rpc_call_id); if (subreq == NULL) { return NT_STATUS_NO_MEMORY; } @@ -2890,6 +2921,15 @@ static void rpc_bind_ntlmssp_api_done(struct async_req *subreq) return; } + if (hdr.call_id != state->rpc_call_id) { + DEBUG(0, ("Missmatched call_id " + "(was %u, should be %u).\n", + (unsigned int)hdr.call_id, + (unsigned int)state->rpc_call_id)); + async_req_nterror(req, NT_STATUS_RPC_PROTOCOL_ERROR); + return; + } + if (!prs_set_offset( &reply_pdu, hdr.frag_len - hdr.auth_len - RPC_HDR_AUTH_LEN)) { -- 1.7.9.5 From abb0f97085a63e8d112ab3c315ff79fb710af91d Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 20 Nov 2013 12:13:47 -0800 Subject: [PATCH 13/15] CVE-2013-4408:s3:Ensure LookupSids replies arrays are range checked. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Stefan Metzmacher Signed-off-by: Jeremy Allison --- source3/rpc_client/cli_lsarpc.c | 13 +++++++++++++ source4/winbind/wb_async_helpers.c | 13 ++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/source3/rpc_client/cli_lsarpc.c b/source3/rpc_client/cli_lsarpc.c index 74fd082..dc29bd0 100644 --- a/source3/rpc_client/cli_lsarpc.c +++ b/source3/rpc_client/cli_lsarpc.c @@ -197,6 +197,11 @@ static NTSTATUS rpccli_lsa_lookup_sids_noalloc(struct rpc_pipe_client *cli, goto done; } + if (num_sids != lsa_names.count) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + /* Return output parameters */ if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED) || @@ -218,6 +223,14 @@ static NTSTATUS rpccli_lsa_lookup_sids_noalloc(struct rpc_pipe_client *cli, /* Translate optimised name through domain index array */ if (dom_idx != 0xffffffff) { + if (ref_domains == NULL) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (dom_idx >= ref_domains->count) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } dom_name = ref_domains->domains[dom_idx].name.string; name = lsa_names.names[i].name.string; diff --git a/source4/winbind/wb_async_helpers.c b/source4/winbind/wb_async_helpers.c index a50a0fe..266634c 100644 --- a/source4/winbind/wb_async_helpers.c +++ b/source4/winbind/wb_async_helpers.c @@ -122,6 +122,12 @@ static void lsa_lookupsids_recv_names(struct rpc_request *req) return; } + if (state->names.count != state->num_sids) { + composite_error(state->ctx, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + state->result = talloc_array(state, struct wb_sid_object *, state->num_sids); if (composite_nomem(state->result, state->ctx)) return; @@ -142,9 +148,14 @@ static void lsa_lookupsids_recv_names(struct rpc_request *req) continue; } + if (domains == NULL) { + composite_error(state->ctx, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } if (name->sid_index >= domains->count) { composite_error(state->ctx, - NT_STATUS_INVALID_PARAMETER); + NT_STATUS_INVALID_NETWORK_RESPONSE); return; } -- 1.7.9.5 From 4a4e0ef9dadbafd21ac9d567ecb04192b1ba467f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 20 Nov 2013 13:10:04 -0800 Subject: [PATCH 14/15] CVE-2013-4408:s3:Ensure LookupNames replies arrays are range checked. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Stefan Metzmacher Signed-off-by: Jeremy Allison --- source3/lib/netapi/group.c | 84 ++++++++++++++++++++++++++++++++++++ source3/lib/netapi/localgroup.c | 9 +++- source3/lib/netapi/user.c | 56 ++++++++++++++++++++++++ source3/libnet/libnet_join.c | 16 +++++++ source3/rpc_client/cli_lsarpc.c | 18 ++++++++ source3/rpcclient/cmd_samr.c | 62 ++++++++++++++++++++++++++ source3/smbd/lanman.c | 8 ++++ source3/utils/net_rpc.c | 40 ++++++++++++++++- source3/utils/net_rpc_join.c | 9 ++++ source4/libcli/util/clilsa.c | 6 ++- source4/libnet/groupinfo.c | 10 +++-- source4/libnet/groupman.c | 10 ++--- source4/libnet/libnet_join.c | 12 +++++- source4/libnet/libnet_lookup.c | 5 +++ source4/libnet/libnet_passwd.c | 10 ++++- source4/libnet/userinfo.c | 9 +++- source4/libnet/userman.c | 24 +++++------ source4/winbind/wb_async_helpers.c | 13 +++++- 18 files changed, 370 insertions(+), 31 deletions(-) diff --git a/source3/lib/netapi/group.c b/source3/lib/netapi/group.c index 004fd3a..1769095 100644 --- a/source3/lib/netapi/group.c +++ b/source3/lib/netapi/group.c @@ -272,6 +272,15 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx, goto done; } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.ids[0] != SID_NAME_DOM_GRP) { werr = WERR_INVALID_DATATYPE; goto done; @@ -436,6 +445,14 @@ WERROR NetGroupSetInfo_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } if (types.ids[0] != SID_NAME_DOM_GRP) { werr = WERR_INVALID_DATATYPE; @@ -671,6 +688,14 @@ WERROR NetGroupGetInfo_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } if (types.ids[0] != SID_NAME_DOM_GRP) { werr = WERR_INVALID_DATATYPE; @@ -787,6 +812,14 @@ WERROR NetGroupAddUser_r(struct libnetapi_ctx *ctx, werr = WERR_GROUP_NOT_FOUND; goto done; } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } if (types.ids[0] != SID_NAME_DOM_GRP) { werr = WERR_GROUP_NOT_FOUND; @@ -815,6 +848,14 @@ WERROR NetGroupAddUser_r(struct libnetapi_ctx *ctx, werr = WERR_USER_NOT_FOUND; goto done; } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } if (types.ids[0] != SID_NAME_USER) { werr = WERR_USER_NOT_FOUND; @@ -908,6 +949,14 @@ WERROR NetGroupDelUser_r(struct libnetapi_ctx *ctx, werr = WERR_GROUP_NOT_FOUND; goto done; } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } if (types.ids[0] != SID_NAME_DOM_GRP) { werr = WERR_GROUP_NOT_FOUND; @@ -936,6 +985,14 @@ WERROR NetGroupDelUser_r(struct libnetapi_ctx *ctx, werr = WERR_USER_NOT_FOUND; goto done; } + if (rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } if (types.ids[0] != SID_NAME_USER) { werr = WERR_USER_NOT_FOUND; @@ -1318,6 +1375,15 @@ WERROR NetGroupGetUsers_r(struct libnetapi_ctx *ctx, goto done; } + if (group_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + status = rpccli_samr_OpenGroup(pipe_cli, ctx, &domain_handle, SAMR_GROUP_ACCESS_GET_MEMBERS, @@ -1470,6 +1536,15 @@ WERROR NetGroupSetUsers_r(struct libnetapi_ctx *ctx, goto done; } + if (group_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (group_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + status = rpccli_samr_OpenGroup(pipe_cli, ctx, &domain_handle, SAMR_GROUP_ACCESS_GET_MEMBERS | @@ -1532,6 +1607,15 @@ WERROR NetGroupSetUsers_r(struct libnetapi_ctx *ctx, goto done; } + if (r->in.num_entries != user_rids.count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (r->in.num_entries != name_types.count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + member_rids = user_rids.ids; num_member_rids = user_rids.count; diff --git a/source3/lib/netapi/localgroup.c b/source3/lib/netapi/localgroup.c index d389c1f..05ac477 100644 --- a/source3/lib/netapi/localgroup.c +++ b/source3/lib/netapi/localgroup.c @@ -48,6 +48,13 @@ static NTSTATUS libnetapi_samr_lookup_and_open_alias(TALLOC_CTX *mem_ctx, return status; } + if (user_rids.count != 1) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (name_types.count != 1) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + switch (name_types.ids[0]) { case SID_NAME_ALIAS: case SID_NAME_WKN_GRP: @@ -949,7 +956,7 @@ static NTSTATUS libnetapi_lsa_lookup_names3(TALLOC_CTX *mem_ctx, NT_STATUS_NOT_OK_RETURN(status); if (count != 1 || sids.count != 1) { - return NT_STATUS_NONE_MAPPED; + return NT_STATUS_INVALID_NETWORK_RESPONSE; } sid_copy(sid, sids.sids[0].sid); diff --git a/source3/lib/netapi/user.c b/source3/lib/netapi/user.c index d28c136..815306f 100644 --- a/source3/lib/netapi/user.c +++ b/source3/lib/netapi/user.c @@ -545,6 +545,14 @@ WERROR NetUserDel_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } status = rpccli_samr_OpenUser(pipe_cli, ctx, &domain_handle, @@ -1677,6 +1685,14 @@ WERROR NetUserGetInfo_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } status = libnetapi_samr_lookup_user_map_USER_INFO(ctx, pipe_cli, domain_sid, @@ -1832,6 +1848,14 @@ WERROR NetUserSetInfo_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } status = rpccli_samr_OpenUser(pipe_cli, ctx, &domain_handle, @@ -2851,6 +2875,14 @@ WERROR NetUserGetGroups_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } status = rpccli_samr_OpenUser(pipe_cli, ctx, &domain_handle, @@ -3002,6 +3034,14 @@ WERROR NetUserSetGroups_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } status = rpccli_samr_OpenUser(pipe_cli, ctx, &domain_handle, @@ -3052,6 +3092,14 @@ WERROR NetUserSetGroups_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (group_rids.count != r->in.num_entries) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != r->in.num_entries) { + werr = WERR_BAD_NET_RESP; + goto done; + } member_rids = group_rids.ids; num_member_rids = group_rids.count; @@ -3296,6 +3344,14 @@ WERROR NetUserGetLocalGroups_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (user_rids.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (name_types.count != 1) { + werr = WERR_BAD_NET_RESP; + goto done; + } status = rpccli_samr_OpenUser(pipe_cli, ctx, &domain_handle, diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 6489126..b2a9517 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -880,6 +880,14 @@ static NTSTATUS libnet_join_joindomain_rpc(TALLOC_CTX *mem_ctx, if (!NT_STATUS_IS_OK(status)) { goto done; } + if (user_rids.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (name_types.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } if (name_types.ids[0] != SID_NAME_USER) { DEBUG(0,("%s is not a user account (type=%d)\n", @@ -1206,6 +1214,14 @@ static NTSTATUS libnet_join_unjoindomain_rpc(TALLOC_CTX *mem_ctx, if (!NT_STATUS_IS_OK(status)) { goto done; } + if (user_rids.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (name_types.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } if (name_types.ids[0] != SID_NAME_USER) { DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name, diff --git a/source3/rpc_client/cli_lsarpc.c b/source3/rpc_client/cli_lsarpc.c index dc29bd0..dc84b5f 100644 --- a/source3/rpc_client/cli_lsarpc.c +++ b/source3/rpc_client/cli_lsarpc.c @@ -515,9 +515,19 @@ static NTSTATUS rpccli_lsa_lookup_names_generic(struct rpc_pipe_client *cli, DOM_SID *sid = &(*sids)[i]; if (use_lookupnames4) { + if (i >= sid_array3.count) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + dom_idx = sid_array3.sids[i].sid_index; (*types)[i] = sid_array3.sids[i].sid_type; } else { + if (i >= sid_array.count) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + dom_idx = sid_array.sids[i].sid_index; (*types)[i] = sid_array.sids[i].sid_type; } @@ -530,6 +540,14 @@ static NTSTATUS rpccli_lsa_lookup_names_generic(struct rpc_pipe_client *cli, (*types)[i] = SID_NAME_UNKNOWN; continue; } + if (domains == NULL) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (dom_idx >= domains->count) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } if (use_lookupnames4) { sid_copy(sid, sid_array3.sids[i].sid); diff --git a/source3/rpcclient/cmd_samr.c b/source3/rpcclient/cmd_samr.c index de21a40..bfe3b44 100644 --- a/source3/rpcclient/cmd_samr.c +++ b/source3/rpcclient/cmd_samr.c @@ -358,6 +358,15 @@ static NTSTATUS cmd_samr_query_user(struct rpc_pipe_client *cli, &types); if (NT_STATUS_IS_OK(result)) { + if (rids.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (types.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + result = rpccli_samr_OpenUser(cli, mem_ctx, &domain_pol, access_mask, @@ -1291,6 +1300,15 @@ static NTSTATUS cmd_samr_delete_alias(struct rpc_pipe_client *cli, &types); if (NT_STATUS_IS_OK(result)) { + if (rids.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (types.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + result = rpccli_samr_OpenAlias(cli, mem_ctx, &domain_pol, access_mask, @@ -1881,6 +1899,15 @@ static NTSTATUS cmd_samr_lookup_names(struct rpc_pipe_client *cli, if (!NT_STATUS_IS_OK(result)) goto done; + if (rids.count != num_names) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (name_types.count != num_names) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + /* Display results */ for (i = 0; i < num_names; i++) @@ -2021,6 +2048,15 @@ static NTSTATUS cmd_samr_delete_dom_group(struct rpc_pipe_client *cli, if (!NT_STATUS_IS_OK(result)) goto done; + if (group_rids.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (name_types.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + result = rpccli_samr_OpenGroup(cli, mem_ctx, &domain_pol, access_mask, @@ -2103,6 +2139,15 @@ static NTSTATUS cmd_samr_delete_dom_user(struct rpc_pipe_client *cli, if (!NT_STATUS_IS_OK(result)) goto done; + if (user_rids.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (name_types.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + result = rpccli_samr_OpenUser(cli, mem_ctx, &domain_pol, access_mask, @@ -2418,6 +2463,15 @@ static NTSTATUS cmd_samr_chgpasswd(struct rpc_pipe_client *cli, goto done; } + if (rids.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (types.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + result = rpccli_samr_OpenUser(cli, mem_ctx, &domain_pol, access_mask, @@ -2786,6 +2840,14 @@ static NTSTATUS cmd_samr_setuserinfo_int(struct rpc_pipe_client *cli, if (!NT_STATUS_IS_OK(status)) { return status; } + if (rids.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (types.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } status = rpccli_samr_OpenUser(cli, mem_ctx, &domain_pol, diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c index 40b6aca..3b2c8c6 100644 --- a/source3/smbd/lanman.c +++ b/source3/smbd/lanman.c @@ -2492,6 +2492,14 @@ static bool api_NetUserGetGroups(connection_struct *conn,uint16 vuid, nt_errstr(status))); goto close_domain; } + if (rid.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto close_domain; + } + if (type.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto close_domain; + } if (type.ids[0] != SID_NAME_USER) { DEBUG(10, ("%s is a %s, not a user\n", UserName, diff --git a/source3/utils/net_rpc.c b/source3/utils/net_rpc.c index aa04172..589874a 100644 --- a/source3/utils/net_rpc.c +++ b/source3/utils/net_rpc.c @@ -1448,6 +1448,15 @@ static NTSTATUS rpc_group_delete_internals(struct net_context *c, goto done; } + if (group_rids.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (name_types.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + switch (name_types.ids[0]) { case SID_NAME_DOM_GRP: @@ -1768,6 +1777,14 @@ static NTSTATUS rpc_add_groupmem(struct rpc_pipe_client *pipe_hnd, d_fprintf(stderr, "Could not lookup up group member %s\n", member); goto done; } + if (rids.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (rid_types.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } result = rpccli_samr_OpenGroup(pipe_hnd, mem_ctx, &domain_pol, @@ -1972,6 +1989,14 @@ static NTSTATUS rpc_del_groupmem(struct net_context *c, d_fprintf(stderr, "Could not lookup up group member %s\n", member); goto done; } + if (rids.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (rid_types.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } result = rpccli_samr_OpenGroup(pipe_hnd, mem_ctx, &domain_pol, @@ -2625,7 +2650,12 @@ static NTSTATUS rpc_group_members_internals(struct net_context *c, if (rids.count != 1) { d_fprintf(stderr, "Couldn't find group %s\n", argv[0]); - return result; + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (rid_types.count != 1) { + d_fprintf(stderr, "Couldn't find group %s\n", + argv[0]); + return NT_STATUS_INVALID_NETWORK_RESPONSE; } if (rid_types.ids[0] == SID_NAME_DOM_GRP) { @@ -5371,6 +5401,14 @@ static NTSTATUS rpc_trustdom_del_internals(struct net_context *c, acct_name, nt_errstr(result) ); goto done; } + if (user_rids.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (name_types.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } result = rpccli_samr_OpenUser(pipe_hnd, mem_ctx, &domain_pol, diff --git a/source3/utils/net_rpc_join.c b/source3/utils/net_rpc_join.c index 127b306..9c7b0c4 100644 --- a/source3/utils/net_rpc_join.c +++ b/source3/utils/net_rpc_join.c @@ -314,6 +314,15 @@ int net_rpc_join_newstyle(struct net_context *c, int argc, const char **argv) ("error looking up rid for user %s: %s\n", acct_name, nt_errstr(result))); + if (user_rids.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (name_types.count != 1) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (name_types.ids[0] != SID_NAME_USER) { DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name, name_types.ids[0])); goto done; diff --git a/source4/libcli/util/clilsa.c b/source4/libcli/util/clilsa.c index 16967d7..f8bb74c 100644 --- a/source4/libcli/util/clilsa.c +++ b/source4/libcli/util/clilsa.c @@ -298,7 +298,11 @@ NTSTATUS smblsa_lookup_name(struct smbcli_state *cli, } if (sids.count != 1) { talloc_free(mem_ctx2); - return NT_STATUS_UNSUCCESSFUL; + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (domains->count != 1) { + talloc_free(mem_ctx2); + return NT_STATUS_INVALID_NETWORK_RESPONSE; } sid = domains->domains[0].sid; diff --git a/source4/libnet/groupinfo.c b/source4/libnet/groupinfo.c index 1779c28..6020350 100644 --- a/source4/libnet/groupinfo.c +++ b/source4/libnet/groupinfo.c @@ -87,12 +87,16 @@ static void continue_groupinfo_lookup(struct rpc_request *req) s->monitor_fn(&msg); } - /* have we actually got name resolved - we're looking for only one at the moment */ - if (s->lookup.out.rids->count == 0) { - composite_error(c, NT_STATUS_NO_SUCH_USER); + if (s->lookup.out.rids->count != s->lookup.in.num_names) { + composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + if (s->lookup.out.types->count != s->lookup.in.num_names) { + composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; } /* TODO: find proper status code for more than one rid found */ diff --git a/source4/libnet/groupman.c b/source4/libnet/groupman.c index 0f54db9..2bc318a 100644 --- a/source4/libnet/groupman.c +++ b/source4/libnet/groupman.c @@ -207,13 +207,13 @@ static void continue_groupdel_name_found(struct rpc_request *req) /* what to do when there's no group account to delete and what if there's more than one rid resolved */ - if (!s->lookupname.out.rids->count) { - c->status = NT_STATUS_NO_SUCH_GROUP; + if (s->lookupname.out.rids->count != s->lookupname.in.num_names) { + c->status = NT_STATUS_INVALID_NETWORK_RESPONSE; composite_error(c, c->status); return; - - } else if (!s->lookupname.out.rids->count > 1) { - c->status = NT_STATUS_INVALID_ACCOUNT_NAME; + } + if (s->lookupname.out.types->count != s->lookupname.in.num_names) { + c->status = NT_STATUS_INVALID_NETWORK_RESPONSE; composite_error(c, c->status); return; } diff --git a/source4/libnet/libnet_join.c b/source4/libnet/libnet_join.c index 0a4e357..d5173fd 100644 --- a/source4/libnet/libnet_join.c +++ b/source4/libnet/libnet_join.c @@ -642,9 +642,17 @@ NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, stru "samr_LookupNames for [%s] returns %d RIDs", r->in.account_name, ln.out.rids->count); talloc_free(tmp_ctx); - return NT_STATUS_INVALID_PARAMETER; + return NT_STATUS_INVALID_NETWORK_RESPONSE; } - + + if (ln.out.types->count != 1) { + r->out.error_string = talloc_asprintf(mem_ctx, + "samr_LookupNames for [%s] returns %d RID TYPEs", + r->in.account_name, ln.out.types->count); + talloc_free(tmp_ctx); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + /* prepare samr_OpenUser */ ZERO_STRUCTP(u_handle); ou.in.domain_handle = &d_handle; diff --git a/source4/libnet/libnet_lookup.c b/source4/libnet/libnet_lookup.c index fc30782..a271d96 100644 --- a/source4/libnet/libnet_lookup.c +++ b/source4/libnet/libnet_lookup.c @@ -354,6 +354,11 @@ static void continue_name_found(struct rpc_request *req) c->status = s->lookup.out.result; if (!composite_is_ok(c)) return; + if (s->lookup.out.sids->count != s->lookup.in.num_names) { + composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + composite_done(c); } diff --git a/source4/libnet/libnet_passwd.c b/source4/libnet/libnet_passwd.c index c2db219..120cf4e 100644 --- a/source4/libnet/libnet_passwd.c +++ b/source4/libnet/libnet_passwd.c @@ -626,10 +626,18 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX * r->samr.out.error_string = talloc_asprintf(mem_ctx, "samr_LookupNames for [%s] returns %d RIDs", r->samr.in.account_name, ln.out.rids->count); - status = NT_STATUS_INVALID_PARAMETER; + status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto disconnect; } + if (ln.out.types->count != 1) { + r->samr.out.error_string = talloc_asprintf(mem_ctx, + "samr_LookupNames for [%s] returns %d RID TYPEs", + r->samr.in.account_name, ln.out.types->count); + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto disconnect; + } + /* prepare samr_OpenUser */ ZERO_STRUCT(u_handle); ou.in.domain_handle = &d_handle; diff --git a/source4/libnet/userinfo.c b/source4/libnet/userinfo.c index a718ab9..283b126 100644 --- a/source4/libnet/userinfo.c +++ b/source4/libnet/userinfo.c @@ -90,8 +90,13 @@ static void continue_userinfo_lookup(struct rpc_request *req) /* have we actually got name resolved - we're looking for only one at the moment */ - if (s->lookup.out.rids->count == 0) { - composite_error(c, NT_STATUS_NO_SUCH_USER); + if (s->lookup.out.rids->count != s->lookup.in.num_names) { + composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + if (s->lookup.out.types->count != s->lookup.in.num_names) { + composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; } /* TODO: find proper status code for more than one rid found */ diff --git a/source4/libnet/userman.c b/source4/libnet/userman.c index 62d4e0e..9e5a30d 100644 --- a/source4/libnet/userman.c +++ b/source4/libnet/userman.c @@ -233,14 +233,12 @@ static void continue_userdel_name_found(struct rpc_request *req) /* what to do when there's no user account to delete and what if there's more than one rid resolved */ - if (!s->lookupname.out.rids->count) { - c->status = NT_STATUS_NO_SUCH_USER; - composite_error(c, c->status); + if (s->lookupname.out.rids->count != s->lookupname.in.num_names) { + composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE); return; - - } else if (!s->lookupname.out.rids->count > 1) { - c->status = NT_STATUS_INVALID_ACCOUNT_NAME; - composite_error(c, c->status); + } + if (s->lookupname.out.types->count != s->lookupname.in.num_names) { + composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE); return; } @@ -501,14 +499,12 @@ static void continue_usermod_name_found(struct rpc_request *req) /* what to do when there's no user account to delete and what if there's more than one rid resolved */ - if (!s->lookupname.out.rids->count) { - c->status = NT_STATUS_NO_SUCH_USER; - composite_error(c, c->status); + if (s->lookupname.out.rids->count != s->lookupname.in.num_names) { + composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE); return; - - } else if (!s->lookupname.out.rids->count > 1) { - c->status = NT_STATUS_INVALID_ACCOUNT_NAME; - composite_error(c, c->status); + } + if (s->lookupname.out.types->count != s->lookupname.in.num_names) { + composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE); return; } diff --git a/source4/winbind/wb_async_helpers.c b/source4/winbind/wb_async_helpers.c index 266634c..433754b 100644 --- a/source4/winbind/wb_async_helpers.c +++ b/source4/winbind/wb_async_helpers.c @@ -282,6 +282,12 @@ static void lsa_lookupnames_recv_sids(struct rpc_request *req) return; } + if (state->sids.count != state->num_names) { + composite_error(state->ctx, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + state->result = talloc_array(state, struct wb_sid_object *, state->num_names); if (composite_nomem(state->result, state->ctx)) return; @@ -300,9 +306,14 @@ static void lsa_lookupnames_recv_sids(struct rpc_request *req) continue; } + if (domains == NULL) { + composite_error(state->ctx, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } if (sid->sid_index >= domains->count) { composite_error(state->ctx, - NT_STATUS_INVALID_PARAMETER); + NT_STATUS_INVALID_NETWORK_RESPONSE); return; } -- 1.7.9.5 From 98edf7444395be7f1dd4b66fac4de20d3948c434 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 20 Nov 2013 13:58:34 -0800 Subject: [PATCH 15/15] CVE-2013-4408:s3:Ensure LookupRids() replies arrays are range checked. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10185 Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher --- source3/lib/netapi/group.c | 16 ++++++++++++++++ source3/lib/netapi/user.c | 16 ++++++++++++++++ source3/rpcclient/cmd_samr.c | 8 ++++++++ source3/utils/net_rpc.c | 7 +++++++ source3/winbindd/winbindd_rpc.c | 8 +++++--- 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/source3/lib/netapi/group.c b/source3/lib/netapi/group.c index 1769095..2b2c805 100644 --- a/source3/lib/netapi/group.c +++ b/source3/lib/netapi/group.c @@ -338,6 +338,14 @@ WERROR NetGroupDel_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (names.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (member_types.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } } for (i=0; i < rid_array->count; i++) { @@ -1412,6 +1420,14 @@ WERROR NetGroupGetUsers_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (names.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (member_types.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } for (i=0; i < names.count; i++) { diff --git a/source3/lib/netapi/user.c b/source3/lib/netapi/user.c index 815306f..9457951 100644 --- a/source3/lib/netapi/user.c +++ b/source3/lib/netapi/user.c @@ -2923,6 +2923,14 @@ WERROR NetUserGetGroups_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (names.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != rid_array->count) { + werr = WERR_BAD_NET_RESP; + goto done; + } for (i=0; i < names.count; i++) { status = add_GROUP_USERS_INFO_X_buffer(ctx, @@ -3448,6 +3456,14 @@ WERROR NetUserGetLocalGroups_r(struct libnetapi_ctx *ctx, werr = ntstatus_to_werror(status); goto done; } + if (names.count != num_rids) { + werr = WERR_BAD_NET_RESP; + goto done; + } + if (types.count != num_rids) { + werr = WERR_BAD_NET_RESP; + goto done; + } for (i=0; i < names.count; i++) { status = add_LOCALGROUP_USERS_INFO_X_buffer(ctx, diff --git a/source3/rpcclient/cmd_samr.c b/source3/rpcclient/cmd_samr.c index bfe3b44..3f892c2 100644 --- a/source3/rpcclient/cmd_samr.c +++ b/source3/rpcclient/cmd_samr.c @@ -1983,6 +1983,14 @@ static NTSTATUS cmd_samr_lookup_rids(struct rpc_pipe_client *cli, goto done; /* Display results */ + if (num_rids != names.count) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (num_rids != types.count) { + result = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } for (i = 0; i < num_rids; i++) { printf("rid 0x%x: %s (%d)\n", diff --git a/source3/utils/net_rpc.c b/source3/utils/net_rpc.c index 589874a..1aa7a9b 100644 --- a/source3/utils/net_rpc.c +++ b/source3/utils/net_rpc.c @@ -2446,6 +2446,13 @@ static NTSTATUS rpc_list_group_members(struct net_context *c, if (!NT_STATUS_IS_OK(result)) return result; + if (names.count != this_time) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (types.count != this_time) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + /* We only have users as members, but make the output the same as the output of alias members */ diff --git a/source3/winbindd/winbindd_rpc.c b/source3/winbindd/winbindd_rpc.c index aa8a5c8..e234dda 100644 --- a/source3/winbindd/winbindd_rpc.c +++ b/source3/winbindd/winbindd_rpc.c @@ -839,9 +839,11 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, /* Copy result into array. The talloc system will take care of freeing the temporary arrays later on. */ - - if (tmp_names.count != tmp_types.count) { - return NT_STATUS_UNSUCCESSFUL; + if (tmp_names.count != num_lookup_rids) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (tmp_types.count != num_lookup_rids) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; } for (r=0; r