From bf1ee4f36fd71b019e0c3a34e4fcbae446f8fe86 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 20 Jun 2016 16:11:37 +0200 Subject: [PATCH 1/7] s4:rpc_server: parse auth data only for BIND,ALTER_REQ,AUTH3 We should tell dcerpc_pull_auth_trailer() that we only want auth data. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11982 Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme (cherry picked from commit 505a4e68d96e6fb3d8c7493632ecb4b0fc6caa9d) --- source4/rpc_server/dcesrv_auth.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/source4/rpc_server/dcesrv_auth.c b/source4/rpc_server/dcesrv_auth.c index 2b3f8b0..802876b 100644 --- a/source4/rpc_server/dcesrv_auth.c +++ b/source4/rpc_server/dcesrv_auth.c @@ -44,7 +44,6 @@ bool dcesrv_auth_bind(struct dcesrv_call_state *call) struct dcesrv_connection *dce_conn = call->conn; struct dcesrv_auth *auth = &dce_conn->auth_state; NTSTATUS status; - uint32_t auth_length; if (pkt->auth_length == 0) { auth->auth_type = DCERPC_AUTH_TYPE_NONE; @@ -55,7 +54,7 @@ bool dcesrv_auth_bind(struct dcesrv_call_state *call) status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.bind.auth_info, &call->in_auth_info, - &auth_length, false); + NULL, true); if (!NT_STATUS_IS_OK(status)) { return false; } @@ -241,7 +240,6 @@ bool dcesrv_auth_auth3(struct dcesrv_call_state *call) struct ncacn_packet *pkt = &call->pkt; struct dcesrv_connection *dce_conn = call->conn; NTSTATUS status; - uint32_t auth_length; if (pkt->auth_length == 0) { return false; @@ -257,7 +255,7 @@ bool dcesrv_auth_auth3(struct dcesrv_call_state *call) } status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.auth3.auth_info, - &call->in_auth_info, &auth_length, true); + &call->in_auth_info, NULL, true); if (!NT_STATUS_IS_OK(status)) { return false; } @@ -324,7 +322,6 @@ bool dcesrv_auth_alter(struct dcesrv_call_state *call) struct ncacn_packet *pkt = &call->pkt; struct dcesrv_connection *dce_conn = call->conn; NTSTATUS status; - uint32_t auth_length; /* on a pure interface change there is no auth blob */ if (pkt->auth_length == 0) { @@ -344,7 +341,7 @@ bool dcesrv_auth_alter(struct dcesrv_call_state *call) } status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.alter.auth_info, - &call->in_auth_info, &auth_length, true); + &call->in_auth_info, NULL, true); if (!NT_STATUS_IS_OK(status)) { return false; } -- 1.9.1 From b6584d4868a260cdaf57e514a106ced7328e6f87 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 20 Jun 2016 16:16:23 +0200 Subject: [PATCH 2/7] s4:librpc/rpc: don't ask for auth_length if we ask for auth data only dcerpc_pull_auth_trailer() handles auth_length=NULL just fine. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11982 Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme (cherry picked from commit e05c732c6074df2524403ad7bb30eade91443525) --- source4/librpc/rpc/dcerpc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c index 55b4385..479abc0 100644 --- a/source4/librpc/rpc/dcerpc.c +++ b/source4/librpc/rpc/dcerpc.c @@ -1415,12 +1415,10 @@ static void dcerpc_bind_recv_handler(struct rpc_request *subreq, /* the bind_ack might contain a reply set of credentials */ if (pkt->auth_length != 0 && sec->tmp_auth_info.in != NULL) { - uint32_t auth_length; - status = dcerpc_pull_auth_trailer(pkt, sec->tmp_auth_info.mem, &pkt->u.bind_ack.auth_info, sec->tmp_auth_info.in, - &auth_length, true); + NULL, true); if (tevent_req_nterror(req, status)) { return; } @@ -2435,12 +2433,10 @@ static void dcerpc_alter_context_recv_handler(struct rpc_request *subreq, /* the alter_resp might contain a reply set of credentials */ if (pkt->auth_length != 0 && sec->tmp_auth_info.in != NULL) { - uint32_t auth_length; - status = dcerpc_pull_auth_trailer(pkt, sec->tmp_auth_info.mem, &pkt->u.alter_resp.auth_info, sec->tmp_auth_info.in, - &auth_length, true); + NULL, true); if (tevent_req_nterror(req, status)) { return; } -- 1.9.1 From 6be73a03ecd0498e553e3cd5c63f722df0ebf360 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 20 Jun 2016 16:17:45 +0200 Subject: [PATCH 3/7] librpc/rpc: let dcerpc_pull_auth_trailer() only accept auth_length!=NULL or auth_data_only=true BUG: https://bugzilla.samba.org/show_bug.cgi?id=11982 Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme (cherry picked from commit f386e81b982cd551313eb9c0f7d2f70d65515d80) --- librpc/rpc/dcerpc_util.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/librpc/rpc/dcerpc_util.c b/librpc/rpc/dcerpc_util.c index 43e1b7f..4d82e9a5 100644 --- a/librpc/rpc/dcerpc_util.c +++ b/librpc/rpc/dcerpc_util.c @@ -99,6 +99,14 @@ NTSTATUS dcerpc_pull_auth_trailer(const struct ncacn_packet *pkt, ZERO_STRUCTP(auth); if (_auth_length != NULL) { *_auth_length = 0; + + if (auth_data_only) { + return NT_STATUS_INTERNAL_ERROR; + } + } else { + if (!auth_data_only) { + return NT_STATUS_INTERNAL_ERROR; + } } /* Paranoia checks for auth_length. The caller should check this... */ -- 1.9.1 From 320b8496aeef9fc3f88a07440560b5631e7bb5ee Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 20 Jun 2016 16:25:12 +0200 Subject: [PATCH 4/7] librpc/rpc: let dcerpc_pull_auth_trailer() check that auth_pad_length fits within the whole pdu. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11982 Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme (cherry picked from commit 3f7e3ed8a276f16aaed87c1f3cd5b9781aa7e1af) --- librpc/rpc/dcerpc_util.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/librpc/rpc/dcerpc_util.c b/librpc/rpc/dcerpc_util.c index 4d82e9a5..ee7b307 100644 --- a/librpc/rpc/dcerpc_util.c +++ b/librpc/rpc/dcerpc_util.c @@ -95,6 +95,7 @@ NTSTATUS dcerpc_pull_auth_trailer(const struct ncacn_packet *pkt, uint16_t data_and_pad; uint16_t auth_length; uint32_t tmp_length; + uint32_t max_pad_len = 0; ZERO_STRUCTP(auth); if (_auth_length != NULL) { @@ -157,6 +158,42 @@ NTSTATUS dcerpc_pull_auth_trailer(const struct ncacn_packet *pkt, return ndr_map_error2ntstatus(ndr_err); } + /* + * Make sure the padding would not exceed + * the frag_length. + * + * Here we assume at least 24 bytes for the + * payload specific header the value of + * DCERPC_{REQUEST,RESPONSE}_LENGTH. + * + * We use this also for BIND_*, ALTER_* and AUTH3 pdus. + * + * We need this check before we ignore possible + * invalid values. See also bug #11982. + * + * This check is mainly used to generate the correct + * error for BIND_*, ALTER_* and AUTH3 pdus. + * + * We always have the 'if (data_and_pad < auth->auth_pad_length)' + * protection for REQUEST and RESPONSE pdus, where the + * auth_pad_length field is actually used by the caller. + */ + tmp_length = DCERPC_REQUEST_LENGTH; + tmp_length += DCERPC_AUTH_TRAILER_LENGTH; + tmp_length += pkt->auth_length; + if (tmp_length < pkt->frag_length) { + max_pad_len = pkt->frag_length - tmp_length; + } + if (max_pad_len < auth->auth_pad_length) { + DEBUG(1, (__location__ ": ERROR: pad length to large. " + "max %u got %u\n", + (unsigned)max_pad_len, + (unsigned)auth->auth_pad_length)); + talloc_free(ndr); + ZERO_STRUCTP(auth); + return NT_STATUS_RPC_PROTOCOL_ERROR; + } + if (data_and_pad < auth->auth_pad_length) { DEBUG(1, (__location__ ": ERROR: pad length mismatch. " "Calculated %u got %u\n", -- 1.9.1 From 2db6ef38d137eb78091eb628c2d59099e33ef37a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 20 Jun 2016 16:26:56 +0200 Subject: [PATCH 5/7] librpc/rpc: ignore invalid auth_pad_length values in BIND, ALTER and AUTH3 pdus This is a workarround for a bug in old Samba releases. For BIND_ACK <= 3.5.x and for ALTER_RESP <= 4.2.x (see bug #11061). BUG: https://bugzilla.samba.org/show_bug.cgi?id=11982 Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme (cherry picked from commit aef032302863e5f3a888dbf4c52b21d561a0dff4) --- librpc/rpc/dcerpc_util.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/librpc/rpc/dcerpc_util.c b/librpc/rpc/dcerpc_util.c index ee7b307..df14948 100644 --- a/librpc/rpc/dcerpc_util.c +++ b/librpc/rpc/dcerpc_util.c @@ -194,6 +194,22 @@ NTSTATUS dcerpc_pull_auth_trailer(const struct ncacn_packet *pkt, return NT_STATUS_RPC_PROTOCOL_ERROR; } + /* + * This is a workarround for a bug in old + * Samba releases. For BIND_ACK <= 3.5.x + * and for ALTER_RESP <= 4.2.x (see bug #11061) + * + * See also bug #11982. + */ + if (auth_data_only && data_and_pad == 0 && + auth->auth_pad_length > 0) { + /* + * we need to ignore invalid auth_pad_length + * values for BIND_*, ALTER_* and AUTH3 pdus. + */ + auth->auth_pad_length = 0; + } + if (data_and_pad < auth->auth_pad_length) { DEBUG(1, (__location__ ": ERROR: pad length mismatch. " "Calculated %u got %u\n", -- 1.9.1 From c833a742a89bb3888eaba47689c324ff193d29e5 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 23 Jun 2016 13:50:39 +0200 Subject: [PATCH 6/7] s4:rpc_server: generate the correct error when we got an invalid auth_pad_length on BIND,ALTER,AUTH3 BUG: https://bugzilla.samba.org/show_bug.cgi?id=11982 Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme (cherry picked from commit 7d8edcc24148658e92729b3d155e432994e27525) --- source4/rpc_server/dcerpc_server.c | 13 ++++++++++--- source4/rpc_server/dcesrv_auth.c | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index ea29d9d..829d2fe 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -804,6 +804,11 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) TALLOC_FREE(call->context); + if (call->fault_code == DCERPC_NCA_S_PROTO_ERROR) { + return dcesrv_bind_nak(call, + DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED); + } + if (auth->auth_level != DCERPC_AUTH_LEVEL_NONE) { /* * We only give INVALID_AUTH_TYPE if the auth_level was @@ -936,6 +941,9 @@ static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call) /* handle the auth3 in the auth code */ if (!dcesrv_auth_auth3(call)) { call->conn->auth_state.auth_invalid = true; + if (call->fault_code != 0) { + return dcesrv_fault_disconnect(call, call->fault_code); + } } talloc_free(call); @@ -1105,9 +1113,8 @@ static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call) auth_ok = dcesrv_auth_alter(call); if (!auth_ok) { - if (call->in_auth_info.auth_type == DCERPC_AUTH_TYPE_NONE) { - return dcesrv_fault_disconnect(call, - DCERPC_FAULT_ACCESS_DENIED); + if (call->fault_code != 0) { + return dcesrv_fault_disconnect(call, call->fault_code); } } diff --git a/source4/rpc_server/dcesrv_auth.c b/source4/rpc_server/dcesrv_auth.c index 802876b..74a62df 100644 --- a/source4/rpc_server/dcesrv_auth.c +++ b/source4/rpc_server/dcesrv_auth.c @@ -56,6 +56,12 @@ bool dcesrv_auth_bind(struct dcesrv_call_state *call) &call->in_auth_info, NULL, true); if (!NT_STATUS_IS_OK(status)) { + /* + * This will cause a + * DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED + * in the caller + */ + call->fault_code = DCERPC_NCA_S_PROTO_ERROR; return false; } @@ -257,6 +263,11 @@ bool dcesrv_auth_auth3(struct dcesrv_call_state *call) status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.auth3.auth_info, &call->in_auth_info, NULL, true); if (!NT_STATUS_IS_OK(status)) { + /* + * Windows returns DCERPC_NCA_S_FAULT_REMOTE_NO_MEMORY + * instead of DCERPC_NCA_S_PROTO_ERROR. + */ + call->fault_code = DCERPC_NCA_S_FAULT_REMOTE_NO_MEMORY; return false; } @@ -332,6 +343,7 @@ bool dcesrv_auth_alter(struct dcesrv_call_state *call) } if (dce_conn->auth_state.auth_finished) { + call->fault_code = DCERPC_FAULT_ACCESS_DENIED; return false; } @@ -343,6 +355,12 @@ bool dcesrv_auth_alter(struct dcesrv_call_state *call) status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.alter.auth_info, &call->in_auth_info, NULL, true); if (!NT_STATUS_IS_OK(status)) { + call->fault_code = DCERPC_NCA_S_PROTO_ERROR; + return false; + } + + if (call->in_auth_info.auth_type == DCERPC_AUTH_TYPE_NONE) { + call->fault_code = DCERPC_FAULT_ACCESS_DENIED; return false; } -- 1.9.1 From 9b56d6ae9fb6dc035bf4746e421a6da636188d7d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 23 Jun 2016 12:06:40 +0200 Subject: [PATCH 7/7] python/tests: add auth_pad test for the dcerpc raw_protocol test BUG: https://bugzilla.samba.org/show_bug.cgi?id=11982 Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme Autobuild-User(master): Stefan Metzmacher Autobuild-Date(master): Fri Jun 24 18:08:44 CEST 2016 on sn-devel-144 (cherry picked from commit c49f9abb19adca999d0b1d897d00d91f0ad91bbd) --- python/samba/tests/dcerpc/raw_protocol.py | 548 ++++++++++++++++++++++++++++++ 1 file changed, 548 insertions(+) diff --git a/python/samba/tests/dcerpc/raw_protocol.py b/python/samba/tests/dcerpc/raw_protocol.py index ccd0f6b..8b0bc4e 100755 --- a/python/samba/tests/dcerpc/raw_protocol.py +++ b/python/samba/tests/dcerpc/raw_protocol.py @@ -2616,6 +2616,554 @@ class TestDCERPC_BIND(RawDCERPCTest): self.assertIsNone(rep) self.assertNotConnected() + def test_spnego_auth_pad_ok(self): + ndr32 = base.transfer_syntax_ndr() + + tsf1_list = [ndr32] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1.transfer_syntaxes = tsf1_list + ctx_list = [ctx1] + + c = Credentials() + c.set_anonymous() + g = gensec.Security.start_client(self.settings) + g.set_credentials(c) + g.want_feature(gensec.FEATURE_DCE_STYLE) + auth_type = dcerpc.DCERPC_AUTH_TYPE_SPNEGO + auth_level = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = "" + (finished, to_server) = g.update(from_server) + self.assertFalse(finished) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob=to_server) + + req = self.generate_bind(call_id=0, + ctx_list=ctx_list, + auth_info=auth_info) + req_pdu = samba.ndr.ndr_pack(req) + + auth_pad_ok = len(req_pdu) + auth_pad_ok -= dcerpc.DCERPC_REQUEST_LENGTH + auth_pad_ok -= dcerpc.DCERPC_AUTH_TRAILER_LENGTH + auth_pad_ok -= len(to_server) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_pad_length=auth_pad_ok, + auth_blob=to_server) + + req = self.generate_bind(call_id=0, + ctx_list=ctx_list, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id) + self.assertEquals(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEquals(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEquals(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEquals(rep.u.secondary_address_size, 4) + self.assertEquals(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertEquals(len(rep.u._pad1), 2) + #self.assertEquals(rep.u._pad1, '\0' * 2) + self.assertEquals(rep.u.num_results, 1) + self.assertEquals(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEquals(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEquals(len(rep.u.auth_info), 0) + a = self.parse_auth(rep.u.auth_info) + + from_server = a.credentials + (finished, to_server) = g.update(from_server) + self.assertFalse(finished) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob=to_server) + req = self.generate_alter(call_id=0, + ctx_list=ctx_list, + assoc_group_id=rep.u.assoc_group_id, + auth_info=auth_info) + req_pdu = samba.ndr.ndr_pack(req) + + auth_pad_ok = len(req_pdu) + auth_pad_ok -= dcerpc.DCERPC_REQUEST_LENGTH + auth_pad_ok -= dcerpc.DCERPC_AUTH_TRAILER_LENGTH + auth_pad_ok -= len(to_server) + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_pad_length=auth_pad_ok, + auth_blob=to_server) + req = self.generate_alter(call_id=0, + ctx_list=ctx_list, + assoc_group_id=rep.u.assoc_group_id, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id) + self.assertEquals(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEquals(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertEquals(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEquals(rep.u.secondary_address_size, 0) + self.assertEquals(len(rep.u._pad1), 2) + # Windows sends garbage + #self.assertEquals(rep.u._pad1, '\0' * 2) + self.assertEquals(rep.u.num_results, 1) + self.assertEquals(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEquals(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEquals(len(rep.u.auth_info), 0) + a = self.parse_auth(rep.u.auth_info) + + from_server = a.credentials + (finished, to_server) = g.update(from_server) + self.assertTrue(finished) + + # And now try a request without auth_info + req = self.generate_request(call_id = 2, + context_id=ctx1.context_id, + opnum=0, + stub="") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEquals(rep.u.alloc_hint, 0) + self.assertEquals(rep.u.context_id, req.u.context_id) + self.assertEquals(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + # Now a request with auth_info DCERPC_AUTH_LEVEL_CONNECT + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob="\x01"+"\x00"*15) + req = self.generate_request(call_id = 3, + context_id=ctx1.context_id, + opnum=0, + stub="", + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + # We don't get an auth_info back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEquals(rep.u.alloc_hint, 0) + self.assertEquals(rep.u.context_id, req.u.context_id) + self.assertEquals(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + self._disconnect("disconnect") + self.assertNotConnected() + + def test_spnego_auth_pad_fail_bind(self): + ndr32 = base.transfer_syntax_ndr() + + tsf1_list = [ndr32] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1.transfer_syntaxes = tsf1_list + ctx_list = [ctx1] + + c = Credentials() + c.set_anonymous() + g = gensec.Security.start_client(self.settings) + g.set_credentials(c) + g.want_feature(gensec.FEATURE_DCE_STYLE) + auth_type = dcerpc.DCERPC_AUTH_TYPE_SPNEGO + auth_level = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = "" + (finished, to_server) = g.update(from_server) + self.assertFalse(finished) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob=to_server) + + req = self.generate_bind(call_id=0, + ctx_list=ctx_list, + auth_info=auth_info) + req_pdu = samba.ndr.ndr_pack(req) + + auth_pad_ok = len(req_pdu) + auth_pad_ok -= dcerpc.DCERPC_REQUEST_LENGTH + auth_pad_ok -= dcerpc.DCERPC_AUTH_TRAILER_LENGTH + auth_pad_ok -= len(to_server) + auth_pad_bad = auth_pad_ok + 1 + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_pad_length=auth_pad_bad, + auth_blob=to_server) + + req = self.generate_bind(call_id=0, + ctx_list=ctx_list, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_NAK, req.call_id, + auth_length=0) + self.assertEquals(rep.u.reject_reason, + dcerpc.DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED) + self.assertEquals(rep.u.num_versions, 1) + self.assertEquals(rep.u.versions[0].rpc_vers, req.rpc_vers) + self.assertEquals(rep.u.versions[0].rpc_vers_minor, req.rpc_vers_minor) + self.assertEquals(len(rep.u._pad), 3) + self.assertEquals(rep.u._pad, '\0' * 3) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_auth_pad_fail_alter(self): + ndr32 = base.transfer_syntax_ndr() + + tsf1_list = [ndr32] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1.transfer_syntaxes = tsf1_list + ctx_list = [ctx1] + + c = Credentials() + c.set_anonymous() + g = gensec.Security.start_client(self.settings) + g.set_credentials(c) + g.want_feature(gensec.FEATURE_DCE_STYLE) + auth_type = dcerpc.DCERPC_AUTH_TYPE_SPNEGO + auth_level = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = "" + (finished, to_server) = g.update(from_server) + self.assertFalse(finished) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob=to_server) + + req = self.generate_bind(call_id=0, + ctx_list=ctx_list, + auth_info=auth_info) + req_pdu = samba.ndr.ndr_pack(req) + + auth_pad_ok = len(req_pdu) + auth_pad_ok -= dcerpc.DCERPC_REQUEST_LENGTH + auth_pad_ok -= dcerpc.DCERPC_AUTH_TRAILER_LENGTH + auth_pad_ok -= len(to_server) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_pad_length=auth_pad_ok, + auth_blob=to_server) + + req = self.generate_bind(call_id=0, + ctx_list=ctx_list, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id) + self.assertEquals(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEquals(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEquals(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEquals(rep.u.secondary_address_size, 4) + self.assertEquals(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertEquals(len(rep.u._pad1), 2) + #self.assertEquals(rep.u._pad1, '\0' * 2) + self.assertEquals(rep.u.num_results, 1) + self.assertEquals(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEquals(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEquals(len(rep.u.auth_info), 0) + a = self.parse_auth(rep.u.auth_info) + + from_server = a.credentials + (finished, to_server) = g.update(from_server) + self.assertFalse(finished) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob=to_server) + req = self.generate_alter(call_id=0, + ctx_list=ctx_list, + assoc_group_id=rep.u.assoc_group_id, + auth_info=auth_info) + req_pdu = samba.ndr.ndr_pack(req) + + auth_pad_ok = len(req_pdu) + auth_pad_ok -= dcerpc.DCERPC_REQUEST_LENGTH + auth_pad_ok -= dcerpc.DCERPC_AUTH_TRAILER_LENGTH + auth_pad_ok -= len(to_server) + auth_pad_bad = auth_pad_ok + 1 + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_pad_length=auth_pad_bad, + auth_blob=to_server) + req = self.generate_alter(call_id=0, + ctx_list=ctx_list, + assoc_group_id=rep.u.assoc_group_id, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + pfc_flags=req.pfc_flags | + dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE, + auth_length=0) + self.assertNotEquals(rep.u.alloc_hint, 0) + self.assertEquals(rep.u.context_id, 0) + self.assertEquals(rep.u.cancel_count, 0) + self.assertEquals(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEquals(len(rep.u._pad), 4) + self.assertEquals(rep.u._pad, '\0' * 4) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_ntlmssp_auth_pad_ok(self): + ndr32 = base.transfer_syntax_ndr() + + tsf1_list = [ndr32] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1.transfer_syntaxes = tsf1_list + ctx_list = [ctx1] + + c = Credentials() + c.set_anonymous() + g = gensec.Security.start_client(self.settings) + g.set_credentials(c) + g.want_feature(gensec.FEATURE_DCE_STYLE) + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = "" + (finished, to_server) = g.update(from_server) + self.assertFalse(finished) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob=to_server) + + req = self.generate_bind(call_id=0, + ctx_list=ctx_list, + auth_info=auth_info) + req_pdu = samba.ndr.ndr_pack(req) + + auth_pad_ok = len(req_pdu) + auth_pad_ok -= dcerpc.DCERPC_REQUEST_LENGTH + auth_pad_ok -= dcerpc.DCERPC_AUTH_TRAILER_LENGTH + auth_pad_ok -= len(to_server) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_pad_length=auth_pad_ok, + auth_blob=to_server) + + req = self.generate_bind(call_id=0, + ctx_list=ctx_list, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id) + self.assertEquals(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEquals(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEquals(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEquals(rep.u.secondary_address_size, 4) + self.assertEquals(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertEquals(len(rep.u._pad1), 2) + #self.assertEquals(rep.u._pad1, '\0' * 2) + self.assertEquals(rep.u.num_results, 1) + self.assertEquals(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEquals(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEquals(len(rep.u.auth_info), 0) + a = self.parse_auth(rep.u.auth_info) + + from_server = a.credentials + (finished, to_server) = g.update(from_server) + self.assertTrue(finished) + + auth_pad_ok = 0 + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_pad_length=auth_pad_ok, + auth_blob=to_server) + req = self.generate_auth3(call_id=0, + auth_info=auth_info) + self.send_pdu(req) + self.assertIsConnected() + + # And now try a request without auth_info + req = self.generate_request(call_id = 2, + context_id=ctx1.context_id, + opnum=0, + stub="") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEquals(rep.u.alloc_hint, 0) + self.assertEquals(rep.u.context_id, req.u.context_id) + self.assertEquals(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + # Now a request with auth_info DCERPC_AUTH_LEVEL_CONNECT + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob="\x01"+"\x00"*15) + req = self.generate_request(call_id = 3, + context_id=ctx1.context_id, + opnum=0, + stub="", + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + # We don't get an auth_info back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEquals(rep.u.alloc_hint, 0) + self.assertEquals(rep.u.context_id, req.u.context_id) + self.assertEquals(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + self._disconnect("disconnect") + self.assertNotConnected() + + def test_ntlmssp_auth_pad_fail_auth3(self): + ndr32 = base.transfer_syntax_ndr() + + tsf1_list = [ndr32] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1.transfer_syntaxes = tsf1_list + ctx_list = [ctx1] + + c = Credentials() + c.set_anonymous() + g = gensec.Security.start_client(self.settings) + g.set_credentials(c) + g.want_feature(gensec.FEATURE_DCE_STYLE) + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = "" + (finished, to_server) = g.update(from_server) + self.assertFalse(finished) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob=to_server) + + req = self.generate_bind(call_id=0, + ctx_list=ctx_list, + auth_info=auth_info) + req_pdu = samba.ndr.ndr_pack(req) + + auth_pad_ok = len(req_pdu) + auth_pad_ok -= dcerpc.DCERPC_REQUEST_LENGTH + auth_pad_ok -= dcerpc.DCERPC_AUTH_TRAILER_LENGTH + auth_pad_ok -= len(to_server) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_pad_length=auth_pad_ok, + auth_blob=to_server) + + req = self.generate_bind(call_id=0, + ctx_list=ctx_list, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id) + self.assertEquals(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEquals(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEquals(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEquals(rep.u.secondary_address_size, 4) + self.assertEquals(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertEquals(len(rep.u._pad1), 2) + #self.assertEquals(rep.u._pad1, '\0' * 2) + self.assertEquals(rep.u.num_results, 1) + self.assertEquals(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEquals(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEquals(len(rep.u.auth_info), 0) + a = self.parse_auth(rep.u.auth_info) + + from_server = a.credentials + (finished, to_server) = g.update(from_server) + self.assertTrue(finished) + + auth_pad_bad = 1 + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_pad_length=auth_pad_bad, + auth_blob=to_server) + req = self.generate_auth3(call_id=0, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + pfc_flags=req.pfc_flags | + dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE, + auth_length=0) + self.assertNotEquals(rep.u.alloc_hint, 0) + self.assertEquals(rep.u.context_id, 0) + self.assertEquals(rep.u.cancel_count, 0) + self.assertEquals(rep.u.status, dcerpc.DCERPC_NCA_S_FAULT_REMOTE_NO_MEMORY) + self.assertEquals(len(rep.u._pad), 4) + self.assertEquals(rep.u._pad, '\0' * 4) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + if __name__ == "__main__": global_ndr_print = True global_hexdump = True -- 1.9.1