diff options
Diffstat (limited to 'python/samba/tests/dcerpc')
21 files changed, 12963 insertions, 0 deletions
diff --git a/python/samba/tests/dcerpc/__init__.py b/python/samba/tests/dcerpc/__init__.py new file mode 100644 index 0000000..b8df5a2 --- /dev/null +++ b/python/samba/tests/dcerpc/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +"""Tests for the DCE/RPC Python bindings.""" diff --git a/python/samba/tests/dcerpc/array.py b/python/samba/tests/dcerpc/array.py new file mode 100644 index 0000000..073d2c2 --- /dev/null +++ b/python/samba/tests/dcerpc/array.py @@ -0,0 +1,206 @@ +# Unix SMB/CIFS implementation. +# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2016 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for array handling in PIDL generated bindings samba.dcerpc.*""" + +from samba.dcerpc import drsblobs +import samba.tests +from samba.ndr import ndr_unpack, ndr_pack +import talloc +import gc + + +class ArrayTests(samba.tests.TestCase): + + def setUp(self): + super().setUp() + talloc.enable_null_tracking() + self.startup_blocks = talloc.total_blocks() + + def tearDown(self): + super().tearDown() + gc.collect() + if talloc.total_blocks() != self.startup_blocks: + talloc.report_full() + self.fail("it appears we are leaking memory") + + def test_array_from_python(self): + rmd = drsblobs.replPropertyMetaDataBlob() + + rmd.version = 1 + rmd.ctr = drsblobs.replPropertyMetaDataCtr1() + rmd.ctr.count = 3 + + rmd1 = drsblobs.replPropertyMetaData1() + rmd1.attid = 1 + rmd1.version = 2 + + rmd2 = drsblobs.replPropertyMetaData1() + rmd2.attid = 2 + rmd2.version = 2 + + rmd3 = drsblobs.replPropertyMetaData1() + rmd3.attid = 3 + rmd3.version = 2 + + rmd.ctr.array = [rmd1, rmd2, rmd3] + gc.collect() + + self.assertIsNotNone(rmd) + self.assertEqual(rmd.version, 1) + self.assertIsNotNone(rmd.ctr) + self.assertEqual(rmd.ctr.count, 3) + self.assertEqual(len(rmd.ctr.array), rmd.ctr.count) + self.assertIsNotNone(rmd.ctr.array[0]) + self.assertEqual(rmd.ctr.array[0].attid, 1) + + def test_array_with_exception(self): + try: + rmd = drsblobs.replPropertyMetaDataBlob() + + rmd.version = 1 + rmd.ctr = drsblobs.replPropertyMetaDataCtr1() + rmd.ctr.count = 3 + + rmd1 = drsblobs.replPropertyMetaData1() + rmd1.attid = 1 + rmd1.version = 2 + + rmd2 = drsblobs.replPropertyMetaData1() + rmd2.attid = 2 + rmd2.version = 2 + + rmd3 = drsblobs.replPropertyMetaData1() + rmd3.attid = 3 + rmd3.version = 2 + + rmd.ctr.array = [rmd1, rmd2, rmd3] + + gc.collect() + + self.assertIsNotNone(rmd) + self.assertEqual(rmd.version, 1) + self.assertIsNotNone(rmd.ctr) + self.assertEqual(rmd.ctr.count, 3) + self.assertEqual(len(rmd.ctr.array), rmd.ctr.count) + self.assertIsNotNone(rmd.ctr.array[0]) + self.assertEqual(rmd.ctr.array[0].attid, 1) + + raise Exception() + except: + pass + + def test_array_from_python_function(self): + def get_rmd(): + rmd = drsblobs.replPropertyMetaDataBlob() + + rmd.version = 1 + rmd.ctr = drsblobs.replPropertyMetaDataCtr1() + rmd.ctr.count = 3 + + rmd1 = drsblobs.replPropertyMetaData1() + rmd1.attid = 1 + rmd1.version = 2 + + rmd2 = drsblobs.replPropertyMetaData1() + rmd2.attid = 2 + rmd2.version = 2 + + rmd3 = drsblobs.replPropertyMetaData1() + rmd3.attid = 3 + rmd3.version = 2 + + rmd.ctr.array = [rmd1, rmd2, rmd3] + return rmd + + rmd = get_rmd() + gc.collect() + self.assertIsNotNone(rmd) + self.assertEqual(rmd.version, 1) + self.assertIsNotNone(rmd.ctr) + self.assertEqual(rmd.ctr.count, 3) + self.assertEqual(len(rmd.ctr.array), rmd.ctr.count) + self.assertIsNotNone(rmd.ctr.array[0]) + self.assertEqual(rmd.ctr.array[0].attid, 1) + + def test_array_from_ndr(self): + rmd = drsblobs.replPropertyMetaDataBlob() + + rmd.version = 1 + rmd.ctr = drsblobs.replPropertyMetaDataCtr1() + rmd.ctr.count = 3 + + rmd1 = drsblobs.replPropertyMetaData1() + rmd1.attid = 1 + rmd1.version = 2 + + rmd2 = drsblobs.replPropertyMetaData1() + rmd2.attid = 2 + rmd2.version = 2 + + rmd3 = drsblobs.replPropertyMetaData1() + rmd3.attid = 3 + rmd3.version = 2 + + rmd.ctr.array = [rmd1, rmd2, rmd3] + + packed = ndr_pack(rmd) + gc.collect() + + rmd_unpacked = ndr_unpack(drsblobs.replPropertyMetaDataBlob, packed) + self.assertIsNotNone(rmd_unpacked) + self.assertEqual(rmd_unpacked.version, 1) + self.assertIsNotNone(rmd_unpacked.ctr) + self.assertEqual(rmd_unpacked.ctr.count, 3) + self.assertEqual(len(rmd_unpacked.ctr.array), rmd_unpacked.ctr.count) + self.assertIsNotNone(rmd_unpacked.ctr.array[0]) + self.assertEqual(rmd_unpacked.ctr.array[0].attid, 1) + + self.assertEqual(rmd.ctr.array[0].attid, + rmd_unpacked.ctr.array[0].attid) + + def test_array_delete(self): + rmd = drsblobs.replPropertyMetaDataBlob() + + rmd.version = 1 + rmd.ctr = drsblobs.replPropertyMetaDataCtr1() + rmd.ctr.count = 3 + + rmd1 = drsblobs.replPropertyMetaData1() + rmd1.attid = 1 + rmd1.version = 2 + + rmd2 = drsblobs.replPropertyMetaData1() + rmd2.attid = 2 + rmd2.version = 2 + + rmd3 = drsblobs.replPropertyMetaData1() + rmd3.attid = 3 + rmd3.version = 2 + + rmd.ctr.array = [rmd1, rmd2, rmd3] + try: + del rmd1.version + self.fail("succeeded in deleting rmd1.version") + except AttributeError as e: + pass + + try: + del rmd.ctr.array + self.fail("succeeded in deleting rmd.ctr.array") + except AttributeError as e: + pass diff --git a/python/samba/tests/dcerpc/bare.py b/python/samba/tests/dcerpc/bare.py new file mode 100644 index 0000000..6229652 --- /dev/null +++ b/python/samba/tests/dcerpc/bare.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.tests.dcerpc.bare.""" + +import os +from samba.dcerpc import ClientConnection +import samba.tests + + +class BareTestCase(samba.tests.TestCase): + + def test_bare(self): + # Connect to the echo pipe + x = ClientConnection("ncalrpc:localhost[DEFAULT]", + ("60a15ec5-4de8-11d7-a637-005056a20182", 1), + lp_ctx=samba.tests.env_loadparm()) + self.assertEqual(b"\x01\x00\x00\x00", x.request(0, chr(0) * 4)) + + def test_two_contexts(self): + x = ClientConnection("ncalrpc:localhost[DEFAULT]", + ("12345778-1234-abcd-ef00-0123456789ac", 1), + lp_ctx=samba.tests.env_loadparm()) + y = ClientConnection("ncalrpc:localhost", + ("60a15ec5-4de8-11d7-a637-005056a20182", 1), + basis_connection=x, lp_ctx=samba.tests.env_loadparm()) + self.assertEqual(24, len(x.request(0, chr(0) * 8))) + self.assertEqual(b"\x01\x00\x00\x00", y.request(0, chr(0) * 4)) + + def test_bare_tcp(self): + # Connect to the echo pipe + x = ClientConnection("ncacn_ip_tcp:%s" % os.environ["SERVER"], + ("60a15ec5-4de8-11d7-a637-005056a20182", 1), + lp_ctx=samba.tests.env_loadparm()) + self.assertEqual(b"\x01\x00\x00\x00", x.request(0, chr(0) * 4)) + + def test_two_contexts_tcp(self): + x = ClientConnection("ncacn_ip_tcp:%s" % os.environ["SERVER"], + ("12345778-1234-abcd-ef00-0123456789ac", 1), + lp_ctx=samba.tests.env_loadparm()) + y = ClientConnection("ncacn_ip_tcp:%s" % os.environ["SERVER"], + ("60a15ec5-4de8-11d7-a637-005056a20182", 1), + basis_connection=x, lp_ctx=samba.tests.env_loadparm()) + self.assertEqual(24, len(x.request(0, chr(0) * 8))) + self.assertEqual(b"\x01\x00\x00\x00", y.request(0, chr(0) * 4)) diff --git a/python/samba/tests/dcerpc/binding.py b/python/samba/tests/dcerpc/binding.py new file mode 100644 index 0000000..1ad1f29 --- /dev/null +++ b/python/samba/tests/dcerpc/binding.py @@ -0,0 +1,101 @@ +# +# Unix SMB/CIFS implementation. +# Copyright (c) 2020 Andreas Schneider <asn@samba.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.dcerpc., credentials and binding strings""" + +import samba.tests +from samba.tests import RpcInterfaceTestCase, TestCase +from samba.dcerpc import lsa +import samba.dcerpc.security as security +from samba.credentials import Credentials, SMB_ENCRYPTION_REQUIRED, SMB_ENCRYPTION_OFF +from samba import NTSTATUSError + +class RpcBindingTests(RpcInterfaceTestCase): + + def get_user_creds(self): + c = Credentials() + c.guess() + domain = samba.tests.env_get_var_value('DOMAIN') + username = samba.tests.env_get_var_value('USERNAME') + password = samba.tests.env_get_var_value('PASSWORD') + c.set_domain(domain) + c.set_username(username) + c.set_password(password) + return c + + def test_smb3_dcerpc_no_encryption(self): + creds = self.get_user_creds() + creds.set_smb_encryption(SMB_ENCRYPTION_OFF) + + lp = self.get_loadparm() + lp.set('client ipc max protocol', 'SMB3') + lp.set('client ipc min protocol', 'SMB3') + + binding_string = ("ncacn_np:%s" % (samba.tests.env_get_var_value('SERVER'))) + lsa_conn = lsa.lsarpc(binding_string, lp, creds) + self.assertFalse(lsa_conn.transport_encrypted()) + + objectAttr = lsa.ObjectAttribute() + objectAttr.sec_qos = lsa.QosInfo() + + pol_handle = lsa_conn.OpenPolicy2('', + objectAttr, + security.SEC_FLAG_MAXIMUM_ALLOWED) + self.assertIsNotNone(pol_handle) + + def test_smb3_dcerpc_encryption(self): + creds = self.get_user_creds() + creds.set_smb_encryption(SMB_ENCRYPTION_REQUIRED) + + lp = self.get_loadparm() + lp.set('client ipc max protocol', 'SMB3') + lp.set('client ipc min protocol', 'SMB3') + + binding_string = ("ncacn_np:%s" % (samba.tests.env_get_var_value('SERVER'))) + lsa_conn = lsa.lsarpc(binding_string, lp, creds) + self.assertTrue(lsa_conn.transport_encrypted()) + + objectAttr = lsa.ObjectAttribute() + objectAttr.sec_qos = lsa.QosInfo() + + pol_handle = lsa_conn.OpenPolicy2('', + objectAttr, + security.SEC_FLAG_MAXIMUM_ALLOWED) + self.assertIsNotNone(pol_handle) + + def test_smb2_dcerpc_encryption(self): + creds = self.get_user_creds() + creds.set_smb_encryption(SMB_ENCRYPTION_REQUIRED) + + lp = self.get_loadparm() + lp.set('client ipc max protocol', 'SMB2') + lp.set('client ipc min protocol', 'SMB2') + + binding_string = ("ncacn_np:%s" % (samba.tests.env_get_var_value('SERVER'))) + self.assertRaises(NTSTATUSError, lsa.lsarpc, binding_string, lp, creds) + + def test_smb1_dcerpc_encryption(self): + creds = self.get_user_creds() + creds.set_smb_encryption(SMB_ENCRYPTION_REQUIRED) + + lp = self.get_loadparm() + lp.set('client ipc max protocol', 'NT1') + lp.set('client ipc min protocol', 'NT1') + + binding_string = ("ncacn_np:%s" % (samba.tests.env_get_var_value('SERVER'))) + self.assertRaises(NTSTATUSError, lsa.lsarpc, binding_string, lp, creds) diff --git a/python/samba/tests/dcerpc/createtrustrelax.py b/python/samba/tests/dcerpc/createtrustrelax.py new file mode 100644 index 0000000..5dcb937 --- /dev/null +++ b/python/samba/tests/dcerpc/createtrustrelax.py @@ -0,0 +1,129 @@ +# Unix SMB/CIFS implementation. +# +# Copyright (C) Andrew Bartlett 2011 +# Copyright (C) Isaac Boukris 2020 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for the CreateTrustedDomainRelax wrapper""" + +import os +import samba +from samba.tests import TestCase +from samba.dcerpc import lsa, security, drsblobs +from samba.credentials import Credentials, SMB_ENCRYPTION_REQUIRED, SMB_ENCRYPTION_OFF +from samba.trust_utils import CreateTrustedDomainRelax + +class CreateTrustedDomainRelaxTest(TestCase): + + def get_user_creds(self): + c = Credentials() + c.guess() + domain = samba.tests.env_get_var_value('DOMAIN') + username = samba.tests.env_get_var_value('USERNAME') + password = samba.tests.env_get_var_value('PASSWORD') + c.set_domain(domain) + c.set_username(username) + c.set_password(password) + return c + + def _create_trust_relax(self, smbencrypt=True): + creds = self.get_user_creds() + + if smbencrypt: + creds.set_smb_encryption(SMB_ENCRYPTION_REQUIRED) + else: + creds.set_smb_encryption(SMB_ENCRYPTION_OFF) + + lp = self.get_loadparm() + + binding_string = ("ncacn_np:%s" % (samba.tests.env_get_var_value('SERVER'))) + lsa_conn = lsa.lsarpc(binding_string, lp, creds) + + if smbencrypt: + self.assertTrue(lsa_conn.transport_encrypted()) + else: + self.assertFalse(lsa_conn.transport_encrypted()) + + objectAttr = lsa.ObjectAttribute() + objectAttr.sec_qos = lsa.QosInfo() + + pol_handle = lsa_conn.OpenPolicy2('', + objectAttr, + security.SEC_FLAG_MAXIMUM_ALLOWED) + self.assertIsNotNone(pol_handle) + + name = lsa.String() + name.string = "tests.samba.example.com" + try: + info = lsa_conn.QueryTrustedDomainInfoByName(pol_handle, name, + lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO) + + lsa_conn.DeleteTrustedDomain(pol_handle, info.info_ex.sid) + except RuntimeError: + pass + + info = lsa.TrustDomainInfoInfoEx() + info.domain_name.string = name.string + info.netbios_name.string = "createtrustrelax" + info.sid = security.dom_sid("S-1-5-21-538490383-3740119673-95748416") + info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND + info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL + info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE + + password_blob = samba.string_to_byte_array("password".encode('utf-16-le')) + + clear_value = drsblobs.AuthInfoClear() + clear_value.size = len(password_blob) + clear_value.password = password_blob + + clear_authentication_information = drsblobs.AuthenticationInformation() + clear_authentication_information.LastUpdateTime = 0 + clear_authentication_information.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR + clear_authentication_information.AuthInfo = clear_value + + authentication_information_array = drsblobs.AuthenticationInformationArray() + authentication_information_array.count = 1 + authentication_information_array.array = [clear_authentication_information] + + outgoing = drsblobs.trustAuthInOutBlob() + outgoing.count = 1 + outgoing.current = authentication_information_array + + trustdom_handle = None + try: + trustdom_handle = CreateTrustedDomainRelax(lsa_conn, + pol_handle, + info, + security.SEC_STD_DELETE, + outgoing, + outgoing) + except samba.NTSTATUSError as nt: + raise AssertionError(nt) + except OSError as e: + if smbencrypt: + raise AssertionError(e) + + if smbencrypt: + self.assertIsNotNone(trustdom_handle) + lsa_conn.DeleteTrustedDomain(pol_handle, info.sid) + else: + self.assertIsNone(trustdom_handle) + + def test_create_trust_relax_encrypt(self): + self._create_trust_relax(True) + + def test_create_trust_relax_no_enc(self): + self._create_trust_relax(False) diff --git a/python/samba/tests/dcerpc/dnsserver.py b/python/samba/tests/dcerpc/dnsserver.py new file mode 100644 index 0000000..13c9af8 --- /dev/null +++ b/python/samba/tests/dcerpc/dnsserver.py @@ -0,0 +1,1314 @@ +# Unix SMB/CIFS implementation. +# Copyright (C) Amitay Isaacs <amitay@gmail.com> 2011 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.dcerpc.dnsserver""" + +import os +import ldb + +from samba.auth import system_session +from samba.samdb import SamDB +from samba.ndr import ndr_unpack +from samba.dcerpc import dnsp, dnsserver, security +from samba.tests import RpcInterfaceTestCase, env_get_var_value +from samba.dnsserver import record_from_string, flag_from_string, ARecord +from samba import sd_utils, descriptor +from samba import WERRORError, werror + + +class DnsserverTests(RpcInterfaceTestCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + + good_dns = ["SAMDOM.EXAMPLE.COM", + "1.EXAMPLE.COM", + "%sEXAMPLE.COM" % ("1." * 100), + "EXAMPLE", + "\n.COM", + "!@#$%^&*()_", + "HIGH\xFFBYTE", + "@.EXAMPLE.COM", + "."] + bad_dns = ["...", + ".EXAMPLE.COM", + ".EXAMPLE.", + "", + "SAMDOM..EXAMPLE.COM"] + + good_mx = ["SAMDOM.EXAMPLE.COM 65535"] + bad_mx = [] + + good_srv = ["SAMDOM.EXAMPLE.COM 65535 65535 65535"] + bad_srv = [] + + for bad_dn in bad_dns: + bad_mx.append("%s 1" % bad_dn) + bad_srv.append("%s 0 0 0" % bad_dn) + for good_dn in good_dns: + good_mx.append("%s 1" % good_dn) + good_srv.append("%s 0 0 0" % good_dn) + + cls.good_records = { + "A": ["192.168.0.1", + "255.255.255.255"], + "AAAA": ["1234:5678:9ABC:DEF0:0000:0000:0000:0000", + "0000:0000:0000:0000:0000:0000:0000:0000", + "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0", + "1234:1234:1234::", + "1234:1234:1234:1234:1234::", + "1234:5678:9ABC:DEF0::", + "0000:0000::0000", + "1234::5678:9ABC:0000:0000:0000:0000", + "::1", + "::", + "1:1:1:1:1:1:1:1"], + "PTR": good_dns, + "CNAME": good_dns, + "NS": good_dns, + "MX": good_mx, + "SRV": good_srv, + "TXT": ["text", "", "@#!", "\n"] + } + + cls.bad_records = { + "A": ["192.168.0.500", + "255.255.255.255/32"], + "AAAA": ["GGGG:1234:5678:9ABC:0000:0000:0000:0000", + "0000:0000:0000:0000:0000:0000:0000:0000/1", + "AAAA:AAAA:AAAA:AAAA:G000:0000:0000:1234", + "1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0:1234", + "1234:5678:9ABC:DEF0:1234:5678:9ABC", + "1111::1111::1111"], + "PTR": bad_dns, + "CNAME": bad_dns, + "NS": bad_dns, + "MX": bad_mx, + "SRV": bad_srv + } + + # Because we use uint16_t for these numbers, we can't + # actually create these records. + invalid_mx = ["SAMDOM.EXAMPLE.COM -1", + "SAMDOM.EXAMPLE.COM 65536", + "%s 1" % ("A" * 256)] + invalid_srv = ["SAMDOM.EXAMPLE.COM 0 65536 0", + "SAMDOM.EXAMPLE.COM 0 0 65536", + "SAMDOM.EXAMPLE.COM 65536 0 0"] + cls.invalid_records = { + "MX": invalid_mx, + "SRV": invalid_srv + } + + def setUp(self): + super().setUp() + self.server = os.environ["DC_SERVER"] + self.zone = env_get_var_value("REALM").lower() + self.conn = dnsserver.dnsserver("ncacn_ip_tcp:%s[sign]" % (self.server), + self.get_loadparm(), + self.get_credentials()) + + self.samdb = SamDB(url="ldap://%s" % os.environ["DC_SERVER_IP"], + lp=self.get_loadparm(), + session_info=system_session(), + credentials=self.get_credentials()) + + self.custom_zone = "zone" + zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN() + zone_create_info.pszZoneName = self.custom_zone + zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY + zone_create_info.fAging = 0 + zone_create_info.fDsIntegrated = 1 + zone_create_info.fLoadExisting = 1 + zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT + + self.conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN, + 0, + self.server, + None, + 0, + 'ZoneCreate', + dnsserver.DNSSRV_TYPEID_ZONE_CREATE, + zone_create_info) + + def tearDown(self): + self.conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN, + 0, + self.server, + self.custom_zone, + 0, + 'DeleteZoneFromDs', + dnsserver.DNSSRV_TYPEID_NULL, + None) + super().tearDown() + + def test_enum_is_sorted(self): + """ + Confirm the zone is sorted + """ + + record_str = "192.168.50.50" + record_type_str = "A" + self.add_record(self.custom_zone, "atestrecord-1", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-2", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-3", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-4", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-0", record_type_str, record_str) + + # This becomes an extra A on the zone itself by server-side magic + self.add_record(self.custom_zone, self.custom_zone, record_type_str, record_str) + + _, result = self.conn.DnssrvEnumRecords2(dnsserver.DNS_CLIENT_VERSION_LONGHORN, + 0, + self.server, + self.custom_zone, + "@", + None, + flag_from_string(record_type_str), + dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA, + None, + None) + + self.assertEqual(len(result.rec), 6) + self.assertEqual(result.rec[0].dnsNodeName.str, "") + self.assertEqual(result.rec[1].dnsNodeName.str, "atestrecord-0") + self.assertEqual(result.rec[2].dnsNodeName.str, "atestrecord-1") + self.assertEqual(result.rec[3].dnsNodeName.str, "atestrecord-2") + self.assertEqual(result.rec[4].dnsNodeName.str, "atestrecord-3") + self.assertEqual(result.rec[5].dnsNodeName.str, "atestrecord-4") + + def test_enum_is_sorted_with_zone_dup(self): + """ + Confirm the zone is sorted + """ + + record_str = "192.168.50.50" + record_type_str = "A" + self.add_record(self.custom_zone, "atestrecord-1", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-2", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-3", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-4", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-0", record_type_str, record_str) + + # This triggers a bug in old Samba + self.add_record(self.custom_zone, self.custom_zone + "1", record_type_str, record_str) + + dn, record = self.get_record_from_db(self.custom_zone, self.custom_zone + "1") + + new_dn = ldb.Dn(self.samdb, str(dn)) + new_dn.set_component(0, "dc", self.custom_zone) + self.samdb.rename(dn, new_dn) + + _, result = self.conn.DnssrvEnumRecords2(dnsserver.DNS_CLIENT_VERSION_LONGHORN, + 0, + self.server, + self.custom_zone, + "@", + None, + flag_from_string(record_type_str), + dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA, + None, + None) + + self.assertEqual(len(result.rec), 7) + self.assertEqual(result.rec[0].dnsNodeName.str, "") + self.assertEqual(result.rec[1].dnsNodeName.str, "atestrecord-0") + self.assertEqual(result.rec[2].dnsNodeName.str, "atestrecord-1") + self.assertEqual(result.rec[3].dnsNodeName.str, "atestrecord-2") + self.assertEqual(result.rec[4].dnsNodeName.str, "atestrecord-3") + self.assertEqual(result.rec[5].dnsNodeName.str, "atestrecord-4") + + # Windows doesn't reload the zone fast enough, but doesn't + # have the bug anyway, it will sort last on both names (where + # it should) + if result.rec[6].dnsNodeName.str != (self.custom_zone + "1"): + self.assertEqual(result.rec[6].dnsNodeName.str, self.custom_zone) + + def test_enum_is_sorted_children_prefix_first(self): + """ + Confirm the zone returns the selected prefix first but no more + as Samba is flappy for the full sort + """ + + record_str = "192.168.50.50" + record_type_str = "A" + self.add_record(self.custom_zone, "atestrecord-1.a.b", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-2.a.b", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-3.a.b", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-4.a.b", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-0.a.b", record_type_str, record_str) + + # Not expected to be returned + self.add_record(self.custom_zone, "atestrecord-0.b.b", record_type_str, record_str) + + _, result = self.conn.DnssrvEnumRecords2(dnsserver.DNS_CLIENT_VERSION_LONGHORN, + 0, + self.server, + self.custom_zone, + "a.b", + None, + flag_from_string(record_type_str), + dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA, + None, + None) + + self.assertEqual(len(result.rec), 6) + self.assertEqual(result.rec[0].dnsNodeName.str, "") + + def test_enum_is_sorted_children(self): + """ + Confirm the zone is sorted + """ + + record_str = "192.168.50.50" + record_type_str = "A" + self.add_record(self.custom_zone, "atestrecord-1.a.b", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-2.a.b", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-3.a.b", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-4.a.b", record_type_str, record_str) + self.add_record(self.custom_zone, "atestrecord-0.a.b", record_type_str, record_str) + + # Not expected to be returned + self.add_record(self.custom_zone, "atestrecord-0.b.b", record_type_str, record_str) + + _, result = self.conn.DnssrvEnumRecords2(dnsserver.DNS_CLIENT_VERSION_LONGHORN, + 0, + self.server, + self.custom_zone, + "a.b", + None, + flag_from_string(record_type_str), + dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA, + None, + None) + + self.assertEqual(len(result.rec), 6) + self.assertEqual(result.rec[0].dnsNodeName.str, "") + self.assertEqual(result.rec[1].dnsNodeName.str, "atestrecord-0") + self.assertEqual(result.rec[2].dnsNodeName.str, "atestrecord-1") + self.assertEqual(result.rec[3].dnsNodeName.str, "atestrecord-2") + self.assertEqual(result.rec[4].dnsNodeName.str, "atestrecord-3") + self.assertEqual(result.rec[5].dnsNodeName.str, "atestrecord-4") + + # This test fails against Samba (but passes against Windows), + # because Samba does not return the record when we enum records. + # Records can be given DNS_RANK_NONE when the zone they are in + # does not have DNS_ZONE_TYPE_PRIMARY. Since such records can be + # deleted, however, we do not consider this urgent to fix and + # so this test is a knownfail. + def test_rank_none(self): + """ + See what happens when we set a record's rank to + DNS_RANK_NONE. + """ + + record_str = "192.168.50.50" + record_type_str = "A" + self.add_record(self.custom_zone, "testrecord", record_type_str, record_str) + + dn, record = self.get_record_from_db(self.custom_zone, "testrecord") + record.rank = 0 # DNS_RANK_NONE + res = self.samdb.dns_replace_by_dn(dn, [record]) + if res is not None: + self.fail("Unable to update dns record to have DNS_RANK_NONE.") + + self.assert_num_records(self.custom_zone, "testrecord", record_type_str) + self.add_record(self.custom_zone, "testrecord", record_type_str, record_str, assertion=False) + self.delete_record(self.custom_zone, "testrecord", record_type_str, record_str) + self.assert_num_records(self.custom_zone, "testrecord", record_type_str, 0) + + def test_dns_tombstoned_zero_timestamp(self): + """What happens with a zero EntombedTime tombstone?""" + # A zero-timestamp tombstone record has a special meaning for + # dns_common_replace(), which is the function exposed by + # samdb.dns_replace_by_dn(), and which is *NOT* a general + # purpose record replacement function but a specialised part + # of the dns update mechanism (for both DLZ and internal). + # + # In the earlier stages of handling updates, a record that + # needs to be deleted is set to be a tombstone with a zero + # timestamp. dns_common_replace() notices this specific + # marker, and if there are no other records, marks the node as + # tombstoned, in the process adding a "real" tombstone. + # + # If the tombstone has a non-zero timestamp, as you'll see in + # the next test, dns_common_replace will decide that the node + # is already tombstoned, and that no action needs to be taken. + # + # This test has worked historically, entirely by accident, as + # changing the wType appears to + + record_str = "192.168.50.50" + self.add_record(self.custom_zone, "testrecord", 'A', record_str) + + dn, record = self.get_record_from_db(self.custom_zone, "testrecord") + record.wType = dnsp.DNS_TYPE_TOMBSTONE + record.data = 0 + self.samdb.dns_replace_by_dn(dn, [record]) + + # there should be no A record, and one TOMBSTONE record. + self.assert_num_records(self.custom_zone, "testrecord", 'A', 0) + # we can't make assertions about the tombstone count based on + # RPC calls, as there are no tombstones in RPCs (there is + # "DNS_TYPE_ZERO" instead). Nor do tombstones show up if we + # use DNS_TYPE_ALL. + self.assert_num_records(self.custom_zone, "testrecord", 'ALL', 0) + + # But we can use LDAP: + records = self.ldap_get_records(self.custom_zone, "testrecord") + self.assertEqual(len(records), 1) + r = records[0] + self.assertEqual(r.wType, dnsp.DNS_TYPE_TOMBSTONE) + self.assertGreater(r.data, 1e17) # ~ October 1916 + + # this should fail, because no A records. + self.delete_record(self.custom_zone, "testrecord", 'A', record_str, + assertion=False) + + def test_dns_tombstoned_nonzero_timestamp(self): + """See what happens when we set a record to be tombstoned with an + EntombedTime timestamp. + """ + # Because this tombstone has a non-zero EntombedTime, + # dns_common_replace() will decide the node was already + # tombstoned and there is nothing to be done, leaving the A + # record where it was. + + record_str = "192.168.50.50" + self.add_record(self.custom_zone, "testrecord", 'A', record_str) + + dn, record = self.get_record_from_db(self.custom_zone, "testrecord") + record.wType = dnsp.DNS_TYPE_TOMBSTONE + record.data = 0x123456789A + self.samdb.dns_replace_by_dn(dn, [record]) + + # there should be the A record and no TOMBSTONE + self.assert_num_records(self.custom_zone, "testrecord", 'A', 1) + self.assert_num_records(self.custom_zone, "testrecord", 'TOMBSTONE', 0) + # this should succeed + self.delete_record(self.custom_zone, "testrecord", 'A', record_str, + assertion=True) + self.assert_num_records(self.custom_zone, "testrecord", 'TOMBSTONE', 0) + self.assert_num_records(self.custom_zone, "testrecord", 'A', 0) + + def get_record_from_db(self, zone_name, record_name): + """ + Returns (dn of record, record) + """ + + zones = self.samdb.search(base="DC=DomainDnsZones,%s" % self.samdb.get_default_basedn(), scope=ldb.SCOPE_SUBTREE, + expression="(objectClass=dnsZone)", + attrs=["cn"]) + + zone_dn = None + for zone in zones: + if "DC=%s," % zone_name in str(zone.dn): + zone_dn = zone.dn + break + + if zone_dn is None: + raise AssertionError("Couldn't find zone '%s'." % zone_name) + + records = self.samdb.search(base=zone_dn, scope=ldb.SCOPE_SUBTREE, + expression="(objectClass=dnsNode)", + attrs=["dnsRecord"]) + + for old_packed_record in records: + if record_name in str(old_packed_record.dn): + rec = ndr_unpack(dnsp.DnssrvRpcRecord, old_packed_record["dnsRecord"][0]) + return (old_packed_record.dn, rec) + + def ldap_get_records(self, zone, name): + zone_dn = (f"DC={zone},CN=MicrosoftDNS,DC=DomainDNSZones," + f"{self.samdb.get_default_basedn()}") + + expr = f"(&(objectClass=dnsNode)(name={name}))" + nodes = self.samdb.search(base=zone_dn, + scope=ldb.SCOPE_SUBTREE, + expression=expr, + attrs=["dnsRecord"]) + + records = nodes[0].get('dnsRecord') + return [ndr_unpack(dnsp.DnssrvRpcRecord, r) for r in records] + + def test_duplicate_matching(self): + """ + Make sure that records which should be distinct from each other or duplicate + to each other behave as expected. + """ + + distinct_dns = [("SAMDOM.EXAMPLE.COM", + "SAMDOM.EXAMPLE.CO", + "EXAMPLE.COM", "SAMDOM.EXAMPLE")] + duplicate_dns = [("SAMDOM.EXAMPLE.COM", "samdom.example.com", "SAMDOM.example.COM"), + ("EXAMPLE.", "EXAMPLE")] + + # Every tuple has entries which should be considered duplicate to one another. + duplicates = { + "AAAA": [("AAAA::", "aaaa::"), + ("AAAA::", "AAAA:0000::"), + ("AAAA::", "AAAA:0000:0000:0000:0000:0000:0000:0000"), + ("AAAA::", "AAAA:0:0:0:0:0:0:0"), + ("0123::", "123::"), + ("::", "::0", "0000:0000:0000:0000:0000:0000:0000:0000")], + } + + # Every tuple has entries which should be considered distinct from one another. + distinct = { + "A": [("192.168.1.0", "192.168.1.1", "192.168.2.0", "192.169.1.0", "193.168.1.0")], + "AAAA": [("AAAA::1234:5678:9ABC", "::AAAA:1234:5678:9ABC"), + ("1000::", "::1000"), + ("::1", "::11", "::1111"), + ("1234::", "0234::")], + "SRV": [("SAMDOM.EXAMPLE.COM 1 1 1", "SAMDOM.EXAMPLE.COM 1 1 0", "SAMDOM.EXAMPLE.COM 1 0 1", + "SAMDOM.EXAMPLE.COM 0 1 1", "SAMDOM.EXAMPLE.COM 2 1 0", "SAMDOM.EXAMPLE.COM 2 2 2")], + "MX": [("SAMDOM.EXAMPLE.COM 1", "SAMDOM.EXAMPLE.COM 0")], + "TXT": [("A RECORD", "B RECORD", "a record")] + } + + for record_type_str in ("PTR", "CNAME", "NS"): + distinct[record_type_str] = distinct_dns + duplicates[record_type_str] = duplicate_dns + + for record_type_str in duplicates: + for duplicate_tuple in duplicates[record_type_str]: + # Attempt to add duplicates and make sure that all after the first fails + self.add_record(self.custom_zone, "testrecord", record_type_str, duplicate_tuple[0]) + for record in duplicate_tuple: + self.add_record(self.custom_zone, "testrecord", record_type_str, record, assertion=False) + self.assert_num_records(self.custom_zone, "testrecord", record_type_str) + self.delete_record(self.custom_zone, "testrecord", record_type_str, duplicate_tuple[0]) + + # Repeatedly: add the first duplicate, and attempt to remove all of the others, making sure this succeeds + for record in duplicate_tuple: + self.add_record(self.custom_zone, "testrecord", record_type_str, duplicate_tuple[0]) + self.delete_record(self.custom_zone, "testrecord", record_type_str, record) + + for record_type_str in distinct: + for distinct_tuple in distinct[record_type_str]: + # Attempt to add distinct and make sure that they all succeed within a tuple + i = 0 + for record in distinct_tuple: + i = i + 1 + try: + self.add_record(self.custom_zone, "testrecord", record_type_str, record) + # All records should have been added. + self.assert_num_records(self.custom_zone, "testrecord", record_type_str, expected_num=i) + except AssertionError as e: + raise AssertionError("Failed to add %s, which should be distinct from all others in the set. " + "Original error: %s\nDistinct set: %s." % (record, e, distinct_tuple)) + for record in distinct_tuple: + self.delete_record(self.custom_zone, "testrecord", record_type_str, record) + # CNAMEs should not have been added, since they conflict. + if record_type_str == 'CNAME': + continue + + # Add the first distinct and attempt to remove all of the others, making sure this fails + # Windows fails this test. This is probably due to weird tombstoning behavior. + self.add_record(self.custom_zone, "testrecord", record_type_str, distinct_tuple[0]) + for record in distinct_tuple: + if record == distinct_tuple[0]: + continue + try: + self.delete_record(self.custom_zone, "testrecord", record_type_str, record, assertion=False) + except AssertionError as e: + raise AssertionError("Managed to remove %s by attempting to remove %s. Original error: %s" + % (distinct_tuple[0], record, e)) + self.delete_record(self.custom_zone, "testrecord", record_type_str, distinct_tuple[0]) + + def test_accept_valid_commands(self): + """ + Make sure that we can add, update and delete a variety + of valid records. + """ + for record_type_str in self.good_records: + for record_str in self.good_records[record_type_str]: + self.add_record(self.custom_zone, "testrecord", record_type_str, record_str) + self.assert_num_records(self.custom_zone, "testrecord", record_type_str) + self.delete_record(self.custom_zone, "testrecord", record_type_str, record_str) + + def check_params(self, wDataLength, rank, flags, dwTtlSeconds, dwReserved, data, + wType, dwTimeStamp=0, zone="zone", rec_name="testrecord"): + res = self.get_record_from_db(zone, rec_name) + self.assertIsNotNone(res, "Expected record %s but was not found over LDAP." % data) + (rec_dn, rec) = res + self.assertEqual(wDataLength, rec.wDataLength, "Unexpected data length for record %s. Got %s, expected %s." % (data, rec.wDataLength, wDataLength)) + self.assertEqual(rank, rec.rank, "Unexpected rank for record %s. Got %s, expected %s." % (data, rec.rank, rank)) + self.assertEqual(flags, rec.flags, "Unexpected flags for record %s. Got %s, expected %s." % (data, rec.flags, flags)) + self.assertEqual(dwTtlSeconds, rec.dwTtlSeconds, "Unexpected time to live for record %s. Got %s, expected %s." % (data, rec.dwTtlSeconds, dwTtlSeconds)) + self.assertEqual(dwReserved, rec.dwReserved, "Unexpected dwReserved for record %s. Got %s, expected %s." % (data, rec.dwReserved, dwReserved)) + self.assertEqual(data.lower(), rec.data.lower(), "Unexpected data for record %s. Got %s, expected %s." % (data, rec.data.lower(), data.lower())) + self.assertEqual(wType, rec.wType, "Unexpected wType for record %s. Got %s, expected %s." % (data, rec.wType, wType)) + self.assertEqual(dwTimeStamp, rec.dwTimeStamp, "Unexpected timestamp for record %s. Got %s, expected %s." % (data, rec.dwTimeStamp, dwTimeStamp)) + + def test_record_params(self): + """ + Make sure that, when we add records to the database, + they're added with reasonable parameters. + """ + self.add_record(self.custom_zone, "testrecord", "A", "192.168.50.50") + self.check_params(4, 240, 0, 900, 0, "192.168.50.50", 1) + self.delete_record(self.custom_zone, "testrecord", "A", "192.168.50.50") + self.add_record(self.custom_zone, "testrecord", "AAAA", "AAAA:AAAA::") + self.check_params(16, 240, 0, 900, 0, "AAAA:AAAA:0000:0000:0000:0000:0000:0000", 28) + self.delete_record(self.custom_zone, "testrecord", "AAAA", "AAAA:AAAA::") + self.add_record(self.custom_zone, "testrecord", "CNAME", "cnamedest") + self.check_params(13, 240, 0, 900, 0, "cnamedest", 5) + self.delete_record(self.custom_zone, "testrecord", "CNAME", "cnamedest") + + def test_reject_invalid_commands(self): + """ + Make sure that we can't add a variety of invalid records, + and that we can't update valid records to invalid ones. + """ + num_failures = 0 + for record_type_str in self.bad_records: + for record_str in self.bad_records[record_type_str]: + # Attempt to add the bad record, which should fail. Then, attempt to query for and delete + # it. Since it shouldn't exist, these should fail too. + try: + self.add_record(self.custom_zone, "testrecord", record_type_str, record_str, assertion=False) + self.assert_num_records(self.custom_zone, "testrecord", record_type_str, expected_num=0) + self.delete_record(self.custom_zone, "testrecord", record_type_str, record_str, assertion=False) + except AssertionError as e: + print(e) + num_failures = num_failures + 1 + + # Also try to update valid records to invalid ones, making sure this fails + for record_type_str in self.bad_records: + for record_str in self.bad_records[record_type_str]: + good_record_str = self.good_records[record_type_str][0] + self.add_record(self.custom_zone, "testrecord", record_type_str, good_record_str) + try: + self.add_record(self.custom_zone, "testrecord", record_type_str, record_str, assertion=False) + except AssertionError as e: + print(e) + num_failures = num_failures + 1 + self.delete_record(self.custom_zone, "testrecord", record_type_str, good_record_str) + + self.assertTrue(num_failures == 0, "Failed to reject invalid commands. Total failures: %d." % num_failures) + + def test_add_duplicate_different_type(self): + """ + Attempt to add some values which have the same name as + existing ones, just a different type. + """ + num_failures = 0 + for record_type_str_1 in self.good_records: + record1 = self.good_records[record_type_str_1][0] + self.add_record(self.custom_zone, "testrecord", record_type_str_1, record1) + for record_type_str_2 in self.good_records: + if record_type_str_1 == record_type_str_2: + continue + + record2 = self.good_records[record_type_str_2][0] + + has_a = record_type_str_1 == 'A' or record_type_str_2 == 'A' + has_aaaa = record_type_str_1 == 'AAAA' or record_type_str_2 == 'AAAA' + has_cname = record_type_str_1 == 'CNAME' or record_type_str_2 == 'CNAME' + has_ptr = record_type_str_1 == 'PTR' or record_type_str_2 == 'PTR' + has_mx = record_type_str_1 == 'MX' or record_type_str_2 == 'MX' + has_srv = record_type_str_1 == 'SRV' or record_type_str_2 == 'SRV' + has_txt = record_type_str_1 == 'TXT' or record_type_str_2 == 'TXT' + + # If we attempt to add any record except A or AAAA when we already have an NS record, + # the add should fail. + add_error_ok = False + if record_type_str_1 == 'NS' and not has_a and not has_aaaa: + add_error_ok = True + # If we attempt to add a CNAME when an A, PTR or MX record exists, the add should fail. + if record_type_str_2 == 'CNAME' and (has_ptr or has_mx or has_a or has_aaaa): + add_error_ok = True + # If we have a CNAME, adding an A, AAAA, SRV or TXT record should fail. + # If we have an A, AAAA, SRV or TXT record, adding a CNAME should fail. + if has_cname and (has_a or has_aaaa or has_srv or has_txt): + add_error_ok = True + + try: + self.add_record(self.custom_zone, "testrecord", record_type_str_2, record2) + if add_error_ok: + num_failures = num_failures + 1 + print("Expected error when adding %s while a %s existed." + % (record_type_str_2, record_type_str_1)) + except AssertionError as e: + if not add_error_ok: + num_failures = num_failures + 1 + print("Didn't expect error when adding %s while a %s existed." + % (record_type_str_2, record_type_str_1)) + + if not add_error_ok: + # In the "normal" case, we expect the add to work and us to have one of each type of record afterwards. + expected_num_type_1 = 1 + expected_num_type_2 = 1 + + # If we have an MX record, a PTR record should replace it when added. + # If we have a PTR record, an MX record should replace it when added. + if has_ptr and has_mx: + expected_num_type_1 = 0 + + # If we have a CNAME, SRV or TXT record, a PTR or MX record should replace it when added. + if (has_cname or has_srv or has_txt) and (record_type_str_2 == 'PTR' or record_type_str_2 == 'MX'): + expected_num_type_1 = 0 + + if (record_type_str_1 == 'NS' and (has_a or has_aaaa)): + expected_num_type_2 = 0 + + try: + self.assert_num_records(self.custom_zone, "testrecord", record_type_str_1, expected_num=expected_num_type_1) + except AssertionError as e: + num_failures = num_failures + 1 + print("Expected %s %s records after adding a %s record and a %s record already existed." + % (expected_num_type_1, record_type_str_1, record_type_str_2, record_type_str_1)) + try: + self.assert_num_records(self.custom_zone, "testrecord", record_type_str_2, expected_num=expected_num_type_2) + except AssertionError as e: + num_failures = num_failures + 1 + print("Expected %s %s records after adding a %s record and a %s record already existed." + % (expected_num_type_2, record_type_str_2, record_type_str_2, record_type_str_1)) + + try: + self.delete_record(self.custom_zone, "testrecord", record_type_str_2, record2) + except AssertionError as e: + pass + + self.delete_record(self.custom_zone, "testrecord", record_type_str_1, record1) + + self.assertTrue(num_failures == 0, "Failed collision and replacement behavior. Total failures: %d." % num_failures) + + # Windows fails this test in the same way we do. + def _test_cname(self): + """ + Test some special properties of CNAME records. + """ + + # RFC 1912: When there is a CNAME record, there must not be any other records with the same alias + cname_record = self.good_records["CNAME"][1] + self.add_record(self.custom_zone, "testrecord", "CNAME", cname_record) + + for record_type_str in self.good_records: + other_record = self.good_records[record_type_str][0] + self.add_record(self.custom_zone, "testrecord", record_type_str, other_record, assertion=False) + self.assert_num_records(self.custom_zone, "testrecord", record_type_str, expected_num=0) + + # RFC 2181: MX & NS records must not be allowed to point to a CNAME alias + mx_record = "testrecord 1" + ns_record = "testrecord" + + self.add_record(self.custom_zone, "mxrec", "MX", mx_record, assertion=False) + self.add_record(self.custom_zone, "nsrec", "NS", ns_record, assertion=False) + + self.delete_record(self.custom_zone, "testrecord", "CNAME", cname_record) + + def test_add_duplicate_value(self): + """ + Make sure that we can't add duplicate values of any type. + """ + for record_type_str in self.good_records: + record = self.good_records[record_type_str][0] + + self.add_record(self.custom_zone, "testrecord", record_type_str, record) + self.add_record(self.custom_zone, "testrecord", record_type_str, record, assertion=False) + self.assert_num_records(self.custom_zone, "testrecord", record_type_str) + self.delete_record(self.custom_zone, "testrecord", record_type_str, record) + + def test_add_similar_value(self): + """ + Attempt to add values with the same name and type in the same + zone. This should work, and should result in both values + existing (except with some types). + """ + for record_type_str in self.good_records: + for i in range(1, len(self.good_records[record_type_str])): + record1 = self.good_records[record_type_str][i - 1] + record2 = self.good_records[record_type_str][i] + + if record_type_str == 'CNAME': + continue + # We expect CNAME records to override one another, as + # an alias can only map to one CNAME record. + # Also, on Windows, when the empty string is added and + # another record is added afterwards, the empty string + # will be silently overridden by the new one, so it + # fails this test for the empty string. + expected_num = 1 if record_type_str == 'CNAME' else 2 + + self.add_record(self.custom_zone, "testrecord", record_type_str, record1) + self.add_record(self.custom_zone, "testrecord", record_type_str, record2) + self.assert_num_records(self.custom_zone, "testrecord", record_type_str, expected_num=expected_num) + self.delete_record(self.custom_zone, "testrecord", record_type_str, record1) + self.delete_record(self.custom_zone, "testrecord", record_type_str, record2) + + def assert_record(self, zone, name, record_type_str, expected_record_str, + assertion=True, client_version=dnsserver.DNS_CLIENT_VERSION_LONGHORN): + """ + Asserts whether or not the given record with the given type exists in the + given zone. + """ + try: + _, result = self.query_records(zone, name, record_type_str) + except RuntimeError as e: + if assertion: + raise AssertionError("Record '%s' of type '%s' was not present when it should have been." + % (expected_record_str, record_type_str)) + else: + return + + found = False + for record in result.rec[0].records: + if record.data == expected_record_str: + found = True + break + + if found and not assertion: + raise AssertionError("Record '%s' of type '%s' was present when it shouldn't have been." % (expected_record_str, record_type_str)) + elif not found and assertion: + raise AssertionError("Record '%s' of type '%s' was not present when it should have been." % (expected_record_str, record_type_str)) + + def assert_num_records(self, zone, name, record_type_str, expected_num=1, + client_version=dnsserver.DNS_CLIENT_VERSION_LONGHORN): + """ + Asserts that there are a given amount of records with the given type in + the given zone. + """ + try: + _, result = self.query_records(zone, name, record_type_str) + num_results = len(result.rec[0].records) + if not num_results == expected_num: + raise AssertionError("There were %d records of type '%s' with the name '%s' when %d were expected." + % (num_results, record_type_str, name, expected_num)) + except RuntimeError: + if not expected_num == 0: + raise AssertionError("There were no records of type '%s' with the name '%s' when %d were expected." + % (record_type_str, name, expected_num)) + + def query_records(self, zone, name, record_type_str, client_version=dnsserver.DNS_CLIENT_VERSION_LONGHORN): + return self.conn.DnssrvEnumRecords2(client_version, + 0, + self.server, + zone, + name, + None, + flag_from_string(record_type_str), + dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA | dnsserver.DNS_RPC_VIEW_NO_CHILDREN, + None, + None) + + def add_record(self, zone, name, record_type_str, record_str, + assertion=True, client_version=dnsserver.DNS_CLIENT_VERSION_LONGHORN): + """ + Attempts to add a map from the given name to a record of the given type, + in the given zone. + Also asserts whether or not the add was successful. + This can also update existing records if they have the same name. + """ + record = record_from_string(record_type_str, record_str, sep=' ') + add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF() + add_rec_buf.rec = record + + try: + self.conn.DnssrvUpdateRecord2(client_version, + 0, + self.server, + zone, + name, + add_rec_buf, + None) + if not assertion: + raise AssertionError("Successfully added record '%s' of type '%s', which should have failed." + % (record_str, record_type_str)) + except RuntimeError as e: + if assertion: + raise AssertionError("Failed to add record '%s' of type '%s', which should have succeeded. Error was '%s'." + % (record_str, record_type_str, str(e))) + + def delete_record(self, zone, name, record_type_str, record_str, + assertion=True, client_version=dnsserver.DNS_CLIENT_VERSION_LONGHORN): + """ + Attempts to delete a record with the given name, record and record type + from the given zone. + Also asserts whether or not the deletion was successful. + """ + record = record_from_string(record_type_str, record_str, sep=' ') + del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF() + del_rec_buf.rec = record + + try: + self.conn.DnssrvUpdateRecord2(client_version, + 0, + self.server, + zone, + name, + None, + del_rec_buf) + if not assertion: + raise AssertionError("Successfully deleted record '%s' of type '%s', which should have failed." % (record_str, record_type_str)) + except RuntimeError as e: + if assertion: + raise AssertionError("Failed to delete record '%s' of type '%s', which should have succeeded. Error was '%s'." % (record_str, record_type_str, str(e))) + + def test_query2(self): + typeid, result = self.conn.DnssrvQuery2(dnsserver.DNS_CLIENT_VERSION_W2K, + 0, + self.server, + None, + 'ServerInfo') + self.assertEqual(dnsserver.DNSSRV_TYPEID_SERVER_INFO_W2K, typeid) + + typeid, result = self.conn.DnssrvQuery2(dnsserver.DNS_CLIENT_VERSION_DOTNET, + 0, + self.server, + None, + 'ServerInfo') + self.assertEqual(dnsserver.DNSSRV_TYPEID_SERVER_INFO_DOTNET, typeid) + + typeid, result = self.conn.DnssrvQuery2(dnsserver.DNS_CLIENT_VERSION_LONGHORN, + 0, + self.server, + None, + 'ServerInfo') + self.assertEqual(dnsserver.DNSSRV_TYPEID_SERVER_INFO, typeid) + + + # This test is to confirm that we do not support multizone operations, + # which are designated by a non-zero dwContext value (the 3rd argument + # to DnssrvOperation). + def test_operation_invalid(self): + non_zone = 'a-zone-that-does-not-exist' + typeid = dnsserver.DNSSRV_TYPEID_NAME_AND_PARAM + name_and_param = dnsserver.DNS_RPC_NAME_AND_PARAM() + name_and_param.pszNodeName = 'AllowUpdate' + name_and_param.dwParam = dnsp.DNS_ZONE_UPDATE_SECURE + try: + res = self.conn.DnssrvOperation(self.server, + non_zone, + 1, + 'ResetDwordProperty', + typeid, + name_and_param) + except WERRORError as e: + if e.args[0] == werror.WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST: + return + + # We should always encounter a DOES_NOT_EXIST error. + self.fail() + + # This test is to confirm that we do not support multizone operations, + # which are designated by a non-zero dwContext value (the 5th argument + # to DnssrvOperation2). + def test_operation2_invalid(self): + client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN + non_zone = 'a-zone-that-does-not-exist' + typeid = dnsserver.DNSSRV_TYPEID_NAME_AND_PARAM + name_and_param = dnsserver.DNS_RPC_NAME_AND_PARAM() + name_and_param.pszNodeName = 'AllowUpdate' + name_and_param.dwParam = dnsp.DNS_ZONE_UPDATE_SECURE + try: + res = self.conn.DnssrvOperation2(client_version, + 0, + self.server, + non_zone, + 1, + 'ResetDwordProperty', + typeid, + name_and_param) + except WERRORError as e: + if e.args[0] == werror.WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST: + return + + # We should always encounter a DOES_NOT_EXIST error. + self.fail() + + def test_operation2(self): + client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN + rev_zone = '1.168.192.in-addr.arpa' + + zone_create = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN() + zone_create.pszZoneName = rev_zone + zone_create.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY + zone_create.fAllowUpdate = dnsp.DNS_ZONE_UPDATE_SECURE + zone_create.fAging = 0 + zone_create.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT + + # Create zone + self.conn.DnssrvOperation2(client_version, + 0, + self.server, + None, + 0, + 'ZoneCreate', + dnsserver.DNSSRV_TYPEID_ZONE_CREATE, + zone_create) + + request_filter = (dnsserver.DNS_ZONE_REQUEST_REVERSE | + dnsserver.DNS_ZONE_REQUEST_PRIMARY) + _, zones = self.conn.DnssrvComplexOperation2(client_version, + 0, + self.server, + None, + 'EnumZones', + dnsserver.DNSSRV_TYPEID_DWORD, + request_filter) + self.assertEqual(1, zones.dwZoneCount) + + # Delete zone + self.conn.DnssrvOperation2(client_version, + 0, + self.server, + rev_zone, + 0, + 'DeleteZoneFromDs', + dnsserver.DNSSRV_TYPEID_NULL, + None) + + typeid, zones = self.conn.DnssrvComplexOperation2(client_version, + 0, + self.server, + None, + 'EnumZones', + dnsserver.DNSSRV_TYPEID_DWORD, + request_filter) + self.assertEqual(0, zones.dwZoneCount) + + def test_complexoperation2(self): + client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN + request_filter = (dnsserver.DNS_ZONE_REQUEST_FORWARD | + dnsserver.DNS_ZONE_REQUEST_PRIMARY) + + typeid, zones = self.conn.DnssrvComplexOperation2(client_version, + 0, + self.server, + None, + 'EnumZones', + dnsserver.DNSSRV_TYPEID_DWORD, + request_filter) + self.assertEqual(dnsserver.DNSSRV_TYPEID_ZONE_LIST, typeid) + self.assertEqual(3, zones.dwZoneCount) + + request_filter = (dnsserver.DNS_ZONE_REQUEST_REVERSE | + dnsserver.DNS_ZONE_REQUEST_PRIMARY) + typeid, zones = self.conn.DnssrvComplexOperation2(client_version, + 0, + self.server, + None, + 'EnumZones', + dnsserver.DNSSRV_TYPEID_DWORD, + request_filter) + self.assertEqual(dnsserver.DNSSRV_TYPEID_ZONE_LIST, typeid) + self.assertEqual(0, zones.dwZoneCount) + + def test_enumrecords2(self): + client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN + record_type = dnsp.DNS_TYPE_NS + select_flags = (dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA | + dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA) + _, roothints = self.conn.DnssrvEnumRecords2(client_version, + 0, + self.server, + '..RootHints', + '.', + None, + record_type, + select_flags, + None, + None) + self.assertEqual(14, roothints.count) # 1 NS + 13 A records (a-m) + + def test_updaterecords2(self): + client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN + record_type = dnsp.DNS_TYPE_A + select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA + name = 'dummy' + rec = ARecord('1.2.3.4') + rec2 = ARecord('5.6.7.8') + + # Add record + add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF() + add_rec_buf.rec = rec + self.conn.DnssrvUpdateRecord2(client_version, + 0, + self.server, + self.zone, + name, + add_rec_buf, + None) + + _, result = self.conn.DnssrvEnumRecords2(client_version, + 0, + self.server, + self.zone, + name, + None, + record_type, + select_flags, + None, + None) + self.assertEqual(1, result.count) + self.assertEqual(1, result.rec[0].wRecordCount) + self.assertEqual(dnsp.DNS_TYPE_A, result.rec[0].records[0].wType) + self.assertEqual('1.2.3.4', result.rec[0].records[0].data) + + # Update record + add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF() + add_rec_buf.rec = rec2 + del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF() + del_rec_buf.rec = rec + self.conn.DnssrvUpdateRecord2(client_version, + 0, + self.server, + self.zone, + name, + add_rec_buf, + del_rec_buf) + + buflen, result = self.conn.DnssrvEnumRecords2(client_version, + 0, + self.server, + self.zone, + name, + None, + record_type, + select_flags, + None, + None) + self.assertEqual(1, result.count) + self.assertEqual(1, result.rec[0].wRecordCount) + self.assertEqual(dnsp.DNS_TYPE_A, result.rec[0].records[0].wType) + self.assertEqual('5.6.7.8', result.rec[0].records[0].data) + + # Delete record + del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF() + del_rec_buf.rec = rec2 + self.conn.DnssrvUpdateRecord2(client_version, + 0, + self.server, + self.zone, + name, + None, + del_rec_buf) + + self.assertRaises(RuntimeError, self.conn.DnssrvEnumRecords2, + client_version, + 0, + self.server, + self.zone, + name, + None, + record_type, + select_flags, + None, + None) + + # The following tests do not pass against Samba because the owner and + # group are not consistent with Windows, as well as some ACEs. + # + # The following ACE are also required for 2012R2: + # + # (OA;CIIO;WP;ea1b7b93-5e48-46d5-bc6c-4df4fda78a35;bf967a86-0de6-11d0-a285-00aa003049e2;PS) + # (OA;OICI;RPWP;3f78c3e5-f79a-46bd-a0b8-9d18116ddc79;;PS)" + # + # [TPM + Allowed-To-Act-On-Behalf-Of-Other-Identity] + def test_security_descriptor_msdcs_zone(self): + """ + Make sure that security descriptors of the msdcs zone is + as expected. + """ + + zones = self.samdb.search(base="DC=ForestDnsZones,%s" % self.samdb.get_default_basedn(), + scope=ldb.SCOPE_SUBTREE, + expression="(&(objectClass=dnsZone)(name=_msdcs*))", + attrs=["nTSecurityDescriptor", "objectClass"]) + self.assertEqual(len(zones), 1) + self.assertIn("nTSecurityDescriptor", zones[0]) + tmp = zones[0]["nTSecurityDescriptor"][0] + utils = sd_utils.SDUtils(self.samdb) + sd = ndr_unpack(security.descriptor, tmp) + + domain_sid = security.dom_sid(self.samdb.get_domain_sid()) + + res = self.samdb.search(base=self.samdb.get_default_basedn(), scope=ldb.SCOPE_SUBTREE, + expression="(sAMAccountName=DnsAdmins)", + attrs=["objectSid"]) + + dns_admin = str(ndr_unpack(security.dom_sid, res[0]['objectSid'][0])) + + packed_sd = descriptor.sddl2binary("O:SYG:BA" + "D:AI(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)" + "(A;;CC;;;AU)" + "(A;;RPLCLORC;;;WD)" + "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" + "(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)", + domain_sid, {"DnsAdmins": dns_admin}) + expected_sd = descriptor.get_clean_sd(ndr_unpack(security.descriptor, packed_sd)) + + diff = descriptor.get_diff_sds(expected_sd, sd, domain_sid) + self.assertEqual(diff, '', "SD of msdcs zone different to expected.\n" + "Difference was:\n%s\nExpected: %s\nGot: %s" % + (diff, expected_sd.as_sddl(utils.domain_sid), + sd.as_sddl(utils.domain_sid))) + + def test_security_descriptor_forest_zone(self): + """ + Make sure that security descriptors of forest dns zones are + as expected. + """ + forest_zone = "test_forest_zone" + zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN() + zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY + zone_create_info.fAging = 0 + zone_create_info.fDsIntegrated = 1 + zone_create_info.fLoadExisting = 1 + + zone_create_info.pszZoneName = forest_zone + zone_create_info.dwDpFlags = dnsserver.DNS_DP_FOREST_DEFAULT + + self.conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN, + 0, + self.server, + None, + 0, + 'ZoneCreate', + dnsserver.DNSSRV_TYPEID_ZONE_CREATE, + zone_create_info) + + partition_dn = self.samdb.get_default_basedn() + partition_dn.add_child("DC=ForestDnsZones") + zones = self.samdb.search(base=partition_dn, scope=ldb.SCOPE_SUBTREE, + expression="(name=%s)" % forest_zone, + attrs=["nTSecurityDescriptor"]) + self.assertEqual(len(zones), 1) + current_dn = zones[0].dn + self.assertIn("nTSecurityDescriptor", zones[0]) + tmp = zones[0]["nTSecurityDescriptor"][0] + utils = sd_utils.SDUtils(self.samdb) + sd = ndr_unpack(security.descriptor, tmp) + + domain_sid = security.dom_sid(self.samdb.get_domain_sid()) + + res = self.samdb.search(base=self.samdb.get_default_basedn(), + scope=ldb.SCOPE_SUBTREE, + expression="(sAMAccountName=DnsAdmins)", + attrs=["objectSid"]) + + dns_admin = str(ndr_unpack(security.dom_sid, res[0]['objectSid'][0])) + + packed_sd = descriptor.sddl2binary("O:DAG:DA" + "D:AI(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)" + "(A;;CC;;;AU)" + "(A;;RPLCLORC;;;WD)" + "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" + "(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)", + domain_sid, {"DnsAdmins": dns_admin}) + expected_sd = descriptor.get_clean_sd(ndr_unpack(security.descriptor, packed_sd)) + + packed_msdns = descriptor.get_dns_forest_microsoft_dns_descriptor(domain_sid, + {"DnsAdmins": dns_admin}) + expected_msdns_sd = descriptor.get_clean_sd(ndr_unpack(security.descriptor, packed_msdns)) + + packed_part_sd = descriptor.get_dns_partition_descriptor(domain_sid) + expected_part_sd = descriptor.get_clean_sd(ndr_unpack(security.descriptor, + packed_part_sd)) + try: + msdns_dn = ldb.Dn(self.samdb, "CN=MicrosoftDNS,%s" % str(partition_dn)) + security_desc_dict = [(current_dn.get_linearized(), expected_sd), + (msdns_dn.get_linearized(), expected_msdns_sd), + (partition_dn.get_linearized(), expected_part_sd)] + + for (key, sec_desc) in security_desc_dict: + zones = self.samdb.search(base=key, scope=ldb.SCOPE_BASE, + attrs=["nTSecurityDescriptor"]) + self.assertIn("nTSecurityDescriptor", zones[0]) + tmp = zones[0]["nTSecurityDescriptor"][0] + utils = sd_utils.SDUtils(self.samdb) + + sd = ndr_unpack(security.descriptor, tmp) + diff = descriptor.get_diff_sds(sec_desc, sd, domain_sid) + + self.assertEqual(diff, '', "Security descriptor of forest DNS zone with DN '%s' different to expected. Difference was:\n%s\nExpected: %s\nGot: %s" + % (key, diff, sec_desc.as_sddl(utils.domain_sid), sd.as_sddl(utils.domain_sid))) + + finally: + self.conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN, + 0, + self.server, + forest_zone, + 0, + 'DeleteZoneFromDs', + dnsserver.DNSSRV_TYPEID_NULL, + None) + + def test_security_descriptor_domain_zone(self): + """ + Make sure that security descriptors of domain dns zones are + as expected. + """ + + partition_dn = self.samdb.get_default_basedn() + partition_dn.add_child("DC=DomainDnsZones") + zones = self.samdb.search(base=partition_dn, scope=ldb.SCOPE_SUBTREE, + expression="(name=%s)" % self.custom_zone, + attrs=["nTSecurityDescriptor"]) + self.assertEqual(len(zones), 1) + current_dn = zones[0].dn + self.assertIn("nTSecurityDescriptor", zones[0]) + tmp = zones[0]["nTSecurityDescriptor"][0] + utils = sd_utils.SDUtils(self.samdb) + sd = ndr_unpack(security.descriptor, tmp) + sddl = sd.as_sddl(utils.domain_sid) + + domain_sid = security.dom_sid(self.samdb.get_domain_sid()) + + res = self.samdb.search(base=self.samdb.get_default_basedn(), scope=ldb.SCOPE_SUBTREE, + expression="(sAMAccountName=DnsAdmins)", + attrs=["objectSid"]) + + dns_admin = str(ndr_unpack(security.dom_sid, res[0]['objectSid'][0])) + + packed_sd = descriptor.sddl2binary("O:DAG:DA" + "D:AI(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)" + "(A;;CC;;;AU)" + "(A;;RPLCLORC;;;WD)" + "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" + "(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)", + domain_sid, {"DnsAdmins": dns_admin}) + expected_sd = descriptor.get_clean_sd(ndr_unpack(security.descriptor, packed_sd)) + + packed_msdns = descriptor.get_dns_domain_microsoft_dns_descriptor(domain_sid, + {"DnsAdmins": dns_admin}) + expected_msdns_sd = descriptor.get_clean_sd(ndr_unpack(security.descriptor, packed_msdns)) + + packed_part_sd = descriptor.get_dns_partition_descriptor(domain_sid) + expected_part_sd = descriptor.get_clean_sd(ndr_unpack(security.descriptor, + packed_part_sd)) + + msdns_dn = ldb.Dn(self.samdb, "CN=MicrosoftDNS,%s" % str(partition_dn)) + security_desc_dict = [(current_dn.get_linearized(), expected_sd), + (msdns_dn.get_linearized(), expected_msdns_sd), + (partition_dn.get_linearized(), expected_part_sd)] + + for (key, sec_desc) in security_desc_dict: + zones = self.samdb.search(base=key, scope=ldb.SCOPE_BASE, + attrs=["nTSecurityDescriptor"]) + self.assertIn("nTSecurityDescriptor", zones[0]) + tmp = zones[0]["nTSecurityDescriptor"][0] + utils = sd_utils.SDUtils(self.samdb) + + sd = ndr_unpack(security.descriptor, tmp) + diff = descriptor.get_diff_sds(sec_desc, sd, domain_sid) + + self.assertEqual(diff, '', "Security descriptor of domain DNS zone with DN '%s' different to expected. Difference was:\n%s\nExpected: %s\nGot: %s" + % (key, diff, sec_desc.as_sddl(utils.domain_sid), sd.as_sddl(utils.domain_sid))) diff --git a/python/samba/tests/dcerpc/integer.py b/python/samba/tests/dcerpc/integer.py new file mode 100644 index 0000000..69a6a09 --- /dev/null +++ b/python/samba/tests/dcerpc/integer.py @@ -0,0 +1,250 @@ +# Unix SMB/CIFS implementation. +# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2015 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for integer handling in PIDL generated bindings samba.dcerpc.*""" + +from samba.dcerpc import server_id, misc, srvsvc, samr +import samba.tests + + +class IntegerTests(samba.tests.TestCase): + + def test_uint32_into_hyper(self): + s = server_id.server_id() + s.unique_id = server_id.NONCLUSTER_VNN + self.assertEqual(s.unique_id, 0xFFFFFFFF) + + def test_int_into_hyper(self): + s = server_id.server_id() + s.unique_id = 1 + self.assertEqual(s.unique_id, 1) + + def test_negative_int_into_hyper(self): + s = server_id.server_id() + + def assign(): + s.unique_id = -1 + self.assertRaises(OverflowError, assign) + + def test_hyper_into_uint32(self): + s = server_id.server_id() + + def assign(): + s.vnn = server_id.SERVERID_UNIQUE_ID_NOT_TO_VERIFY + self.assertRaises(OverflowError, assign) + + def test_hyper_into_int32(self): + s = srvsvc.NetRemoteTODInfo() + + def assign(): + s.timezone = server_id.SERVERID_UNIQUE_ID_NOT_TO_VERIFY + self.assertRaises(OverflowError, assign) + + def test_int_into_int32(self): + s = srvsvc.NetRemoteTODInfo() + s.timezone = 5 + self.assertEqual(s.timezone, 5) + + def test_uint32_into_int32(self): + s = srvsvc.NetRemoteTODInfo() + + def assign(): + s.timezone = server_id.NONCLUSTER_VNN + self.assertRaises(OverflowError, assign) + + def test_long_into_int32(self): + s = srvsvc.NetRemoteTODInfo() + # here we force python2 to convert its 32/64 bit python int into + # an arbitrarily long python long, then reduce the number back + # down to something that would fit in an int anyway. In a pure + # python2 world, you could achieve the same thing by writing + # s.timezone = 5L + # but that is a syntax error in py3. + s.timezone = (5 << 65) >> 65 + self.assertEqual(s.timezone, 5) + + def test_larger_long_int_into_int32(self): + s = srvsvc.NetRemoteTODInfo() + + def assign(): + s.timezone = 2147483648 + self.assertRaises(OverflowError, assign) + + def test_larger_int_into_int32(self): + s = srvsvc.NetRemoteTODInfo() + s.timezone = 2147483647 + self.assertEqual(s.timezone, 2147483647) + + def test_float_into_int32(self): + s = srvsvc.NetRemoteTODInfo() + + def assign(): + s.timezone = 2.5 + self.assertRaises(TypeError, assign) + + def test_int_float_into_int32(self): + s = srvsvc.NetRemoteTODInfo() + + def assign(): + s.timezone = 2.0 + self.assertRaises(TypeError, assign) + + def test_negative_int_into_int32(self): + s = srvsvc.NetRemoteTODInfo() + s.timezone = -2147483648 + self.assertEqual(s.timezone, -2147483648) + + def test_negative_into_uint32(self): + s = server_id.server_id() + + def assign(): + s.vnn = -1 + self.assertRaises(OverflowError, assign) + + def test_hyper_into_uint16(self): + g = misc.GUID() + + def assign(): + g.time_mid = server_id.SERVERID_UNIQUE_ID_NOT_TO_VERIFY + self.assertRaises(OverflowError, assign) + + def test_int_into_uint16(self): + g = misc.GUID() + + def assign(): + g.time_mid = 200000 + self.assertRaises(OverflowError, assign) + + def test_negative_int_into_uint16(self): + g = misc.GUID() + + def assign(): + g.time_mid = -2 + self.assertRaises(OverflowError, assign) + + def test_enum_into_uint16(self): + g = misc.GUID() + g.time_mid = misc.SEC_CHAN_DOMAIN + self.assertEqual(g.time_mid, misc.SEC_CHAN_DOMAIN) + + def test_bitmap_into_uint16(self): + g = misc.GUID() + g.time_mid = misc.SV_TYPE_WFW + self.assertEqual(g.time_mid, misc.SV_TYPE_WFW) + + def test_overflow_bitmap_into_uint16(self): + g = misc.GUID() + + def assign(): + g.time_mid = misc.SV_TYPE_LOCAL_LIST_ONLY + self.assertRaises(OverflowError, assign) + + def test_overflow_bitmap_into_uint16_2(self): + g = misc.GUID() + + def assign(): + g.time_mid = misc.SV_TYPE_DOMAIN_ENUM + self.assertRaises(OverflowError, assign) + + def test_hyper_into_int64(self): + s = samr.DomInfo1() + + def assign(): + s.max_password_age = server_id.SERVERID_UNIQUE_ID_NOT_TO_VERIFY + self.assertRaises(OverflowError, assign) + + def test_int_into_int64(self): + s = samr.DomInfo1() + s.max_password_age = 5 + self.assertEqual(s.max_password_age, 5) + + def test_negative_int_into_int64(self): + s = samr.DomInfo1() + s.max_password_age = -5 + self.assertEqual(s.max_password_age, -5) + + def test_larger_int_into_int64(self): + s = samr.DomInfo1() + s.max_password_age = server_id.NONCLUSTER_VNN + self.assertEqual(s.max_password_age, 0xFFFFFFFF) + + def test_larger_negative_int_into_int64(self): + s = samr.DomInfo1() + s.max_password_age = -2147483649 + self.assertEqual(s.max_password_age, -2147483649) + + def test_int_list_over_list(self): + g = misc.GUID() + g.node = [5, 0, 5, 0, 7, 4] + self.assertEqual(g.node[0], 5) + + def test_long_int_list_over_uint8_list(self): + g = misc.GUID() + g.node = [5, 0, 5, 0, 7, 4] + self.assertEqual(g.node[0], 5) + + def test_negative_list_over_uint8_list(self): + g = misc.GUID() + + def assign(): + g.node = [-1, 0, 5, 0, 7, 4] + self.assertRaises(OverflowError, assign) + + def test_overflow_list_over_uint8_list(self): + g = misc.GUID() + + def assign(): + g.node = [256, 0, 5, 0, 7, 4] + self.assertRaises(OverflowError, assign) + + def test_short_list_over_uint8_list(self): + g = misc.GUID() + + def assign(): + g.node = [5, 0, 5] + self.assertRaises(TypeError, assign) + + def test_long_list_over_uint8_list(self): + g = misc.GUID() + + def assign(): + g.node = [5, 0, 5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] + self.assertRaises(TypeError, assign) + + # Due to our PIDL bindings generating a python List, modifications + # to a list of non-objects are not reflected in the C list + # (modifications objects in lists of objects work because the + # objects are modified), so changes essentially vanish and are not + # type checked either. + def test_assign_into_uint8_list(self): + g = misc.GUID() + g.node[1] = 5 + self.assertEqual(g.node[1], 5) + + def test_negative_into_uint8_list(self): + g = misc.GUID() + + def assign(): + g.node[1] = -1 + self.assertRaises(OverflowError, assign) + + def test_overflow_into_uint8_list(self): + g = misc.GUID() + + def assign(): + g.node[1] = 256 + self.assertRaises(OverflowError, assign) diff --git a/python/samba/tests/dcerpc/lsa.py b/python/samba/tests/dcerpc/lsa.py new file mode 100644 index 0000000..355bb1f --- /dev/null +++ b/python/samba/tests/dcerpc/lsa.py @@ -0,0 +1,333 @@ +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright © Andrew Bartlett <abartlet@samba.org> 2021 +# Copyright (C) Catalyst IT Ltd. 2017 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.dcerpc.lsa.""" + +from samba.dcerpc import lsa +from samba.credentials import Credentials +from samba.tests import TestCase +from samba.dcerpc.security import dom_sid +from samba import NTSTATUSError +from samba.ntstatus import NT_STATUS_ACCESS_DENIED +import samba.tests + +class LsaTests(TestCase): + + def setUp(self): + self.lp = self.get_loadparm() + self.server = samba.tests.env_get_var_value('SERVER') + + def test_lsa_LookupSids3_multiple(self): + machine_creds = Credentials() + machine_creds.guess(self.lp) + machine_creds.set_machine_account() + + c = lsa.lsarpc( + "ncacn_ip_tcp:%s[schannel,seal]" % self.server, + self.lp, + machine_creds) + + sids = lsa.SidArray() + sid = lsa.SidPtr() + # Need a set + x = dom_sid("S-1-5-7") + sid.sid = x + sids.sids = [sid] + sids.num_sids = 1 + names = lsa.TransNameArray2() + level = lsa.LSA_LOOKUP_NAMES_ALL + count = 0 + lookup_options = lsa.LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES + client_revision = lsa.LSA_CLIENT_REVISION_2 + + # We want to run LookupSids3 multiple times on the same + # connection as we have code to re-use the sam.ldb and we need + # to check things work for the second request. + (domains, names, count) = c.LookupSids3(sids, names, level, count, lookup_options, client_revision) + self.assertEqual(count, 1) + self.assertEqual(names.count, 1) + self.assertEqual(names.names[0].name.string, + "ANONYMOUS LOGON") + (domains2, names2, count2) = c.LookupSids3(sids, names, level, count, lookup_options, client_revision) + self.assertEqual(count2, 1) + self.assertEqual(names2.count, 1) + self.assertEqual(names2.names[0].name.string, + "ANONYMOUS LOGON") + + # Just looking for any exceptions in the last couple of loops + c.LookupSids3(sids, names, level, count, lookup_options, client_revision) + c.LookupSids3(sids, names, level, count, lookup_options, client_revision) + + def test_lsa_LookupSids3_multiple_conns(self): + machine_creds = Credentials() + machine_creds.guess(self.lp) + machine_creds.set_machine_account() + + c = lsa.lsarpc( + "ncacn_ip_tcp:%s[schannel,seal]" % self.server, + self.lp, + machine_creds) + + sids = lsa.SidArray() + sid = lsa.SidPtr() + # Need a set + x = dom_sid("S-1-5-7") + sid.sid = x + sids.sids = [sid] + sids.num_sids = 1 + names = lsa.TransNameArray2() + level = lsa.LSA_LOOKUP_NAMES_ALL + count = 0 + lookup_options = lsa.LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES + client_revision = lsa.LSA_CLIENT_REVISION_2 + + # We want to run LookupSids3, and then again on a new + # connection to show that we don't have an issue with the DB + # being tied to the wrong connection. + (domains, names, count) = c.LookupSids3(sids, + names, + level, + count, + lookup_options, + client_revision) + self.assertEqual(count, 1) + self.assertEqual(names.count, 1) + self.assertEqual(names.names[0].name.string, + "ANONYMOUS LOGON") + + c = lsa.lsarpc( + "ncacn_ip_tcp:%s[schannel,seal]" % self.server, + self.lp, + machine_creds) + + (domains, names, count) = c.LookupSids3(sids, + names, + level, + count, + lookup_options, + client_revision) + self.assertEqual(count, 1) + self.assertEqual(names.count, 1) + self.assertEqual(names.names[0].name.string, + "ANONYMOUS LOGON") + + + def test_lsa_LookupNames4_LookupSids3_multiple(self): + """ + Test by going back and forward between real DB lookups + name->sid->name to ensure the sam.ldb handle is fine once + shared + """ + + machine_creds = Credentials() + machine_creds.guess(self.lp) + machine_creds.set_machine_account() + + c_normal = lsa.lsarpc( + "ncacn_np:%s[seal]" % self.server, + self.lp, + machine_creds) + + username, domain = c_normal.GetUserName(None, None, None) + + c = lsa.lsarpc( + "ncacn_ip_tcp:%s[schannel,seal]" % self.server, + self.lp, + machine_creds) + + sids = lsa.TransSidArray3() + names = [username] + level = lsa.LSA_LOOKUP_NAMES_ALL + count = 0 + lookup_options = lsa.LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES + client_revision = lsa.LSA_CLIENT_REVISION_2 + (domains, sids, count) = c.LookupNames4(names, + sids, + level, + count, + lookup_options, + client_revision) + + # Another lookup on the same connection, will re-used the + # server-side implicit state handle on the connection + (domains, sids, count) = c.LookupNames4(names, + sids, + level, + count, + lookup_options, + client_revision) + + self.assertEqual(count, 1) + self.assertEqual(sids.count, 1) + + # Now look the SIDs back up + names = lsa.TransNameArray2() + sid = lsa.SidPtr() + sid.sid = sids.sids[0].sid + lookup_sids = lsa.SidArray() + lookup_sids.sids = [sid] + lookup_sids.num_sids = 1 + level = lsa.LSA_LOOKUP_NAMES_ALL + count = 1 + lookup_options = 0 + client_revision = lsa.LSA_CLIENT_REVISION_2 + + (domains, names, count) = c.LookupSids3(lookup_sids, + names, + level, + count, + lookup_options, + client_revision) + self.assertEqual(count, 1) + self.assertEqual(names.count, 1) + self.assertEqual(names.names[0].name.string, + username.string) + + # And once more just to be sure, just checking for a fault + sids = lsa.TransSidArray3() + names = [username] + level = lsa.LSA_LOOKUP_NAMES_ALL + count = 0 + lookup_options = lsa.LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES + client_revision = lsa.LSA_CLIENT_REVISION_2 + (domains, sids, count) = c.LookupNames4(names, + sids, + level, + count, + lookup_options, + client_revision) + + + def test_lsa_LookupNames4_multiple_conns(self): + """ + Test by going back and forward between real DB lookups + name->sid->name to ensure the sam.ldb handle is fine once + shared + """ + + machine_creds = Credentials() + machine_creds.guess(self.lp) + machine_creds.set_machine_account() + + c_normal = lsa.lsarpc( + "ncacn_np:%s[seal]" % self.server, + self.lp, + machine_creds) + + username, domain = c_normal.GetUserName(None, None, None) + + c = lsa.lsarpc( + "ncacn_ip_tcp:%s[schannel,seal]" % self.server, + self.lp, + machine_creds) + + sids = lsa.TransSidArray3() + names = [username] + level = lsa.LSA_LOOKUP_NAMES_ALL + count = 0 + lookup_options = lsa.LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES + client_revision = lsa.LSA_CLIENT_REVISION_2 + (domains, sids, count) = c.LookupNames4(names, + sids, + level, + count, + lookup_options, + client_revision) + + c = lsa.lsarpc( + "ncacn_ip_tcp:%s[schannel,seal]" % self.server, + self.lp, + machine_creds) + + sids = lsa.TransSidArray3() + names = [username] + level = lsa.LSA_LOOKUP_NAMES_ALL + count = 0 + lookup_options = lsa.LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES + client_revision = lsa.LSA_CLIENT_REVISION_2 + (domains, sids, count) = c.LookupNames4(names, + sids, + level, + count, + lookup_options, + client_revision) + + def test_lsa_LookupNames4_without_schannel(self): + + machine_creds = Credentials() + machine_creds.guess(self.lp) + machine_creds.set_machine_account() + + c_normal = lsa.lsarpc( + "ncacn_np:%s[seal]" % self.server, + self.lp, + machine_creds) + + username, domain = c_normal.GetUserName(None, None, None) + + sids = lsa.TransSidArray3() + names = [username] + level = lsa.LSA_LOOKUP_NAMES_ALL + count = 0 + lookup_options = lsa.LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES + client_revision = lsa.LSA_CLIENT_REVISION_2 + + with self.assertRaises(NTSTATUSError) as e: + c_normal.LookupNames4(names, + sids, + level, + count, + lookup_options, + client_revision) + if (e.exception.args[0] != NT_STATUS_ACCESS_DENIED): + raise AssertionError("LookupNames4 without schannel must fail with ACCESS_DENIED") + + def test_lsa_LookupSids3_without_schannel(self): + machine_creds = Credentials() + machine_creds.guess(self.lp) + machine_creds.set_machine_account() + + c = lsa.lsarpc( + "ncacn_ip_tcp:%s[seal]" % self.server, + self.lp, + machine_creds) + + sids = lsa.SidArray() + sid = lsa.SidPtr() + # Need a set + x = dom_sid("S-1-5-7") + sid.sid = x + sids.sids = [sid] + sids.num_sids = 1 + names = lsa.TransNameArray2() + level = lsa.LSA_LOOKUP_NAMES_ALL + count = 0 + lookup_options = lsa.LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES + client_revision = lsa.LSA_CLIENT_REVISION_2 + + with self.assertRaises(NTSTATUSError) as e: + c.LookupSids3(sids, + names, + level, + count, + lookup_options, + client_revision) + if (e.exception.args[0] != NT_STATUS_ACCESS_DENIED): + raise AssertionError("LookupSids3 without schannel must fail with ACCESS_DENIED") diff --git a/python/samba/tests/dcerpc/mdssvc.py b/python/samba/tests/dcerpc/mdssvc.py new file mode 100644 index 0000000..1d53676 --- /dev/null +++ b/python/samba/tests/dcerpc/mdssvc.py @@ -0,0 +1,194 @@ +# +# Unix SMB/CIFS implementation. +# Copyright Ralph Boehme <slow@samba.org> 2019 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.dcerpc.mdssvc""" + +import os +import time +import threading +import logging +import json +from http.server import HTTPServer, BaseHTTPRequestHandler +from samba.dcerpc import mdssvc +from samba.tests import RpcInterfaceTestCase +from samba.samba3 import mdscli +from samba.logger import get_samba_logger + +logger = get_samba_logger(name=__name__) + +testfiles = [ + "foo", + "bar", + "x+x", + "x*x", + "x=x", + "x'x", + "x?x", + "x\"x", + "x\\x", + "x(x", + "x x", +] + +class MdssvcHTTPRequestHandler(BaseHTTPRequestHandler): + def do_POST(self): + content_length = int(self.headers['content-length']) + body = self.rfile.read(content_length) + + actual_json = json.loads((body)) + expected_json = json.loads(self.server.json_in) + + if actual_json != expected_json: + logger.error("Bad request, expected:\n%s\nGot:\n%s\n" % (expected_json, actual_json)) + self.send_error(400, + "Bad request", + "Expected: %s\n" + "Got: %s\n" % + (expected_json, actual_json)) + return + + resp = bytes(self.server.json_out, encoding="utf-8") + + self.send_response(200) + self.send_header('content-type', 'application/json; charset=UTF-8') + self.send_header('content-length', len(resp)) + self.end_headers() + self.wfile.write(resp) + +class MdssvcTests(RpcInterfaceTestCase): + + def setUp(self): + super().setUp() + + self.pipe = mdssvc.mdssvc('ncacn_np:fileserver[/pipe/mdssvc]', self.get_loadparm()) + + self.server = HTTPServer(('10.53.57.35', 8080), + MdssvcHTTPRequestHandler, + bind_and_activate=False) + + self.t = threading.Thread(target=MdssvcTests.http_server, args=(self,)) + self.t.setDaemon(True) + self.t.start() + self.sharepath = os.environ["LOCAL_PATH"] + time.sleep(1) + + conn = mdscli.conn(self.pipe, 'spotlight', '/foo') + self.fakepath = conn.sharepath() + conn.disconnect(self.pipe) + + for file in testfiles: + f = open("%s/%s" % (self.sharepath, file), "w") + f.close() + + def tearDown(self): + super().tearDown() + for file in testfiles: + os.remove("%s/%s" % (self.sharepath, file)) + + def http_server(self): + self.server.server_bind() + self.server.server_activate() + self.server.serve_forever() + + def run_test(self, query, expect, json_in, json_out): + self.server.json_in = json_in.replace("%BASEPATH%", self.sharepath) + self.server.json_out = json_out.replace("%BASEPATH%", self.sharepath) + + self.conn = mdscli.conn(self.pipe, 'spotlight', '/foo') + search = self.conn.search(self.pipe, query, self.fakepath) + + # Give it some time, the get_results() below returns immediately + # what's available, so if we ask to soon, we might get back no results + # as the server is still processing the request + time.sleep(1) + + results = search.get_results(self.pipe) + self.assertEqual(results, expect) + + search.close(self.pipe) + self.conn.disconnect(self.pipe) + + def test_mdscli_search(self): + exp_json_query = r'''{ + "from": 0, "size": 50, "_source": ["path.real"], + "query": { + "query_string": { + "query": "(samba*) AND path.real.fulltext:\"%BASEPATH%\"" + } + } + }''' + fake_json_response = '''{ + "hits" : { + "total" : { "value" : 2}, + "hits" : [ + {"_source" : {"path" : {"real" : "%BASEPATH%/foo"}}}, + {"_source" : {"path" : {"real" : "%BASEPATH%/bar"}}} + ] + } + }''' + exp_results = ["foo", "bar"] + self.run_test('*=="samba*"', exp_results, exp_json_query, fake_json_response) + + def test_mdscli_search_escapes(self): + sl_query = ( + r'kMDItemFSName=="x+x"||' + r'kMDItemFSName=="x\*x"||' + r'kMDItemFSName=="x=x"||' + 'kMDItemFSName=="x\'x"||' + r'kMDItemFSName=="x?x"||' + r'kMDItemFSName=="x x"||' + r'kMDItemFSName=="x(x"||' + r'kMDItemFSName=="x\"x"||' + r'kMDItemFSName=="x\\x"' + ) + exp_json_query = r'''{ + "from": 0, "size": 50, "_source": ["path.real"], + "query": { + "query_string": { + "query": "(file.filename:x\\+x OR file.filename:x\\*x OR file.filename:x=x OR file.filename:x'x OR file.filename:x\\?x OR file.filename:x\\ x OR file.filename:x\\(x OR file.filename:x\\\"x OR file.filename:x\\\\x) AND path.real.fulltext:\"%BASEPATH%\"" + } + } + }''' + fake_json_response = r'''{ + "hits" : { + "total" : {"value" : 9}, + "hits" : [ + {"_source" : {"path" : {"real" : "%BASEPATH%/x+x"}}}, + {"_source" : {"path" : {"real" : "%BASEPATH%/x*x"}}}, + {"_source" : {"path" : {"real" : "%BASEPATH%/x=x"}}}, + {"_source" : {"path" : {"real" : "%BASEPATH%/x'x"}}}, + {"_source" : {"path" : {"real" : "%BASEPATH%/x?x"}}}, + {"_source" : {"path" : {"real" : "%BASEPATH%/x x"}}}, + {"_source" : {"path" : {"real" : "%BASEPATH%/x(x"}}}, + {"_source" : {"path" : {"real" : "%BASEPATH%/x\"x"}}}, + {"_source" : {"path" : {"real" : "%BASEPATH%/x\\x"}}} + ] + } + }''' + exp_results = [ + r"x+x", + r"x*x", + r"x=x", + r"x'x", + r"x?x", + r"x x", + r"x(x", + "x\"x", + r"x\x", + ] + self.run_test(sl_query, exp_results, exp_json_query, fake_json_response) diff --git a/python/samba/tests/dcerpc/misc.py b/python/samba/tests/dcerpc/misc.py new file mode 100644 index 0000000..6b58e94 --- /dev/null +++ b/python/samba/tests/dcerpc/misc.py @@ -0,0 +1,101 @@ +# Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.dcerpc.misc.""" + +from samba.dcerpc import misc +import samba.tests +from samba.common import cmp + +text1 = "76f53846-a7c2-476a-ae2c-20e2b80d7b34" +text2 = "344edffa-330a-4b39-b96e-2c34da52e8b1" +text3 = "00112233-4455-6677-8899-aabbccddeeff" + + +class GUIDTests(samba.tests.TestCase): + + def test_str(self): + guid = misc.GUID(text1) + self.assertEqual(text1, str(guid)) + + def test_repr(self): + guid = misc.GUID(text1) + self.assertEqual("GUID('%s')" % text1, repr(guid)) + + def test_compare_different(self): + guid1 = misc.GUID(text1) + guid2 = misc.GUID(text2) + self.assertFalse(guid1 == guid2) + self.assertGreater(guid1, guid2) + self.assertTrue(cmp(guid1, guid2) > 0) + + def test_compare_same(self): + guid1 = misc.GUID(text1) + guid2 = misc.GUID(text1) + self.assertTrue(guid1 == guid2) + self.assertEqual(guid1, guid2) + self.assertEqual(0, cmp(guid1, guid2)) + + def test_valid_formats(self): + fmts = [ + "00112233-4455-6677-8899-aabbccddeeff", # 36 + b"00112233-4455-6677-8899-aabbccddeeff", # 36 as bytes + "{00112233-4455-6677-8899-aabbccddeeff}", # 38 + + "33221100554477668899aabbccddeeff", # 32 + b"33221100554477668899aabbccddeeff", # 32 as bytes + + # 16 as hex bytes + b"\x33\x22\x11\x00\x55\x44\x77\x66\x88\x99\xaa\xbb\xcc\xdd\xee\xff" + ] + for fmt in fmts: + guid = misc.GUID(fmt) + self.assertEqual(text3, str(guid)) + + def test_invalid_formats(self): + fmts = [ + "00112233-4455-6677-8899-aabbccddee", # 34 + "{33221100554477668899aabbccddeeff}", + "33221100554477668899aabbccddee", # 30 + "\\x33\\x22\\x11\\x00\\x55\\x44\\x77\\x66\\x88\\x99\\xaa\\xbb\\xcc\\xdd\\xee\\xff", + r"\x33\x22\x11\x00\x55\x44\x77\x66\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + ] + for fmt in fmts: + try: + misc.GUID(fmt) + except samba.NTSTATUSError: + # invalid formats should get this error + continue + else: + # otherwise, test fail + self.fail() + + +class PolicyHandleTests(samba.tests.TestCase): + + def test_init(self): + x = misc.policy_handle(text1, 1) + self.assertEqual(1, x.handle_type) + self.assertEqual(text1, str(x.uuid)) + + def test_repr(self): + x = misc.policy_handle(text1, 42) + self.assertEqual("policy_handle(%d, '%s')" % (42, text1), repr(x)) + + def test_str(self): + x = misc.policy_handle(text1, 42) + self.assertEqual("%d, %s" % (42, text1), str(x)) diff --git a/python/samba/tests/dcerpc/raw_protocol.py b/python/samba/tests/dcerpc/raw_protocol.py new file mode 100755 index 0000000..fa5a042 --- /dev/null +++ b/python/samba/tests/dcerpc/raw_protocol.py @@ -0,0 +1,7514 @@ +#!/usr/bin/env python3 +# Unix SMB/CIFS implementation. +# Copyright (C) Stefan Metzmacher 2014,2015 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import sys +import os +import time + +sys.path.insert(0, "bin/python") +os.environ["PYTHONUNBUFFERED"] = "1" + +import samba.dcerpc.dcerpc as dcerpc +import samba.dcerpc.base as base +import samba.dcerpc.misc as misc +import samba.dcerpc.epmapper +import samba.dcerpc.mgmt +import samba.dcerpc.netlogon +import samba.dcerpc.lsa +import struct +from samba import gensec +from samba.tests.dcerpc.raw_testcase import RawDCERPCTest +from samba.ntstatus import ( + NT_STATUS_SUCCESS +) + +global_ndr_print = False +global_hexdump = False + + +class TestDCERPC_BIND(RawDCERPCTest): + + def setUp(self): + super().setUp() + self.do_ndr_print = global_ndr_print + self.do_hexdump = global_hexdump + + def _test_no_auth_request_bind_pfc_flags(self, req_pfc_flags, rep_pfc_flags): + 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 + + req = self.generate_bind(call_id=0, pfc_flags=req_pfc_flags, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + pfc_flags=rep_pfc_flags, auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + # And now try a request + req = self.generate_request(call_id=1, + context_id=ctx1.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + def _test_no_auth_request_alter_pfc_flags(self, req_pfc_flags, rep_pfc_flags): + 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 + + req = self.generate_bind(call_id=0, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + # And now try a alter context + req = self.generate_alter(call_id=0, pfc_flags=req_pfc_flags, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + pfc_flags=rep_pfc_flags, auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertEqual(rep.u.secondary_address, "") + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + # And now try a request + req = self.generate_request(call_id=1, + context_id=ctx1.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + def test_no_auth_request(self): + return self._test_no_auth_request_bind_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_bind_pfc_00(self): + return self._test_no_auth_request_bind_pfc_flags( + req_pfc_flags=0 | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_bind_pfc_FIRST(self): + return self._test_no_auth_request_bind_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_bind_pfc_LAST(self): + return self._test_no_auth_request_bind_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_LAST | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_bind_pfc_HDR_SIGNING(self): + return self._test_no_auth_request_bind_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST | + dcerpc.DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) + + def test_no_auth_request_bind_pfc_08(self): + return self._test_no_auth_request_bind_pfc_flags( + req_pfc_flags=0 | + 8 | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_bind_pfc_CONC_MPX(self): + return self._test_no_auth_request_bind_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_CONC_MPX | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST | + dcerpc.DCERPC_PFC_FLAG_CONC_MPX) + + def test_no_auth_request_bind_pfc_DID_NOT_EXECUTE(self): + return self._test_no_auth_request_bind_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_bind_pfc_MAYBE(self): + return self._test_no_auth_request_bind_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_MAYBE | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_bind_pfc_OBJECT_UUID(self): + return self._test_no_auth_request_bind_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_OBJECT_UUID | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + # TODO: doesn't announce DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN + # without authentication + # TODO: doesn't announce DCERPC_PFC_FLAG_CONC_MPX + # by default + def _test_no_auth_request_bind_pfc_ff(self): + return self._test_no_auth_request_bind_pfc_flags( + req_pfc_flags=0 | + 0xff | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST | + dcerpc.DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN | + dcerpc.DCERPC_PFC_FLAG_CONC_MPX) + + def test_no_auth_request_alter_pfc_00(self): + return self._test_no_auth_request_alter_pfc_flags( + req_pfc_flags=0 | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_alter_pfc_FIRST(self): + return self._test_no_auth_request_alter_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_alter_pfc_LAST(self): + return self._test_no_auth_request_alter_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_LAST | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_alter_pfc_HDR_SIGNING(self): + return self._test_no_auth_request_alter_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST | + dcerpc.DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) + + def test_no_auth_request_alter_pfc_08(self): + return self._test_no_auth_request_alter_pfc_flags( + req_pfc_flags=0 | + 8 | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_alter_pfc_CONC_MPX(self): + return self._test_no_auth_request_alter_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_CONC_MPX | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_alter_pfc_DID_NOT_EXECUTE(self): + return self._test_no_auth_request_alter_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_alter_pfc_MAYBE(self): + return self._test_no_auth_request_alter_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_MAYBE | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_alter_pfc_OBJECT_UUID(self): + return self._test_no_auth_request_alter_pfc_flags( + req_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_OBJECT_UUID | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST) + + def test_no_auth_request_alter_pfc_ff(self): + return self._test_no_auth_request_alter_pfc_flags( + req_pfc_flags=0 | + 0xff | + 0, + rep_pfc_flags=0 | + dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST | + dcerpc.DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) + + def test_no_auth_no_ctx(self): + # send an useless bind + req = self.generate_bind(call_id=0) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_NAK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.reject_reason, + dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + self.assertEqual(rep.u.num_versions, 1) + self.assertEqual(rep.u.versions[0].rpc_vers, req.rpc_vers) + self.assertEqual(rep.u.versions[0].rpc_vers_minor, req.rpc_vers_minor) + self.assertPadding(rep.u._pad, 3) + + def test_invalid_auth_noctx(self): + req = self.generate_bind(call_id=0) + req.auth_length = dcerpc.DCERPC_AUTH_TRAILER_LENGTH + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_NAK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.reject_reason, + dcerpc.DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED) + self.assertEqual(rep.u.num_versions, 1) + self.assertEqual(rep.u.versions[0].rpc_vers, req.rpc_vers) + self.assertEqual(rep.u.versions[0].rpc_vers_minor, req.rpc_vers_minor) + self.assertPadding(rep.u._pad, 3) + + def test_no_auth_valid_valid_request(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 + + req = self.generate_bind(call_id=0, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + # Send a bind again + tsf2_list = [ndr32] + ctx2 = dcerpc.ctx_list() + ctx2.context_id = 2 + ctx2.num_transfer_syntaxes = len(tsf2_list) + ctx2.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx2.transfer_syntaxes = tsf2_list + + req = self.generate_bind(call_id=1, ctx_list=[ctx2]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_NAK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.reject_reason, + dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + self.assertEqual(rep.u.num_versions, 1) + self.assertEqual(rep.u.versions[0].rpc_vers, req.rpc_vers) + self.assertEqual(rep.u.versions[0].rpc_vers_minor, req.rpc_vers_minor) + self.assertPadding(rep.u._pad, 3) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_no_auth_invalid_valid_request(self): + # send an useless bind + req = self.generate_bind(call_id=0) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_NAK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.reject_reason, + dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + self.assertEqual(rep.u.num_versions, 1) + self.assertEqual(rep.u.versions[0].rpc_vers, req.rpc_vers) + self.assertEqual(rep.u.versions[0].rpc_vers_minor, req.rpc_vers_minor) + self.assertPadding(rep.u._pad, 3) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_alter_no_auth_no_ctx(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 + + req = self.generate_bind(call_id=0, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + # Send a alter + req = self.generate_alter(call_id=1, ctx_list=[]) + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_no_auth_presentation_ctx_valid1(self): + ndr32 = base.transfer_syntax_ndr() + + zero_syntax = misc.ndr_syntax_id() + + tsf1_list = [zero_syntax, 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 + + req = self.generate_bind(call_id=0, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + # Send a alter + req = self.generate_alter(call_id=1, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + req = self.generate_request(call_id=2, + context_id=ctx1.context_id, + opnum=0xffff, + stub=b"") + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, ctx1.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_OP_RNG_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + def test_no_auth_presentation_ctx_invalid1(self): + ndr32 = base.transfer_syntax_ndr() + + zero_syntax = misc.ndr_syntax_id() + + tsf1_list = [ndr32] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = ndr32 + ctx1.transfer_syntaxes = tsf1_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_PROVIDER_REJECTION) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_ABSTRACT_SYNTAX_NOT_SUPPORTED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + # Send a alter + req = self.generate_alter(call_id=1, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_PROVIDER_REJECTION) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_ABSTRACT_SYNTAX_NOT_SUPPORTED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + req = self.generate_request(call_id=2, + context_id=12345, + opnum=0, + stub=b"") + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_UNKNOWN_IF) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # Send a alter again to prove the connection is still alive + req = self.generate_alter(call_id=3, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_PROVIDER_REJECTION) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_ABSTRACT_SYNTAX_NOT_SUPPORTED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + def test_no_auth_presentation_ctx_invalid2(self): + ndr32 = base.transfer_syntax_ndr() + + zero_syntax = misc.ndr_syntax_id() + + tsf1a_list = [] + ctx1a = dcerpc.ctx_list() + ctx1a.context_id = 1 + ctx1a.num_transfer_syntaxes = len(tsf1a_list) + ctx1a.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1a.transfer_syntaxes = tsf1a_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1a]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_NAK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.reject_reason, + dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + self.assertEqual(rep.u.num_versions, 1) + self.assertEqual(rep.u.versions[0].rpc_vers, req.rpc_vers) + self.assertEqual(rep.u.versions[0].rpc_vers_minor, req.rpc_vers_minor) + self.assertPadding(rep.u._pad, 3) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_no_auth_presentation_ctx_invalid3(self): + ndr32 = base.transfer_syntax_ndr() + + zero_syntax = misc.ndr_syntax_id() + + tsf1a_list = [zero_syntax, ndr32, ndr32, ndr32] + ctx1a = dcerpc.ctx_list() + ctx1a.context_id = 1 + ctx1a.num_transfer_syntaxes = len(tsf1a_list) + ctx1a.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1a.transfer_syntaxes = tsf1a_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1a]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + tsf1b_list = [] + ctx1b = dcerpc.ctx_list() + ctx1b.context_id = 1 + ctx1b.num_transfer_syntaxes = len(tsf1b_list) + ctx1b.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1b.transfer_syntaxes = tsf1b_list + + # Send a alter + req = self.generate_alter(call_id=1, ctx_list=[ctx1b]) + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_no_auth_presentation_ctx_invalid4(self): + ndr32 = base.transfer_syntax_ndr() + ndr64 = base.transfer_syntax_ndr64() + + zero_syntax = misc.ndr_syntax_id() + + tsf1a_list = [zero_syntax, ndr32, ndr32, ndr32] + ctx1a = dcerpc.ctx_list() + ctx1a.context_id = 1 + ctx1a.num_transfer_syntaxes = len(tsf1a_list) + ctx1a.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1a.transfer_syntaxes = tsf1a_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1a]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + # With a known but wrong syntax we get a protocol error + # see test_no_auth_presentation_ctx_valid2 + tsf1b_list = [zero_syntax, samba.dcerpc.epmapper.abstract_syntax(), ndr64] + ctx1b = dcerpc.ctx_list() + ctx1b.context_id = 1 + ctx1b.num_transfer_syntaxes = len(tsf1b_list) + ctx1b.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1b.transfer_syntaxes = tsf1b_list + + # Send a alter + req = self.generate_alter(call_id=1, ctx_list=[ctx1b]) + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_no_auth_presentation_ctx_valid2(self): + ndr32 = base.transfer_syntax_ndr() + + zero_syntax = misc.ndr_syntax_id() + + tsf1a_list = [zero_syntax, ndr32, ndr32, ndr32] + ctx1a = dcerpc.ctx_list() + ctx1a.context_id = 1 + ctx1a.num_transfer_syntaxes = len(tsf1a_list) + ctx1a.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1a.transfer_syntaxes = tsf1a_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1a]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + # With a unknown but wrong syntaxes we get NO protocol error + # see test_no_auth_presentation_ctx_invalid4 + tsf1b_list = [zero_syntax, samba.dcerpc.epmapper.abstract_syntax()] + ctx1b = dcerpc.ctx_list() + ctx1b.context_id = 1 + ctx1b.num_transfer_syntaxes = len(tsf1b_list) + ctx1b.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1b.transfer_syntaxes = tsf1b_list + + # Send a alter + req = self.generate_alter(call_id=1, ctx_list=[ctx1b]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_PROVIDER_REJECTION) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_TRANSFER_SYNTAXES_NOT_SUPPORTED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + req = self.generate_request(call_id=2, + context_id=ctx1a.context_id, + opnum=0xffff, + stub=b"") + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, ctx1a.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_OP_RNG_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + def test_no_auth_presentation_ctx_no_ndr64(self): + ndr32 = base.transfer_syntax_ndr() + zero_syntax = misc.ndr_syntax_id() + + tsfZ_list = [zero_syntax] + ctxZ = dcerpc.ctx_list() + ctxZ.context_id = 54321 + ctxZ.num_transfer_syntaxes = len(tsfZ_list) + ctxZ.abstract_syntax = zero_syntax + ctxZ.transfer_syntaxes = tsfZ_list + + req = self.generate_bind(call_id=0, ctx_list=[ctxZ]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_PROVIDER_REJECTION) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_ABSTRACT_SYNTAX_NOT_SUPPORTED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + tsf0_list = [ndr32] + ctx0 = dcerpc.ctx_list() + ctx0.context_id = 0 + ctx0.num_transfer_syntaxes = len(tsf0_list) + ctx0.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx0.transfer_syntaxes = tsf0_list + + req = self.generate_alter(call_id=0, ctx_list=[ctx0]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + req = self.generate_request(call_id=1, + context_id=ctx0.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + tsf1_list = [zero_syntax, 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 + + req = self.generate_alter(call_id=1, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + req = self.generate_request(call_id=1, + context_id=ctx1.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + tsf2_list = [ndr32, ndr32] + ctx2 = dcerpc.ctx_list() + ctx2.context_id = 2 + ctx2.num_transfer_syntaxes = len(tsf2_list) + ctx2.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx2.transfer_syntaxes = tsf2_list + + req = self.generate_alter(call_id=2, ctx_list=[ctx2]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + req = self.generate_request(call_id=1, + context_id=ctx2.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + tsf3_list = [ndr32] + ctx3 = dcerpc.ctx_list() + ctx3.context_id = 3 + ctx3.num_transfer_syntaxes = len(tsf3_list) + ctx3.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx3.transfer_syntaxes = tsf3_list + + tsf4_list = [ndr32] + ctx4 = dcerpc.ctx_list() + ctx4.context_id = 4 + ctx4.num_transfer_syntaxes = len(tsf4_list) + ctx4.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx4.transfer_syntaxes = tsf4_list + + req = self.generate_alter(call_id=34, ctx_list=[ctx3, ctx4]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 2) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.ctx_list[1].result, + dcerpc.DCERPC_BIND_ACK_RESULT_PROVIDER_REJECTION) + self.assertEqual(rep.u.ctx_list[1].reason, + dcerpc.DCERPC_BIND_ACK_REASON_TRANSFER_SYNTAXES_NOT_SUPPORTED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[1].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + req = self.generate_request(call_id=1, + context_id=ctx3.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + req = self.generate_alter(call_id=43, ctx_list=[ctx4, ctx3]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 2) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.ctx_list[1].result, + dcerpc.DCERPC_BIND_ACK_RESULT_PROVIDER_REJECTION) + self.assertEqual(rep.u.ctx_list[1].reason, + dcerpc.DCERPC_BIND_ACK_REASON_TRANSFER_SYNTAXES_NOT_SUPPORTED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[1].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + req = self.generate_request(call_id=1, + context_id=ctx4.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + req = self.generate_request(call_id=1, + context_id=ctx3.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + req = self.generate_alter(call_id=44, ctx_list=[ctx4, ctx4]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 2) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.ctx_list[1].result, + dcerpc.DCERPC_BIND_ACK_RESULT_PROVIDER_REJECTION) + self.assertEqual(rep.u.ctx_list[1].reason, + dcerpc.DCERPC_BIND_ACK_REASON_TRANSFER_SYNTAXES_NOT_SUPPORTED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[1].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + req = self.generate_request(call_id=1, + context_id=ctx4.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + req = self.generate_request(call_id=1, + context_id=ctx3.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + tsf5mgmt_list = [ndr32] + ctx5mgmt = dcerpc.ctx_list() + ctx5mgmt.context_id = 5 + ctx5mgmt.num_transfer_syntaxes = len(tsf5mgmt_list) + ctx5mgmt.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx5mgmt.transfer_syntaxes = tsf5mgmt_list + + tsf5epm_list = [ndr32] + ctx5epm = dcerpc.ctx_list() + ctx5epm.context_id = 5 + ctx5epm.num_transfer_syntaxes = len(tsf5epm_list) + ctx5epm.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx5epm.transfer_syntaxes = tsf5epm_list + + req = self.generate_alter(call_id=55, ctx_list=[ctx5mgmt, ctx5epm]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 2) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.ctx_list[1].result, + dcerpc.DCERPC_BIND_ACK_RESULT_PROVIDER_REJECTION) + self.assertEqual(rep.u.ctx_list[1].reason, + dcerpc.DCERPC_BIND_ACK_REASON_TRANSFER_SYNTAXES_NOT_SUPPORTED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[1].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + req = self.generate_request(call_id=1, + context_id=ctx5mgmt.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + req = self.generate_alter(call_id=55, ctx_list=[ctx5mgmt, ctx5epm]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 2) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.ctx_list[1].result, + dcerpc.DCERPC_BIND_ACK_RESULT_PROVIDER_REJECTION) + self.assertEqual(rep.u.ctx_list[1].reason, + dcerpc.DCERPC_BIND_ACK_REASON_TRANSFER_SYNTAXES_NOT_SUPPORTED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[1].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + req = self.generate_request(call_id=1, + context_id=ctx5mgmt.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + def test_no_auth_bind_time_none_simple(self): + features = 0 + btf = base.bind_time_features_syntax(features) + + zero_syntax = misc.ndr_syntax_id() + + tsf1_list = [btf] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = zero_syntax + ctx1.transfer_syntaxes = tsf1_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_NEGOTIATE_ACK) + self.assertEqual(rep.u.ctx_list[0].reason, features) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + def test_no_auth_bind_time_none_ignore_additional(self): + features1 = 0 + btf1 = base.bind_time_features_syntax(features1) + + features2 = dcerpc.DCERPC_BIND_TIME_KEEP_CONNECTION_ON_ORPHAN + features2 |= dcerpc.DCERPC_BIND_TIME_SECURITY_CONTEXT_MULTIPLEXING + btf2 = base.bind_time_features_syntax(features2) + + zero_syntax = misc.ndr_syntax_id() + ndr64 = base.transfer_syntax_ndr64() + + tsf1_list = [btf1, btf2, zero_syntax] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = ndr64 + ctx1.transfer_syntaxes = tsf1_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_NEGOTIATE_ACK) + self.assertEqual(rep.u.ctx_list[0].reason, features1) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + def test_no_auth_bind_time_only_first(self): + features1 = dcerpc.DCERPC_BIND_TIME_KEEP_CONNECTION_ON_ORPHAN + btf1 = base.bind_time_features_syntax(features1) + + features2 = dcerpc.DCERPC_BIND_TIME_SECURITY_CONTEXT_MULTIPLEXING + btf2 = base.bind_time_features_syntax(features2) + + zero_syntax = misc.ndr_syntax_id() + + tsf1_list = [zero_syntax, btf1, btf2, zero_syntax] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = zero_syntax + ctx1.transfer_syntaxes = tsf1_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_PROVIDER_REJECTION) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_ABSTRACT_SYNTAX_NOT_SUPPORTED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + def test_no_auth_bind_time_twice(self): + features1 = dcerpc.DCERPC_BIND_TIME_KEEP_CONNECTION_ON_ORPHAN + btf1 = base.bind_time_features_syntax(features1) + + features2 = dcerpc.DCERPC_BIND_TIME_SECURITY_CONTEXT_MULTIPLEXING + btf2 = base.bind_time_features_syntax(features2) + + zero_syntax = misc.ndr_syntax_id() + + tsf1_list = [btf1] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = zero_syntax + ctx1.transfer_syntaxes = tsf1_list + + tsf2_list = [btf2] + ctx2 = dcerpc.ctx_list() + ctx2.context_id = 2 + ctx2.num_transfer_syntaxes = len(tsf2_list) + ctx2.abstract_syntax = zero_syntax + ctx2.transfer_syntaxes = tsf2_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1, ctx2]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_NAK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.reject_reason, + dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + self.assertEqual(rep.u.num_versions, 1) + self.assertEqual(rep.u.versions[0].rpc_vers, req.rpc_vers) + self.assertEqual(rep.u.versions[0].rpc_vers_minor, req.rpc_vers_minor) + self.assertPadding(rep.u._pad, 3) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_no_auth_bind_time_keep_on_orphan_simple(self): + features = dcerpc.DCERPC_BIND_TIME_KEEP_CONNECTION_ON_ORPHAN + btf = base.bind_time_features_syntax(features) + + zero_syntax = misc.ndr_syntax_id() + + tsf1_list = [btf] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = zero_syntax + ctx1.transfer_syntaxes = tsf1_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_NEGOTIATE_ACK) + self.assertEqual(rep.u.ctx_list[0].reason, features) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + def test_no_auth_bind_time_keep_on_orphan_ignore_additional(self): + features1 = dcerpc.DCERPC_BIND_TIME_KEEP_CONNECTION_ON_ORPHAN + btf1 = base.bind_time_features_syntax(features1) + + features2 = dcerpc.DCERPC_BIND_TIME_SECURITY_CONTEXT_MULTIPLEXING + btf2 = base.bind_time_features_syntax(features2) + + zero_syntax = misc.ndr_syntax_id() + ndr64 = base.transfer_syntax_ndr64() + + tsf1_list = [btf1, btf2, zero_syntax] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = ndr64 + ctx1.transfer_syntaxes = tsf1_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_NEGOTIATE_ACK) + self.assertEqual(rep.u.ctx_list[0].reason, features1) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + def test_no_auth_bind_time_sec_ctx_ignore_additional(self): + features1 = dcerpc.DCERPC_BIND_TIME_SECURITY_CONTEXT_MULTIPLEXING + btf1 = base.bind_time_features_syntax(features1) + + features2 = dcerpc.DCERPC_BIND_TIME_KEEP_CONNECTION_ON_ORPHAN + btf2 = base.bind_time_features_syntax(features2) + + zero_syntax = misc.ndr_syntax_id() + ndr64 = base.transfer_syntax_ndr64() + + tsf1_list = [btf1, btf2, zero_syntax] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = ndr64 + ctx1.transfer_syntaxes = tsf1_list + + req = self.generate_bind(call_id=0, ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_NEGOTIATE_ACK) + self.assertEqual(rep.u.ctx_list[0].reason, features1) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, zero_syntax) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + def _test_auth_type_level_bind_nak(self, auth_type, auth_level, creds=None, + reason=dcerpc.DCERPC_BIND_NAK_REASON_INVALID_AUTH_TYPE): + 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] + + auth_context_id = 0 + + if creds is not None: + # We always start with DCERPC_AUTH_LEVEL_INTEGRITY + auth_context = self.get_auth_context_creds(creds, + auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY) + from_server = b"" + (finished, to_server) = auth_context["gensec"].update(from_server) + self.assertFalse(finished) + + auth_info = self.generate_auth(auth_type=auth_context["auth_type"], + auth_level=auth_context["auth_level"], + auth_context_id=auth_context["auth_context_id"], + auth_blob=to_server) + else: + to_server = b"none" + 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) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_NAK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.reject_reason, reason) + self.assertEqual(rep.u.num_versions, 1) + self.assertEqual(rep.u.versions[0].rpc_vers, req.rpc_vers) + self.assertEqual(rep.u.versions[0].rpc_vers_minor, req.rpc_vers_minor) + self.assertPadding(rep.u._pad, 3) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def _test_auth_none_level_bind(self, auth_level, + reason=dcerpc.DCERPC_BIND_NAK_REASON_INVALID_AUTH_TYPE): + return self._test_auth_type_level_bind_nak(auth_type=dcerpc.DCERPC_AUTH_LEVEL_NONE, + auth_level=auth_level, reason=reason) + + def test_auth_none_none_bind(self): + return self._test_auth_none_level_bind(dcerpc.DCERPC_AUTH_LEVEL_NONE, + reason=dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + + def test_auth_none_connect_bind(self): + return self._test_auth_none_level_bind(dcerpc.DCERPC_AUTH_LEVEL_CONNECT) + + def test_auth_none_call_bind(self): + return self._test_auth_none_level_bind(dcerpc.DCERPC_AUTH_LEVEL_CALL) + + def test_auth_none_packet_bind(self): + return self._test_auth_none_level_bind(dcerpc.DCERPC_AUTH_LEVEL_PACKET) + + def test_auth_none_integrity_bind(self): + return self._test_auth_none_level_bind(dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY) + + def test_auth_none_privacy_bind(self): + return self._test_auth_none_level_bind(dcerpc.DCERPC_AUTH_LEVEL_PRIVACY) + + def test_auth_none_0_bind(self): + return self._test_auth_none_level_bind(0, + reason=dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + + def test_auth_none_7_bind(self): + return self._test_auth_none_level_bind(7, + reason=dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + + def test_auth_none_255_bind(self): + return self._test_auth_none_level_bind(255, + reason=dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + + def _test_auth_none_level_request(self, auth_level): + 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] + + auth_type = dcerpc.DCERPC_AUTH_TYPE_NONE + auth_context_id = 0 + + req = self.generate_bind(call_id=0, + ctx_list=ctx_list) + + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(len(rep.u.auth_info), 0) + + # And now try a request without auth_info + req = self.generate_request(call_id=2, + context_id=ctx1.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob=b"none") + + req = self.generate_request(call_id=3, + context_id=ctx1.context_id, + opnum=0, + stub=b"", + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + # We get a fault back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_FAULT_ACCESS_DENIED) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_auth_none_none_request(self): + return self._test_auth_none_level_request(dcerpc.DCERPC_AUTH_LEVEL_NONE) + + def test_auth_none_connect_request(self): + return self._test_auth_none_level_request(dcerpc.DCERPC_AUTH_LEVEL_CONNECT) + + def test_auth_none_call_request(self): + return self._test_auth_none_level_request(dcerpc.DCERPC_AUTH_LEVEL_CALL) + + def test_auth_none_packet_request(self): + return self._test_auth_none_level_request(dcerpc.DCERPC_AUTH_LEVEL_PACKET) + + def test_ntlmssp_multi_auth_first1_lastSame2(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_FAULT_SEC_PKG_ERROR + auth_context_2nd = 2 + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = None + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_lastNext2(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = 2 + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = 4 + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_lastSame111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = None + auth_context_2nd = 1 + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = None + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = 111 + forced_auth_type = 111 + forced_auth_level = 111 + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_lastNext111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = 1 + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = 4 + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = 111 + forced_auth_type = 111 + forced_auth_level = 111 + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_MPX_first1_lastNext111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = 1 + expected_call_id = 4 + expected_context_id = 0 + not_executed = False + conc_mpx = True + forced_call_id = 4 + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = 111 + forced_auth_type = 111 + forced_auth_level = 111 + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_lastSameNone(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = None + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_MPX_first1_lastSameNone(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = True + forced_call_id = None + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_lastNextNone(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = 4 + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_MPX_first1_lastNextNone(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = 4 + expected_context_id = 0 + not_executed = False + conc_mpx = True + forced_call_id = 4 + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_lastSameNone111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = None + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_MPX_first1_lastSameNone111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = True + forced_call_id = None + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_lastNextNone111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = 4 + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_MPX_first1_lastNextNone111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = 4 + expected_context_id = 0 + not_executed = False + conc_mpx = True + forced_call_id = 4 + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_last(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def _test_generic_auth_first_2nd(self, + auth_type, + pfc_flags_2nd, + expected_fault, + auth_context_2nd=2, + skip_first=False, + expected_call_id=None, + expected_context_id=None, + conc_mpx=False, + not_executed=False, + forced_call_id=None, + forced_context_id=None, + forced_opnum=None, + forced_auth_context_id=None, + forced_auth_type=None, + forced_auth_level=None): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level1 = dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY + auth_context_id1=1 + auth_level2 = dcerpc.DCERPC_AUTH_LEVEL_PACKET + auth_context_id2=2 + + creds = self.get_user_creds() + + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + tsf1_list = [transfer] + ctx = samba.dcerpc.dcerpc.ctx_list() + ctx.context_id = 1 + ctx.num_transfer_syntaxes = len(tsf1_list) + ctx.abstract_syntax = abstract + ctx.transfer_syntaxes = tsf1_list + + auth_context1 = self.get_auth_context_creds(creds=creds, + auth_type=auth_type, + auth_level=auth_level1, + auth_context_id=auth_context_id1, + hdr_signing=False) + auth_context2 = self.get_auth_context_creds(creds=creds, + auth_type=auth_type, + auth_level=auth_level2, + auth_context_id=auth_context_id2, + hdr_signing=False) + + bind_pfc_flags = dcerpc.DCERPC_PFC_FLAG_FIRST | dcerpc.DCERPC_PFC_FLAG_LAST + if conc_mpx: + bind_pfc_flags |= dcerpc.DCERPC_PFC_FLAG_CONC_MPX + + ack0 = self.do_generic_bind(call_id=0, + ctx=ctx, + pfc_flags=bind_pfc_flags) + + ack1 = self.do_generic_bind(call_id=1, + ctx=ctx, + auth_context=auth_context1, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + if auth_context_2nd == 2: + ack2 = self.do_generic_bind(call_id=2, + ctx=ctx, + auth_context=auth_context2, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + + ndr_print = self.do_ndr_print + hexdump = self.do_hexdump + inq_if_ids = samba.dcerpc.mgmt.inq_if_ids() + io = inq_if_ids + if ndr_print: + sys.stderr.write("in: %s" % samba.ndr.ndr_print_in(io)) + stub_in = samba.ndr.ndr_pack_in(io) + stub_in += b'\xfe'*45 # add some padding in order to have some payload + if hexdump: + sys.stderr.write("stub_in: %d\n%s" % (len(stub_in), self.hexdump(stub_in))) + + call_id = 3 + context_id = ctx.context_id + opnum = io.opnum() + + if not skip_first: + pfc_flags = samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST + stub_in_tmp = stub_in[0:16] + req = self.generate_request_auth(call_id=call_id, + context_id=context_id, + pfc_flags=pfc_flags, + opnum=opnum, + alloc_hint=len(stub_in), + stub=stub_in_tmp, + auth_context=auth_context1) + self.send_pdu(req, ndr_print=ndr_print, hexdump=hexdump) + rep = self.recv_pdu(timeout=0.01) + self.assertIsNone(rep) + self.assertIsConnected() + + # context_id, opnum and auth header values are completely ignored + if auth_context_2nd == 1: + auth_context_copy = auth_context1.copy() + elif auth_context_2nd == 2: + auth_context_copy = auth_context2.copy() + else: + auth_context_copy = None + + expected_pfc_flags = dcerpc.DCERPC_PFC_FLAG_FIRST | dcerpc.DCERPC_PFC_FLAG_LAST + if expected_context_id is None: + expected_context_id = context_id + if expected_call_id is None: + expected_call_id = call_id + if not_executed: + expected_pfc_flags |= dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE + + if forced_call_id is not None: + call_id = forced_call_id + if forced_context_id is not None: + context_id = forced_context_id + if forced_opnum is not None: + opnum = forced_opnum + if forced_auth_context_id is not None: + auth_context_copy["auth_context_id"] = forced_auth_context_id + if forced_auth_type is not None: + auth_context_copy["auth_type"] = forced_auth_type + if forced_auth_level is not None: + auth_context_copy["auth_level"] = forced_auth_level + + pfc_flags = samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST + stub_in_tmp = stub_in[16:-1] + req = self.generate_request_auth(call_id=call_id, + context_id=context_id, + pfc_flags=pfc_flags_2nd, + opnum=opnum, + alloc_hint=len(stub_in_tmp), + stub=stub_in_tmp, + auth_context=auth_context_copy) + self.send_pdu(req, ndr_print=ndr_print, hexdump=hexdump) + if expected_fault is None: + self.do_single_request(call_id=3, ctx=ctx, io=io, send_req=False, auth_context=auth_context1) + return + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, expected_call_id, + pfc_flags=expected_pfc_flags, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, expected_context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, expected_fault) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + if not_executed: + # still alive + rep = self.recv_pdu(timeout=0.01) + self.assertIsNone(rep) + self.assertIsConnected() + return + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def _test_generic_auth_first_last(self, + auth_type, + expected_fault, + auth_context_2nd=2, + expected_call_id=None, + expected_context_id=None, + conc_mpx=False, + not_executed=False, + forced_call_id=None, + forced_context_id=None, + forced_opnum=None, + forced_auth_context_id=None, + forced_auth_type=None, + forced_auth_level=None): + pfc_flags_2nd = samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST + return self._test_generic_auth_first_2nd(auth_type, + pfc_flags_2nd, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def _test_generic_auth_first_first(self, + auth_type, + expected_fault, + auth_context_2nd=2, + expected_call_id=None, + expected_context_id=None, + conc_mpx=False, + not_executed=False, + forced_call_id=None, + forced_context_id=None, + forced_opnum=None, + forced_auth_context_id=None, + forced_auth_type=None, + forced_auth_level=None): + pfc_flags_2nd = samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST + return self._test_generic_auth_first_2nd(auth_type, + pfc_flags_2nd, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_firstSame2(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_FAULT_SEC_PKG_ERROR + auth_context_2nd = 2 + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = None + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_firstNext2(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = 2 + expected_call_id = 3 + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = 4 + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_firstSame111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = 1 + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = None + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = 111 + forced_auth_type = 111 + forced_auth_level = 111 + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_MPX_first1_firstSame111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = 1 + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = True + forced_call_id = None + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = 111 + forced_auth_type = 111 + forced_auth_level = 111 + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_firstNext111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = 1 + expected_call_id = 3 + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = 4 + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = 111 + forced_auth_type = 111 + forced_auth_level = 111 + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_MPX_first1_firstNext111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = 1 + expected_call_id = 4 + expected_context_id = 0 + not_executed = False + conc_mpx = True + forced_call_id = 4 + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = 111 + forced_auth_type = 111 + forced_auth_level = 111 + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_firstSameNone(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = None + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_MPX_first1_firstSameNone(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = True + forced_call_id = None + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_firstNextNone(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = 4 + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_MPX_first1_firstNextNone(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = 4 + expected_context_id = 0 + not_executed = False + conc_mpx = True + forced_call_id = 4 + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_firstSameNone111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = None + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_MPX_first1_firstSameNone111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = True + forced_call_id = None + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_first1_firstNextNone111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = None + expected_context_id = None + not_executed = False + conc_mpx = False + forced_call_id = 4 + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_multi_auth_MPX_first1_firstNextNone111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + auth_context_2nd = None + expected_call_id = 4 + expected_context_id = 0 + not_executed = False + conc_mpx = True + forced_call_id = 4 + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_first_first(auth_type, + expected_fault, + auth_context_2nd=auth_context_2nd, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def _test_generic_auth_middle(self, + auth_type, + expected_fault, + expected_context_id=None, + not_executed=False, + conc_mpx=False, + forced_context_id=None, + forced_opnum=None, + forced_auth_context_id=None, + forced_auth_type=None, + forced_auth_level=None): + auth_context_2nd = 1 + skip_first = True + pfc_flags_2nd = 0 + expected_call_id = None + forced_call_id = None + return self._test_generic_auth_first_2nd(auth_type, + pfc_flags_2nd, + expected_fault, + auth_context_2nd=auth_context_2nd, + skip_first=skip_first, + expected_call_id=expected_call_id, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_call_id=forced_call_id, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_middle_alone(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + expected_context_id = 0 + not_executed = False + conc_mpx = False + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_MPX_middle_alone(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + expected_context_id = None + not_executed = False + conc_mpx = True + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_middle_all_111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + expected_context_id = 0 + not_executed = False + conc_mpx = False + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = 111 + forced_auth_type = 111 + forced_auth_level = 111 + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_MPX_middle_all_111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_UNKNOWN_IF + expected_context_id = 0 + not_executed = True + conc_mpx = True + forced_context_id = 111 + forced_opnum = 111 + forced_auth_context_id = 111 + forced_auth_type = 111 + forced_auth_level = 111 + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_middle_auth_all_111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + expected_context_id = 0 + not_executed = False + conc_mpx = False + forced_context_id = None + forced_opnum = 111 + forced_auth_context_id = 111 + forced_auth_type = 111 + forced_auth_level = 111 + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_MPX_middle_auth_all_111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_FAULT_ACCESS_DENIED + expected_context_id = None + not_executed = False + conc_mpx = True + forced_context_id = None + forced_opnum = 111 + forced_auth_context_id = 111 + forced_auth_type = 111 + forced_auth_level = 111 + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_middle_auth_context_111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + expected_context_id = 0 + not_executed = False + conc_mpx = False + forced_context_id = None + forced_opnum = None + forced_auth_context_id = 111 + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_MPX_middle_auth_context_111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_FAULT_ACCESS_DENIED + expected_context_id = None + not_executed = False + conc_mpx = True + forced_context_id = None + forced_opnum = None + forced_auth_context_id = 111 + forced_auth_type = None + forced_auth_level = None + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_middle_auth_type_111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + expected_context_id = 0 + not_executed = False + conc_mpx = False + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = 111 + forced_auth_level = None + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_MPX_middle_auth_type_111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_FAULT_ACCESS_DENIED + expected_context_id = None + not_executed = False + conc_mpx = True + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = 111 + forced_auth_level = None + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_middle_auth_level_111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + expected_context_id = 0 + not_executed = False + conc_mpx = False + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = 111 + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def test_ntlmssp_auth_MPX_middle_auth_level_111(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + expected_fault = dcerpc.DCERPC_FAULT_ACCESS_DENIED + expected_context_id = None + not_executed = False + conc_mpx = True + forced_context_id = None + forced_opnum = None + forced_auth_context_id = None + forced_auth_type = None + forced_auth_level = 111 + return self._test_generic_auth_middle(auth_type, + expected_fault, + expected_context_id=expected_context_id, + not_executed=not_executed, + conc_mpx=conc_mpx, + forced_context_id=forced_context_id, + forced_opnum=forced_opnum, + forced_auth_context_id=forced_auth_context_id, + forced_auth_type=forced_auth_type, + forced_auth_level=forced_auth_level) + + def _test_neg_xmit_check_values(self, + req_xmit=None, + req_recv=None, + rep_both=None, + alter_xmit=None, + alter_recv=None): + 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 + + req = self.generate_bind(call_id=0, + max_xmit_frag=req_xmit, + max_recv_frag=req_recv, + ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, rep_both) + self.assertEqual(rep.u.max_recv_frag, rep_both) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + assoc_group_id = rep.u.assoc_group_id + if alter_xmit is None: + alter_xmit = rep_both - 8 + if alter_recv is None: + alter_recv = rep_both - 8 + + # max_{xmit,recv}_frag and assoc_group_id are completely + # ignored in alter_context requests + req = self.generate_alter(call_id=1, + max_xmit_frag=alter_xmit, + max_recv_frag=alter_recv, + assoc_group_id=0xffffffff - rep.u.assoc_group_id, + ctx_list=[ctx1]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, rep_both) + self.assertEqual(rep.u.max_recv_frag, rep_both) + self.assertEqual(rep.u.assoc_group_id, rep.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + chunk_size = rep_both - dcerpc.DCERPC_REQUEST_LENGTH + req = self.generate_request(call_id=2, + context_id=ctx1.context_id, + opnum=0, + alloc_hint=0xffffffff, + stub=b"\00" * chunk_size) + self.send_pdu(req, ndr_print=True, hexdump=True) + rep = self.recv_pdu(ndr_print=True, hexdump=True) + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + chunk_size = 5840 - dcerpc.DCERPC_REQUEST_LENGTH + req = self.generate_request(call_id=2, + context_id=ctx1.context_id, + opnum=0, + alloc_hint=0xffffffff, + stub=b"\00" * chunk_size) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + chunk_size += 1 + req = self.generate_request(call_id=3, + context_id=ctx1.context_id, + opnum=0, + alloc_hint=0xffffffff, + stub=b"\00" * chunk_size) + self.send_pdu(req) + rep = self.recv_pdu() + # We get a fault + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_neg_xmit_ffff_ffff(self): + return self._test_neg_xmit_check_values(req_xmit=0xffff, + req_recv=0xffff, + rep_both=5840) + + def test_neg_xmit_0_ffff(self): + return self._test_neg_xmit_check_values(req_xmit=0, + req_recv=0xffff, + rep_both=2048, + alter_xmit=0xffff, + alter_recv=0xffff) + + def test_neg_xmit_ffff_0(self): + return self._test_neg_xmit_check_values(req_xmit=0xffff, + req_recv=0, + rep_both=2048) + + def test_neg_xmit_0_0(self): + return self._test_neg_xmit_check_values(req_xmit=0, + req_recv=0, + rep_both=2048, + alter_xmit=0xffff, + alter_recv=0xffff) + + def test_neg_xmit_3199_0(self): + return self._test_neg_xmit_check_values(req_xmit=3199, + req_recv=0, + rep_both=2048) + + def test_neg_xmit_0_3199(self): + return self._test_neg_xmit_check_values(req_xmit=0, + req_recv=3199, + rep_both=2048) + + def test_neg_xmit_3199_ffff(self): + return self._test_neg_xmit_check_values(req_xmit=3199, + req_recv=0xffff, + rep_both=3192) + + def test_neg_xmit_ffff_3199(self): + return self._test_neg_xmit_check_values(req_xmit=0xffff, + req_recv=3199, + rep_both=3192) + + def test_alloc_hint(self): + ndr32 = base.transfer_syntax_ndr() + + tsf1_list = [ndr32] + ctx = dcerpc.ctx_list() + ctx.context_id = 0 + ctx.num_transfer_syntaxes = len(tsf1_list) + ctx.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx.transfer_syntaxes = tsf1_list + + req = self.generate_bind(call_id=0, + ctx_list=[ctx]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertEqual(rep.u.auth_info, b'\0' * 0) + + # And now try a request without auth_info + req = self.generate_request(call_id=2, + context_id=ctx.context_id, + opnum=0, + alloc_hint=0xffffffff, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + req = self.generate_request(call_id=3, + context_id=ctx.context_id, + opnum=1, + alloc_hint=0xffffffff, + stub=b"\04\00\00\00\00\00\00\00") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + req = self.generate_request(call_id=4, + context_id=ctx.context_id, + opnum=1, + alloc_hint=1, + stub=b"\04\00\00\00\00\00\00\00") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + def _get_netlogon_ctx(self): + abstract = samba.dcerpc.netlogon.abstract_syntax() + ndr32 = base.transfer_syntax_ndr() + + (ctx, ack) = self.prepare_presentation(abstract, ndr32, context_id=0, + epmap=True, return_ack=True) + + server = '\\\\' + self.target_hostname + if isinstance(server, bytes): + server_utf16 = server.decode('utf-8').encode('utf-16-le') + else: + server_utf16 = server.encode('utf-16-le') + computer = 'UNKNOWNCOMPUTER' + if isinstance(server, bytes): + computer_utf16 = computer.decode('utf-8').encode('utf-16-le') + else: + computer_utf16 = computer.encode('utf-16-le') + + real_stub = struct.pack('<IIII', 0x00200000, + len(server) + 1, 0, len(server) + 1) + real_stub += server_utf16 + b'\x00\x00' + mod_len = len(real_stub) % 4 + if mod_len != 0: + real_stub += b'\x00' * (4 - mod_len) + real_stub += struct.pack('<III', + len(computer) + 1, 0, len(computer) + 1) + real_stub += computer_utf16 + b'\x00\x00' + real_stub += b'\x11\x22\x33\x44\x55\x66\x77\x88' + + return (ctx, ack, real_stub) + + def _test_fragmented_requests(self, remaining=None, alloc_hint=None, + fault_first=None, fault_last=None): + (ctx, rep, real_stub) = self._get_netlogon_ctx() + + chunk = rep.u.max_recv_frag - dcerpc.DCERPC_REQUEST_LENGTH + + total = 0 + first = True + while remaining > 0: + thistime = min(remaining, chunk) + remaining -= thistime + total += thistime + + pfc_flags = 0 + if first: + pfc_flags |= dcerpc.DCERPC_PFC_FLAG_FIRST + first = False + stub = real_stub + b'\x00' * (thistime - len(real_stub)) + else: + stub = b"\x00" * thistime + + if remaining == 0: + pfc_flags |= dcerpc.DCERPC_PFC_FLAG_LAST + + # And now try a request without auth_info + # netr_ServerReqChallenge() + req = self.generate_request(call_id=0x21234, + pfc_flags=pfc_flags, + context_id=ctx.context_id, + opnum=4, + alloc_hint=alloc_hint, + stub=stub) + if alloc_hint >= thistime: + alloc_hint -= thistime + else: + alloc_hint = 0 + self.send_pdu(req, hexdump=False) + if fault_first is not None: + rep = self.recv_pdu() + # We get a fault back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, fault_first) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + return + if remaining == 0: + break + if total >= 0x400000 and fault_last is not None: + rep = self.recv_pdu() + # We get a fault back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, fault_last) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + return + rep = self.recv_pdu(timeout=0.01) + self.assertIsNone(rep) + self.assertIsConnected() + + if total >= 0x400000 and fault_last is not None: + rep = self.recv_pdu() + # We get a fault back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, fault_last) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + return + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + self.assertEqual(len(rep.u.stub_and_verifier), 12) + status = struct.unpack_from("<I", rep.u.stub_and_verifier, len(rep.u.stub_and_verifier) - 4) + self.assertEqual(status[0], 0) + + def test_fragmented_requests01(self): + return self._test_fragmented_requests(remaining=0x400000, + alloc_hint=0x400000) + + def test_fragmented_requests02(self): + return self._test_fragmented_requests(remaining=0x400000, + alloc_hint=0x100000) + + def test_fragmented_requests03(self): + return self._test_fragmented_requests(remaining=0x400000, + alloc_hint=0) + + def test_fragmented_requests04(self): + return self._test_fragmented_requests(remaining=0x400000, + alloc_hint=0x400001, + fault_first=dcerpc.DCERPC_FAULT_ACCESS_DENIED) + + def test_fragmented_requests05(self): + return self._test_fragmented_requests(remaining=0x500001, + alloc_hint=0, + fault_last=dcerpc.DCERPC_FAULT_ACCESS_DENIED) + + def _test_same_requests(self, pfc_flags, fault_1st=False, fault_2nd=False): + (ctx, rep, real_stub) = self._get_netlogon_ctx() + + # netr_ServerReqChallenge with given flags + req = self.generate_request(call_id=2, + pfc_flags=pfc_flags, + context_id=ctx.context_id, + opnum=4, + stub=real_stub) + self.send_pdu(req) + if fault_1st: + rep = self.recv_pdu() + # We get a fault back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + return + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + # netr_ServerReqChallenge without DCERPC_PFC_FLAG_LAST + # with the same call_id + req = self.generate_request(call_id=2, + pfc_flags=pfc_flags, + context_id=ctx.context_id, + opnum=4, + stub=real_stub) + self.send_pdu(req) + if fault_2nd: + rep = self.recv_pdu() + # We get a fault back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + return + + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + def test_first_only_requests(self): + return self._test_same_requests(pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST, + fault_2nd=True) + + def test_none_only_requests(self): + return self._test_same_requests(pfc_flags=0, fault_1st=True) + + def test_last_only_requests(self): + return self._test_same_requests(pfc_flags=dcerpc.DCERPC_PFC_FLAG_LAST, + fault_1st=True) + + def test_first_maybe_requests(self): + return self._test_same_requests(pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_MAYBE, + fault_2nd=True) + + def test_first_didnot_requests(self): + return self._test_same_requests(pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE, + fault_2nd=True) + + def test_first_cmpx_requests(self): + return self._test_same_requests(pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_CONC_MPX, + fault_2nd=True) + + def test_first_08_requests(self): + return self._test_same_requests(pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST | + 0x08, + fault_2nd=True) + + def test_first_cancel_requests(self): + (ctx, rep, real_stub) = self._get_netlogon_ctx() + + # netr_ServerReqChallenge with given flags + req = self.generate_request(call_id=2, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_PENDING_CANCEL, + context_id=ctx.context_id, + opnum=4, + stub=real_stub) + self.send_pdu(req) + rep = self.recv_pdu() + # We get a fault back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST | + dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_FAULT_NO_CALL_ACTIVE) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_2nd_cancel_requests(self): + (ctx, rep, real_stub) = self._get_netlogon_ctx() + + # netr_ServerReqChallenge with given flags + req = self.generate_request(call_id=2, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST, + context_id=ctx.context_id, + opnum=4, + stub=real_stub) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + # netr_ServerReqChallenge with given flags + req = self.generate_request(call_id=2, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_PENDING_CANCEL, + context_id=ctx.context_id, + opnum=4, + stub=real_stub) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + # netr_ServerReqChallenge with given flags + req = self.generate_request(call_id=2, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_LAST, + context_id=ctx.context_id, + opnum=4, + stub=real_stub) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + self.assertEqual(len(rep.u.stub_and_verifier), 12) + status = struct.unpack_from("<I", rep.u.stub_and_verifier, len(rep.u.stub_and_verifier) - 4) + self.assertEqual(status[0], 0) + + def test_last_cancel_requests(self): + (ctx, rep, real_stub) = self._get_netlogon_ctx() + + # netr_ServerReqChallenge with given flags + req = self.generate_request(call_id=2, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST, + context_id=ctx.context_id, + opnum=4, + stub=real_stub[:4]) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + # netr_ServerReqChallenge with given flags + req = self.generate_request(call_id=2, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_LAST | + dcerpc.DCERPC_PFC_FLAG_PENDING_CANCEL, + context_id=ctx.context_id, + opnum=4, + stub=real_stub[4:]) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + self.assertEqual(len(rep.u.stub_and_verifier), 12) + status = struct.unpack_from("<I", rep.u.stub_and_verifier, len(rep.u.stub_and_verifier) - 4) + self.assertEqual(status[0], 0) + + def test_mix_requests(self): + (ctx, rep, real_stub) = self._get_netlogon_ctx() + + # netr_ServerReqChallenge with given flags + req = self.generate_request(call_id=50, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST, + context_id=ctx.context_id, + opnum=4, + stub=real_stub) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + # netr_ServerReqChallenge with given flags + req = self.generate_request(call_id=51, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST, + context_id=ctx.context_id, + opnum=4, + stub=real_stub) + self.send_pdu(req) + rep = self.recv_pdu() + # We get a fault back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, 50, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + def test_co_cancel_no_request(self): + ndr32 = base.transfer_syntax_ndr() + abstract = samba.dcerpc.mgmt.abstract_syntax() + ctx = self.prepare_presentation(abstract, ndr32, context_id=0xff) + + req = self.generate_co_cancel(call_id=3) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.01) + self.assertIsNone(rep) + self.assertIsConnected() + + # And now try a request + req = self.generate_request(call_id=1, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + def test_co_cancel_request_after_first(self): + ndr32 = base.transfer_syntax_ndr() + abstract = samba.dcerpc.mgmt.abstract_syntax() + ctx = self.prepare_presentation(abstract, ndr32, context_id=0xff) + + req = self.generate_request(call_id=1, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.01) + self.assertIsNone(rep) + self.assertIsConnected() + + req = self.generate_co_cancel(call_id=1) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.01) + self.assertIsNone(rep) + self.assertIsConnected() + + req = self.generate_request(call_id=1, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_LAST, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + # And now try a request + req = self.generate_request(call_id=2, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + def test_orphaned_no_request(self): + ndr32 = base.transfer_syntax_ndr() + abstract = samba.dcerpc.mgmt.abstract_syntax() + ctx = self.prepare_presentation(abstract, ndr32) + + req = self.generate_orphaned(call_id=3) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.01) + self.assertIsNone(rep) + self.assertIsConnected() + + # And now try a request + req = self.generate_request(call_id=1, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id & 0xff) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + def test_orphaned_request_after_first_last(self): + ndr32 = base.transfer_syntax_ndr() + abstract = samba.dcerpc.mgmt.abstract_syntax() + ctx = self.prepare_presentation(abstract, ndr32) + + req = self.generate_request(call_id=1, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + req = self.generate_orphaned(call_id=1) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + req = self.generate_request(call_id=1, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_LAST, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id & 0xff) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + # And now try a request + req = self.generate_request(call_id=2, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id & 0xff) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + def test_orphaned_request_after_first_mpx_last(self): + ndr32 = base.transfer_syntax_ndr() + abstract = samba.dcerpc.mgmt.abstract_syntax() + + pfc_flags = samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST + pfc_flags |= samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST + pfc_flags |= samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_CONC_MPX + ctx = self.prepare_presentation(abstract, ndr32, pfc_flags=pfc_flags) + + req = self.generate_request(call_id=1, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + req = self.generate_orphaned(call_id=1) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + req = self.generate_request(call_id=1, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_LAST, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id & 0xff) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + # And now try a request + req = self.generate_request(call_id=2, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id & 0xff) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + def test_orphaned_request_after_first_no_last(self): + ndr32 = base.transfer_syntax_ndr() + abstract = samba.dcerpc.mgmt.abstract_syntax() + ctx = self.prepare_presentation(abstract, ndr32) + + req1 = self.generate_request(call_id=1, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req1) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + req = self.generate_orphaned(call_id=1) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + # And now try a new request + req2 = self.generate_request(call_id=2, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req2) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req1.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req1.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_orphaned_request_after_first_mpx_no_last(self): + ndr32 = base.transfer_syntax_ndr() + abstract = samba.dcerpc.mgmt.abstract_syntax() + + pfc_flags = samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST + pfc_flags |= samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST + pfc_flags |= samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_CONC_MPX + ctx = self.prepare_presentation(abstract, ndr32, + pfc_flags=pfc_flags) + + req1 = self.generate_request(call_id=1, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST, + context_id=ctx.context_id, + opnum=0, + stub=b"") + self.send_pdu(req1) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + req = self.generate_orphaned(call_id=1) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.1) + self.assertIsNone(rep) + self.assertIsConnected() + + # And now try a new request + req2 = self.generate_request(call_id=2, + context_id=ctx.context_id - 1, + opnum=0, + stub=b"") + self.send_pdu(req2) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req2.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def _test_spnego_connect_upgrade_request(self, upgrade_auth_level): + 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 = self.get_anon_creds() + 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 = b"" + (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) + + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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) + + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id & 0xff) + self.assertEqual(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=b"\x01" +b"\x00" *15) + req = self.generate_request(call_id=3, + context_id=ctx1.context_id, + opnum=0, + stub=b"", + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id & 0xff) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + # Now a request with auth_info upgrade_auth_level + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=upgrade_auth_level, + auth_context_id=auth_context_id, + auth_blob=b"\x01" + b"\x00" * 15) + req = self.generate_request(call_id=4, + context_id=ctx1.context_id, + opnum=0, + stub=b"", + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + # We get a fault back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_FAULT_ACCESS_DENIED) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_connect_packet_upgrade(self): + return self._test_spnego_connect_upgrade_request( + dcerpc.DCERPC_AUTH_LEVEL_PACKET) + + def test_spnego_connect_integrity_upgrade(self): + return self._test_spnego_connect_upgrade_request( + dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY) + + def _test_spnego_connect_downgrade_request(self, initial_auth_level): + 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 = self.get_anon_creds() + 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 = initial_auth_level + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = b"" + (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) + + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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) + + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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) + + # Now a request with auth_info DCERPC_AUTH_LEVEL_CONNECT + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=dcerpc.DCERPC_AUTH_LEVEL_CONNECT, + auth_context_id=auth_context_id, + auth_blob=b"\x01" + b"\x00" * 15) + req = self.generate_request(call_id=3, + context_id=ctx1.context_id, + opnum=0, + stub=b"", + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + # We get a fault back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_FAULT_ACCESS_DENIED) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_packet_downgrade_connect(self): + return self._test_spnego_connect_downgrade_request( + dcerpc.DCERPC_AUTH_LEVEL_PACKET) + + def test_spnego_integrity_downgrade_connect(self): + return self._test_spnego_connect_upgrade_request( + dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY) + + def test_spnego_unfinished_request(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 = self.get_anon_creds() + 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 = b"" + (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) + + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + assoc_group_id = rep.u.assoc_group_id + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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) + + # 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=b"\x01" + b"\x00" * 15) + req = self.generate_request(call_id=1, + context_id=ctx1.context_id, + opnum=0, + stub=b"", + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + # We get a fault + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_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 = self.get_anon_creds() + 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 = b"" + (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) + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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_auth3(call_id=0, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.01) + self.assertIsNone(rep) + 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=b"") + self.send_pdu(req) + rep = self.recv_pdu() + # We get a fault back + self.verify_pdu(rep, dcerpc.DCERPC_PKT_FAULT, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_FAULT_ACCESS_DENIED) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_connect_reauth_alter(self): + ndr32 = base.transfer_syntax_ndr() + ndr64 = base.transfer_syntax_ndr64() + + 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 = self.get_anon_creds() + 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 = b"" + (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) + + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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=[ctx1], + 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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id & 0xff) + self.assertEqual(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=b"\x01" + b"\x00" * 15) + req = self.generate_request(call_id=3, + context_id=ctx1.context_id, + opnum=0, + stub=b"", + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + # Now a reauth + + 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 = b"" + (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, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + # We get a fault + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_FAULT_ACCESS_DENIED) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_connect_reauth_auth3(self): + ndr32 = base.transfer_syntax_ndr() + ndr64 = base.transfer_syntax_ndr64() + + 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 = self.get_anon_creds() + 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 = b"" + (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) + + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_BIND_ACK, req.call_id) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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=[ctx1], + 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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(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=b"\x01" + b"\x00" * 15) + req = self.generate_request(call_id=3, + context_id=ctx1.context_id, + opnum=0, + stub=b"", + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + + # Now a reauth + + 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 = b"" + (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_auth3(call_id=0, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + # We get a fault + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_change_auth_level(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 + + c = self.get_anon_creds() + 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_INTEGRITY + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = b"" + (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=[ctx1], + 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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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=dcerpc.DCERPC_AUTH_LEVEL_PRIVACY, + auth_context_id=auth_context_id, + auth_blob=to_server) + req = self.generate_alter(call_id=0, + ctx_list=[ctx1], + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_FAULT_ACCESS_DENIED) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_change_abstract(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 + + ctx1b = dcerpc.ctx_list() + ctx1b.context_id = 1 + ctx1b.num_transfer_syntaxes = len(tsf1_list) + ctx1b.abstract_syntax = samba.dcerpc.epmapper.abstract_syntax() + ctx1b.transfer_syntaxes = tsf1_list + + c = self.get_anon_creds() + 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_INTEGRITY + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = b"" + (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=[ctx1], + 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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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=dcerpc.DCERPC_AUTH_LEVEL_PRIVACY, + auth_context_id=auth_context_id, + auth_blob=to_server) + req = self.generate_alter(call_id=0, + ctx_list=[ctx1b], + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_change_transfer(self): + ndr32 = base.transfer_syntax_ndr() + ndr64 = base.transfer_syntax_ndr64() + + 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 + + tsf1b_list = [ndr32, ndr64] + ctx1b = dcerpc.ctx_list() + ctx1b.context_id = 1 + ctx1b.num_transfer_syntaxes = len(tsf1b_list) + ctx1b.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1b.transfer_syntaxes = tsf1b_list + + c = self.get_anon_creds() + 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_INTEGRITY + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = b"" + (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=[ctx1], + 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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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) + + # We change ctx_list and auth_level + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=dcerpc.DCERPC_AUTH_LEVEL_PRIVACY, + auth_context_id=auth_context_id, + auth_blob=to_server) + req = self.generate_alter(call_id=0, + ctx_list=[ctx1b], + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_change_auth_type1(self): + ndr32 = base.transfer_syntax_ndr() + ndr64 = base.transfer_syntax_ndr64() + + 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 + + c = self.get_anon_creds() + 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_INTEGRITY + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = b"" + (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=[ctx1], + 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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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) + + # We change ctx_list and auth_level + auth_info = self.generate_auth(auth_type=dcerpc.DCERPC_AUTH_TYPE_KRB5, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob=to_server) + req = self.generate_alter(call_id=0, + ctx_list=[ctx1], + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_FAULT_SEC_PKG_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_change_auth_type2(self): + ndr32 = base.transfer_syntax_ndr() + ndr64 = base.transfer_syntax_ndr64() + + 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 + + tsf1b_list = [ndr32, ndr64] + ctx1b = dcerpc.ctx_list() + ctx1b.context_id = 1 + ctx1b.num_transfer_syntaxes = len(tsf1b_list) + ctx1b.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1b.transfer_syntaxes = tsf1b_list + + c = self.get_anon_creds() + 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_INTEGRITY + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = b"" + (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=[ctx1], + 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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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) + + # We change ctx_list and auth_level + auth_info = self.generate_auth(auth_type=dcerpc.DCERPC_AUTH_TYPE_KRB5, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob=to_server) + req = self.generate_alter(call_id=0, + ctx_list=[ctx1b], + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_change_auth_type3(self): + ndr32 = base.transfer_syntax_ndr() + ndr64 = base.transfer_syntax_ndr64() + + 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 + + tsf1b_list = [ndr32, ndr64] + ctx1b = dcerpc.ctx_list() + ctx1b.context_id = 1 + ctx1b.num_transfer_syntaxes = len(tsf1b_list) + ctx1b.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1b.transfer_syntaxes = tsf1b_list + + c = self.get_anon_creds() + 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_INTEGRITY + auth_context_id = 2 + g.start_mech_by_authtype(auth_type, auth_level) + from_server = b"" + (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=[ctx1], + 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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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) + + # We change ctx_list and auth_level + auth_info = self.generate_auth(auth_type=dcerpc.DCERPC_AUTH_TYPE_NONE, + auth_level=auth_level, + auth_context_id=auth_context_id, + auth_blob=to_server) + req = self.generate_alter(call_id=0, + ctx_list=[ctx1b], + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_FAULT_ACCESS_DENIED) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + 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 = self.get_anon_creds() + 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 = b"" + (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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(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=b"\x01" + b"\x00" * 15) + req = self.generate_request(call_id=3, + context_id=ctx1.context_id, + opnum=0, + stub=b"", + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(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 = self.get_anon_creds() + 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 = b"" + (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.assertEqual(rep.u.reject_reason, + dcerpc.DCERPC_BIND_NAK_REASON_PROTOCOL_VERSION_NOT_SUPPORTED) + self.assertEqual(rep.u.num_versions, 1) + self.assertEqual(rep.u.versions[0].rpc_vers, req.rpc_vers) + self.assertEqual(rep.u.versions[0].rpc_vers_minor, req.rpc_vers_minor) + self.assertEqual(len(rep.u._pad), 3) + self.assertEqual(rep.u._pad, b'\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 = self.get_anon_creds() + 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 = b"" + (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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_PROTO_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # 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 = self.get_anon_creds() + 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 = b"" + (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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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) + rep = self.recv_pdu(timeout=0.01) + self.assertIsNone(rep) + 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=b"") + self.send_pdu(req) + rep = self.recv_pdu() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(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=b"\x01" + b"\x00" * 15) + req = self.generate_request(call_id=3, + context_id=ctx1.context_id, + opnum=0, + stub=b"", + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id) + self.assertEqual(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 = self.get_anon_creds() + 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 = b"" + (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.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertNotEqual(rep.u.assoc_group_id, req.u.assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 4) + self.assertEqual(rep.u.secondary_address, "%d" % self.tcp_port) + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ndr32) + self.assertNotEqual(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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_FAULT_REMOTE_NO_MEMORY) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def _test_auth_bind_auth_level(self, auth_type, auth_level, auth_context_id, ctx, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY, + hdr_signing=False, + alter_fault=None): + creds = self.get_user_creds() + auth_context = self.get_auth_context_creds(creds=creds, + auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + g_auth_level=g_auth_level, + hdr_signing=hdr_signing) + if auth_context is None: + return None + ack = self.do_generic_bind(ctx=ctx, + auth_context=auth_context, + alter_fault=alter_fault) + if ack is None: + return None + return auth_context + + def _test_spnego_level_bind_nak(self, auth_level, + reason=dcerpc.DCERPC_BIND_NAK_REASON_INVALID_CHECKSUM): + c = self.get_user_creds() + return self._test_auth_type_level_bind_nak(auth_type=dcerpc.DCERPC_AUTH_TYPE_SPNEGO, + auth_level=auth_level, creds=c, reason=reason) + + def _test_spnego_level_bind(self, auth_level, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY, + alter_fault=None, + request_fault=None, + response_fault_flags=0): + ndr32 = base.transfer_syntax_ndr() + + tsf1_list = [ndr32] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 0x1001 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1.transfer_syntaxes = tsf1_list + + auth_type = dcerpc.DCERPC_AUTH_TYPE_SPNEGO + auth_context_id = 2 + + auth_context = self._test_auth_bind_auth_level(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + ctx=ctx1, + g_auth_level=g_auth_level, + alter_fault=alter_fault) + if request_fault is None: + return + + self.assertIsNotNone(auth_context) + g = auth_context["gensec"] + self.assertIsNotNone(g) + + stub_bin = b'\x00' * 17 + mod_len = len(stub_bin) % dcerpc.DCERPC_AUTH_PAD_ALIGNMENT + auth_pad_length = 0 + if mod_len > 0: + auth_pad_length = dcerpc.DCERPC_AUTH_PAD_ALIGNMENT - mod_len + stub_bin += b'\x00' * auth_pad_length + + if g_auth_level >= dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY: + sig_size = g.sig_size(len(stub_bin)) + else: + sig_size = 16 + zero_sig = b"\x00" * sig_size + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_pad_length=auth_pad_length, + auth_context_id=auth_context_id, + auth_blob=zero_sig) + req = self.generate_request(call_id=4, + context_id=ctx1.context_id, + opnum=0xffff, + stub=stub_bin, + auth_info=auth_info) + if g_auth_level >= dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY: + req_blob = samba.ndr.ndr_pack(req) + ofs_stub = dcerpc.DCERPC_REQUEST_LENGTH + ofs_sig = len(req_blob) - req.auth_length + ofs_trailer = ofs_sig - dcerpc.DCERPC_AUTH_TRAILER_LENGTH + req_data = req_blob[ofs_stub:ofs_trailer] + req_whole = req_blob[0:ofs_sig] + sig = g.sign_packet(req_data, req_whole) + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_pad_length=auth_pad_length, + auth_context_id=auth_context_id, + auth_blob=sig) + req = self.generate_request(call_id=4, + context_id=ctx1.context_id, + opnum=0xffff, + stub=stub_bin, + 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 | response_fault_flags, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, ctx1.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, request_fault) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + if response_fault_flags & dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE: + return + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_spnego_none_bind(self): + return self._test_spnego_level_bind_nak(dcerpc.DCERPC_AUTH_LEVEL_NONE, + reason=dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + + def test_spnego_call_bind(self): + return self._test_spnego_level_bind_nak(dcerpc.DCERPC_AUTH_LEVEL_CALL, + reason=dcerpc.DCERPC_BIND_NAK_REASON_INVALID_CHECKSUM) + + def test_spnego_0_bind(self): + return self._test_spnego_level_bind_nak(0, + reason=dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + + def test_spnego_7_bind(self): + return self._test_spnego_level_bind_nak(7, + reason=dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + + def test_spnego_255_bind(self): + return self._test_spnego_level_bind_nak(255, + reason=dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + + def test_spnego_connect_bind_none(self): + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_CONNECT, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_CONNECT) + + def test_spnego_connect_bind_sign(self): + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_CONNECT, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY) + + def test_spnego_connect_bind_seal(self): + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_CONNECT, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_PRIVACY) + + def test_spnego_packet_bind_none(self): + # DCERPC_AUTH_LEVEL_PACKET is handled as alias of + # DCERPC_AUTH_LEVEL_INTEGRITY + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_PACKET, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_CONNECT, + request_fault=dcerpc.DCERPC_FAULT_SEC_PKG_ERROR) + + def test_spnego_packet_bind_sign(self): + # DCERPC_AUTH_LEVEL_PACKET is handled as alias of + # DCERPC_AUTH_LEVEL_INTEGRITY + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_PACKET, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY, + request_fault=dcerpc.DCERPC_NCA_S_OP_RNG_ERROR, + response_fault_flags=dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE) + + def test_spnego_packet_bind_seal(self): + # DCERPC_AUTH_LEVEL_PACKET is handled as alias of + # DCERPC_AUTH_LEVEL_INTEGRITY + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_PACKET, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_PRIVACY, + request_fault=dcerpc.DCERPC_NCA_S_OP_RNG_ERROR, + response_fault_flags=dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE) + + def test_spnego_integrity_bind_none(self): + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_CONNECT, + request_fault=dcerpc.DCERPC_FAULT_SEC_PKG_ERROR) + + def test_spnego_integrity_bind_sign(self): + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY, + request_fault=dcerpc.DCERPC_NCA_S_OP_RNG_ERROR, + response_fault_flags=dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE) + + def test_spnego_integrity_bind_seal(self): + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_PRIVACY, + request_fault=dcerpc.DCERPC_NCA_S_OP_RNG_ERROR, + response_fault_flags=dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE) + + def test_spnego_privacy_bind_none(self): + # This fails... + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_PRIVACY, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_CONNECT, + alter_fault=dcerpc.DCERPC_FAULT_SEC_PKG_ERROR) + + def test_spnego_privacy_bind_sign(self): + # This fails... + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_PRIVACY, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY, + alter_fault=dcerpc.DCERPC_FAULT_SEC_PKG_ERROR) + + def test_spnego_privacy_bind_seal(self): + return self._test_spnego_level_bind(auth_level=dcerpc.DCERPC_AUTH_LEVEL_PRIVACY, + g_auth_level=dcerpc.DCERPC_AUTH_LEVEL_PRIVACY) + + def _test_auth_signing_auth_level_request(self, auth_type, auth_level, hdr_sign=False): + ndr32 = base.transfer_syntax_ndr() + + tsf1_list = [ndr32] + ctx1 = dcerpc.ctx_list() + ctx1.context_id = 0x1001 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = samba.dcerpc.mgmt.abstract_syntax() + ctx1.transfer_syntaxes = tsf1_list + ctx_list = [ctx1] + + auth_context_id = 2 + + auth_context = self._test_auth_bind_auth_level(auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + hdr_signing=hdr_sign, + ctx=ctx1) + self.assertIsNotNone(auth_context) + g = auth_context["gensec"] + self.assertIsNotNone(g) + + stub_bin = b'\x00' * 0 + mod_len = len(stub_bin) % dcerpc.DCERPC_AUTH_PAD_ALIGNMENT + auth_pad_length = 0 + if mod_len > 0: + auth_pad_length = dcerpc.DCERPC_AUTH_PAD_ALIGNMENT - mod_len + stub_bin += b'\x00' * auth_pad_length + + sig_size = g.sig_size(len(stub_bin)) + zero_sig = b"\x00" * sig_size + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_pad_length=auth_pad_length, + auth_context_id=auth_context_id, + auth_blob=zero_sig) + req = self.generate_request(call_id=3, + context_id=ctx1.context_id, + opnum=0, + stub=stub_bin, + auth_info=auth_info) + req_blob = samba.ndr.ndr_pack(req) + ofs_stub = dcerpc.DCERPC_REQUEST_LENGTH + ofs_sig = len(req_blob) - req.auth_length + ofs_trailer = ofs_sig - dcerpc.DCERPC_AUTH_TRAILER_LENGTH + req_data = req_blob[ofs_stub:ofs_trailer] + req_whole = req_blob[0:ofs_sig] + sig = g.sign_packet(req_data, req_whole) + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_pad_length=auth_pad_length, + auth_context_id=auth_context_id, + auth_blob=sig) + req = self.generate_request(call_id=3, + context_id=ctx1.context_id, + opnum=0, + stub=stub_bin, + auth_info=auth_info) + self.send_pdu(req) + (rep, rep_blob) = self.recv_pdu_raw() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=sig_size) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id & 0xff) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + self.assertEqual(rep.auth_length, sig_size) + + ofs_stub = dcerpc.DCERPC_REQUEST_LENGTH + ofs_sig = rep.frag_length - rep.auth_length + ofs_trailer = ofs_sig - dcerpc.DCERPC_AUTH_TRAILER_LENGTH + rep_data = rep_blob[ofs_stub:ofs_trailer] + rep_whole = rep_blob[0:ofs_sig] + rep_sig = rep_blob[ofs_sig:] + rep_auth_info_blob = rep_blob[ofs_trailer:] + + rep_auth_info = self.parse_auth(rep_auth_info_blob) + self.assertEqual(rep_auth_info.auth_type, auth_type) + self.assertEqual(rep_auth_info.auth_level, auth_level) + # mgmt_inq_if_ids() returns no fixed size results + #self.assertEqual(rep_auth_info.auth_pad_length, 0) + self.assertEqual(rep_auth_info.auth_reserved, 0) + self.assertEqual(rep_auth_info.auth_context_id, auth_context_id) + self.assertEqual(rep_auth_info.credentials, rep_sig) + + g.check_packet(rep_data, rep_whole, rep_sig) + + stub_bin = b'\x00' * 17 + mod_len = len(stub_bin) % dcerpc.DCERPC_AUTH_PAD_ALIGNMENT + auth_pad_length = 0 + if mod_len > 0: + auth_pad_length = dcerpc.DCERPC_AUTH_PAD_ALIGNMENT - mod_len + stub_bin += b'\x00' * auth_pad_length + + sig_size = g.sig_size(len(stub_bin)) + zero_sig = b"\x00" * sig_size + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_pad_length=auth_pad_length, + auth_context_id=auth_context_id, + auth_blob=zero_sig) + req = self.generate_request(call_id=4, + context_id=ctx1.context_id, + opnum=0xffff, + stub=stub_bin, + auth_info=auth_info) + req_blob = samba.ndr.ndr_pack(req) + ofs_stub = dcerpc.DCERPC_REQUEST_LENGTH + ofs_sig = len(req_blob) - req.auth_length + ofs_trailer = ofs_sig - dcerpc.DCERPC_AUTH_TRAILER_LENGTH + req_data = req_blob[ofs_stub:ofs_trailer] + req_whole = req_blob[0:ofs_sig] + sig = g.sign_packet(req_data, req_whole) + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_pad_length=auth_pad_length, + auth_context_id=auth_context_id, + auth_blob=sig) + req = self.generate_request(call_id=4, + context_id=ctx1.context_id, + opnum=0xffff, + stub=stub_bin, + 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.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, ctx1.context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, dcerpc.DCERPC_NCA_S_OP_RNG_ERROR) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + + stub_bin = b'\x00' * 8 + mod_len = len(stub_bin) % dcerpc.DCERPC_AUTH_PAD_ALIGNMENT + auth_pad_length = 0 + if mod_len > 0: + auth_pad_length = dcerpc.DCERPC_AUTH_PAD_ALIGNMENT - mod_len + stub_bin += b'\x00' * auth_pad_length + + sig_size = g.sig_size(len(stub_bin)) + zero_sig = b"\x00" * sig_size + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_pad_length=auth_pad_length, + auth_context_id=auth_context_id, + auth_blob=zero_sig) + req = self.generate_request(call_id=5, + context_id=ctx1.context_id, + opnum=1, + stub=stub_bin, + auth_info=auth_info) + req_blob = samba.ndr.ndr_pack(req) + ofs_stub = dcerpc.DCERPC_REQUEST_LENGTH + ofs_sig = len(req_blob) - req.auth_length + ofs_trailer = ofs_sig - dcerpc.DCERPC_AUTH_TRAILER_LENGTH + req_data = req_blob[ofs_stub:ofs_trailer] + req_whole = req_blob[0:ofs_sig] + sig = g.sign_packet(req_data, req_whole) + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_pad_length=auth_pad_length, + auth_context_id=auth_context_id, + auth_blob=sig) + req = self.generate_request(call_id=5, + context_id=ctx1.context_id, + opnum=1, + stub=stub_bin, + auth_info=auth_info) + self.send_pdu(req) + (rep, rep_blob) = self.recv_pdu_raw() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=sig_size) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id & 0xff) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + self.assertEqual(rep.auth_length, sig_size) + + ofs_stub = dcerpc.DCERPC_REQUEST_LENGTH + ofs_sig = rep.frag_length - rep.auth_length + ofs_trailer = ofs_sig - dcerpc.DCERPC_AUTH_TRAILER_LENGTH + rep_data = rep_blob[ofs_stub:ofs_trailer] + rep_whole = rep_blob[0:ofs_sig] + rep_sig = rep_blob[ofs_sig:] + rep_auth_info_blob = rep_blob[ofs_trailer:] + + rep_auth_info = self.parse_auth(rep_auth_info_blob) + self.assertEqual(rep_auth_info.auth_type, auth_type) + self.assertEqual(rep_auth_info.auth_level, auth_level) + self.assertEqual(rep_auth_info.auth_pad_length, 4) + self.assertEqual(rep_auth_info.auth_reserved, 0) + self.assertEqual(rep_auth_info.auth_context_id, auth_context_id) + self.assertEqual(rep_auth_info.credentials, rep_sig) + + g.check_packet(rep_data, rep_whole, rep_sig) + + stub_bin = b'\x00' * 8 + mod_len = len(stub_bin) % dcerpc.DCERPC_AUTH_PAD_ALIGNMENT + auth_pad_length = 0 + if mod_len > 0: + auth_pad_length = dcerpc.DCERPC_AUTH_PAD_ALIGNMENT - mod_len + stub_bin += b'\x00' * auth_pad_length + + sig_size = g.sig_size(len(stub_bin)) + zero_sig = b"\x00" * sig_size + + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_pad_length=auth_pad_length, + auth_context_id=auth_context_id, + auth_blob=zero_sig) + req = self.generate_request(call_id=6, + context_id=ctx1.context_id, + opnum=3, + stub=stub_bin, + auth_info=auth_info) + req_blob = samba.ndr.ndr_pack(req) + ofs_stub = dcerpc.DCERPC_REQUEST_LENGTH + ofs_sig = len(req_blob) - req.auth_length + ofs_trailer = ofs_sig - dcerpc.DCERPC_AUTH_TRAILER_LENGTH + req_data = req_blob[ofs_stub:ofs_trailer] + req_whole = req_blob[0:ofs_sig] + sig = g.sign_packet(req_data, req_whole) + auth_info = self.generate_auth(auth_type=auth_type, + auth_level=auth_level, + auth_pad_length=auth_pad_length, + auth_context_id=auth_context_id, + auth_blob=sig) + req = self.generate_request(call_id=6, + context_id=ctx1.context_id, + opnum=3, + stub=stub_bin, + auth_info=auth_info) + self.send_pdu(req) + (rep, rep_blob) = self.recv_pdu_raw() + self.verify_pdu(rep, dcerpc.DCERPC_PKT_RESPONSE, req.call_id, + auth_length=sig_size) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, req.u.context_id & 0xff) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + self.assertEqual(rep.auth_length, sig_size) + + ofs_stub = dcerpc.DCERPC_REQUEST_LENGTH + ofs_sig = rep.frag_length - rep.auth_length + ofs_trailer = ofs_sig - dcerpc.DCERPC_AUTH_TRAILER_LENGTH + rep_data = rep_blob[ofs_stub:ofs_trailer] + rep_whole = rep_blob[0:ofs_sig] + rep_sig = rep_blob[ofs_sig:] + rep_auth_info_blob = rep_blob[ofs_trailer:] + + rep_auth_info = self.parse_auth(rep_auth_info_blob) + self.assertEqual(rep_auth_info.auth_type, auth_type) + self.assertEqual(rep_auth_info.auth_level, auth_level) + self.assertEqual(rep_auth_info.auth_pad_length, 12) + self.assertEqual(rep_auth_info.auth_reserved, 0) + self.assertEqual(rep_auth_info.auth_context_id, auth_context_id) + self.assertEqual(rep_auth_info.credentials, rep_sig) + + g.check_packet(rep_data, rep_whole, rep_sig) + + def test_spnego_signing_packet(self): + # DCERPC_AUTH_LEVEL_PACKET is handled as alias of + # DCERPC_AUTH_LEVEL_INTEGRITY + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_SPNEGO, + dcerpc.DCERPC_AUTH_LEVEL_PACKET) + + def test_spnego_hdr_signing_packet(self): + # DCERPC_AUTH_LEVEL_PACKET is handled as alias of + # DCERPC_AUTH_LEVEL_INTEGRITY + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_SPNEGO, + dcerpc.DCERPC_AUTH_LEVEL_PACKET, + hdr_sign=True) + + def test_spnego_signing_integrity(self): + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_SPNEGO, + dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY) + + def test_spnego_hdr_signing_integrity(self): + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_SPNEGO, + dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY, + hdr_sign=True) + + def test_ntlm_signing_packet(self): + # DCERPC_AUTH_LEVEL_PACKET is handled as alias of + # DCERPC_AUTH_LEVEL_INTEGRITY + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_NTLMSSP, + dcerpc.DCERPC_AUTH_LEVEL_PACKET) + + def test_ntlm_hdr_signing_packet(self): + # DCERPC_AUTH_LEVEL_PACKET is handled as alias of + # DCERPC_AUTH_LEVEL_INTEGRITY + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_NTLMSSP, + dcerpc.DCERPC_AUTH_LEVEL_PACKET, + hdr_sign=True) + + def test_ntlm_signing_integrity(self): + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_NTLMSSP, + dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY) + + def test_ntlm_hdr_signing_integrity(self): + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_NTLMSSP, + dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY, + hdr_sign=True) + + def test_krb5_signing_packet(self): + # DCERPC_AUTH_LEVEL_PACKET is handled as alias of + # DCERPC_AUTH_LEVEL_INTEGRITY + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_KRB5, + dcerpc.DCERPC_AUTH_LEVEL_PACKET) + + def test_krb5_hdr_signing_packet(self): + # DCERPC_AUTH_LEVEL_PACKET is handled as alias of + # DCERPC_AUTH_LEVEL_INTEGRITY + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_KRB5, + dcerpc.DCERPC_AUTH_LEVEL_PACKET, + hdr_sign=True) + + def test_krb5_signing_integrity(self): + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_KRB5, + dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY) + + def test_krb5_hdr_signing_integrity(self): + return self._test_auth_signing_auth_level_request(dcerpc.DCERPC_AUTH_TYPE_KRB5, + dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY, + hdr_sign=True) + + def test_assoc_group_fail1(self): + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + tsf1_list = [transfer] + ctx = samba.dcerpc.dcerpc.ctx_list() + ctx.context_id = 1 + ctx.num_transfer_syntaxes = len(tsf1_list) + ctx.abstract_syntax = abstract + ctx.transfer_syntaxes = tsf1_list + + ack = self.do_generic_bind(ctx=ctx, assoc_group_id=1, + nak_reason=dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + return + + def test_assoc_group_fail2(self): + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + tsf1_list = [transfer] + ctx = samba.dcerpc.dcerpc.ctx_list() + ctx.context_id = 1 + ctx.num_transfer_syntaxes = len(tsf1_list) + ctx.abstract_syntax = abstract + ctx.transfer_syntaxes = tsf1_list + + ack = self.do_generic_bind(ctx=ctx) + + self._disconnect("test_assoc_group_fail2") + self.assertNotConnected() + time.sleep(0.5) + self.connect() + + ack2 = self.do_generic_bind(ctx=ctx, assoc_group_id=ack.u.assoc_group_id, + nak_reason=dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + return + + def test_assoc_group_diff1(self): + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + (ctx1, ack1) = self.prepare_presentation(abstract, transfer, + context_id=1, return_ack=True) + + conn2 = self.second_connection() + (ctx2, ack2) = conn2.prepare_presentation(abstract, transfer, + context_id=2, return_ack=True) + self.assertNotEqual(ack2.u.assoc_group_id, ack1.u.assoc_group_id) + + conn2._disconnect("End of Test") + return + + def test_assoc_group_ok1(self): + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + (ctx1, ack1) = self.prepare_presentation(abstract, transfer, + context_id=1, return_ack=True) + + conn2 = self.second_connection() + (ctx2, ack2) = conn2.prepare_presentation(abstract, transfer, + assoc_group_id=ack1.u.assoc_group_id, + context_id=2, return_ack=True) + + inq_if_ids = samba.dcerpc.mgmt.inq_if_ids() + self.do_single_request(call_id=1, ctx=ctx1, io=inq_if_ids) + conn2.do_single_request(call_id=1, ctx=ctx2, io=inq_if_ids) + + conn2.do_single_request(call_id=1, ctx=ctx1, io=inq_if_ids, + fault_pfc_flags=( + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE), + fault_status=dcerpc.DCERPC_NCA_S_UNKNOWN_IF, + fault_context_id=0) + + self.do_single_request(call_id=1, ctx=ctx1, io=inq_if_ids) + conn2.do_single_request(call_id=1, ctx=ctx2, io=inq_if_ids) + conn2._disconnect("End of Test") + return + + def test_assoc_group_ok2(self): + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + self.reconnect_smb_pipe(primary_address='\\pipe\\lsarpc', + secondary_address='\\pipe\\lsass', + transport_creds=self.get_user_creds()) + (ctx1, ack1) = self.prepare_presentation(abstract, transfer, + context_id=1, return_ack=True) + + conn2 = self.second_connection() + (ctx2, ack2) = conn2.prepare_presentation(abstract, transfer, + assoc_group_id=ack1.u.assoc_group_id, + context_id=2, return_ack=True) + + inq_if_ids = samba.dcerpc.mgmt.inq_if_ids() + self.do_single_request(call_id=1, ctx=ctx1, io=inq_if_ids) + conn2.do_single_request(call_id=1, ctx=ctx2, io=inq_if_ids) + + conn2.do_single_request(call_id=1, ctx=ctx1, io=inq_if_ids, + fault_pfc_flags=( + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE), + fault_status=dcerpc.DCERPC_NCA_S_UNKNOWN_IF, + fault_context_id=0) + + self.do_single_request(call_id=1, ctx=ctx1, io=inq_if_ids) + conn2.do_single_request(call_id=1, ctx=ctx2, io=inq_if_ids) + conn2._disconnect("End of Test") + return + + def test_assoc_group_fail3(self): + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + (ctx1, ack1) = self.prepare_presentation(abstract, transfer, + context_id=1, return_ack=True) + + # assoc groups are per transport + connF = self.second_connection(primary_address="\\pipe\\lsarpc", + secondary_address="\\pipe\\lsass", + transport_creds=self.get_user_creds()) + tsfF_list = [transfer] + ctxF = samba.dcerpc.dcerpc.ctx_list() + ctxF.context_id = 0xF + ctxF.num_transfer_syntaxes = len(tsfF_list) + ctxF.abstract_syntax = abstract + ctxF.transfer_syntaxes = tsfF_list + ack = connF.do_generic_bind(ctx=ctxF, assoc_group_id=ack1.u.assoc_group_id, + nak_reason=dcerpc.DCERPC_BIND_NAK_REASON_NOT_SPECIFIED) + # wait for a disconnect + rep = connF.recv_pdu() + self.assertIsNone(rep) + connF.assertNotConnected() + + conn2 = self.second_connection() + (ctx2, ack2) = conn2.prepare_presentation(abstract, transfer, + assoc_group_id=ack1.u.assoc_group_id, + context_id=2, return_ack=True) + + inq_if_ids = samba.dcerpc.mgmt.inq_if_ids() + self.do_single_request(call_id=1, ctx=ctx1, io=inq_if_ids) + conn2.do_single_request(call_id=1, ctx=ctx2, io=inq_if_ids) + + conn2.do_single_request(call_id=1, ctx=ctx1, io=inq_if_ids, + fault_pfc_flags=( + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE), + fault_status=dcerpc.DCERPC_NCA_S_UNKNOWN_IF, + fault_context_id=0) + + self.do_single_request(call_id=1, ctx=ctx1, io=inq_if_ids) + conn2.do_single_request(call_id=1, ctx=ctx2, io=inq_if_ids) + conn2._disconnect("End of Test") + return + + def _test_krb5_hdr_sign_delayed1(self, do_upgrade): + auth_type = dcerpc.DCERPC_AUTH_TYPE_KRB5 + auth_level = dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY + auth_context_id = 1 + + creds = self.get_user_creds() + + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + tsf1_list = [transfer] + ctx = samba.dcerpc.dcerpc.ctx_list() + ctx.context_id = 1 + ctx.num_transfer_syntaxes = len(tsf1_list) + ctx.abstract_syntax = abstract + ctx.transfer_syntaxes = tsf1_list + + auth_context = self.get_auth_context_creds(creds=creds, + auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + hdr_signing=False) + + ack = self.do_generic_bind(call_id=1, + ctx=ctx, + auth_context=auth_context) + + inq_if_ids = samba.dcerpc.mgmt.inq_if_ids() + self.do_single_request(call_id=2, ctx=ctx, io=inq_if_ids, + auth_context=auth_context) + + # + # This is just an alter context without authentication + # But it can turn on header signing for the whole connection + # + ack2 = self.do_generic_bind(call_id=3, ctx=ctx, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST | + dcerpc.DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN, + assoc_group_id = ack.u.assoc_group_id, + start_with_alter=True) + + self.assertFalse(auth_context['hdr_signing']) + if do_upgrade: + auth_context['hdr_signing'] = True + auth_context["gensec"].want_feature(gensec.FEATURE_SIGN_PKT_HEADER) + fault_status=None + else: + fault_status=dcerpc.DCERPC_FAULT_SEC_PKG_ERROR + + self.do_single_request(call_id=4, ctx=ctx, io=inq_if_ids, + auth_context=auth_context, + fault_status=fault_status) + + if fault_status is not None: + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + return + + self.do_single_request(call_id=5, ctx=ctx, io=inq_if_ids, + auth_context=auth_context) + return + + def test_krb5_hdr_sign_delayed1_ok1(self): + return self._test_krb5_hdr_sign_delayed1(do_upgrade=True) + + def test_krb5_hdr_sign_delayed1_fail1(self): + return self._test_krb5_hdr_sign_delayed1(do_upgrade=False) + + def _test_krb5_hdr_sign_delayed2(self, do_upgrade): + auth_type = dcerpc.DCERPC_AUTH_TYPE_KRB5 + auth_level = dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY + auth_context_id = 1 + + creds = self.get_user_creds() + + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + tsf1_list = [transfer] + ctx = samba.dcerpc.dcerpc.ctx_list() + ctx.context_id = 1 + ctx.num_transfer_syntaxes = len(tsf1_list) + ctx.abstract_syntax = abstract + ctx.transfer_syntaxes = tsf1_list + + auth_context = self.get_auth_context_creds(creds=creds, + auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + hdr_signing=False) + + # + # SUPPORT_HEADER_SIGN on alter context activates header signing + # + ack = self.do_generic_bind(call_id=1, + ctx=ctx, + auth_context=auth_context, + pfc_flags_2nd=dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST | + dcerpc.DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) + + self.assertFalse(auth_context['hdr_signing']) + if do_upgrade: + auth_context['hdr_signing'] = True + auth_context["gensec"].want_feature(gensec.FEATURE_SIGN_PKT_HEADER) + fault_status=None + else: + fault_status=dcerpc.DCERPC_FAULT_SEC_PKG_ERROR + + inq_if_ids = samba.dcerpc.mgmt.inq_if_ids() + self.do_single_request(call_id=4, ctx=ctx, io=inq_if_ids, + auth_context=auth_context, + fault_status=fault_status) + + if fault_status is not None: + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + return + + self.do_single_request(call_id=5, ctx=ctx, io=inq_if_ids, + auth_context=auth_context) + return + + def test_krb5_hdr_sign_delayed2_ok1(self): + return self._test_krb5_hdr_sign_delayed2(do_upgrade=True) + + def test_krb5_hdr_sign_delayed2_fail1(self): + return self._test_krb5_hdr_sign_delayed2(do_upgrade=False) + + def test_krb5_hdr_sign_delayed3_fail1(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_KRB5 + auth_level = dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY + auth_context_id = 1 + + creds = self.get_user_creds() + + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + tsf1_list = [transfer] + ctx = samba.dcerpc.dcerpc.ctx_list() + ctx.context_id = 1 + ctx.num_transfer_syntaxes = len(tsf1_list) + ctx.abstract_syntax = abstract + ctx.transfer_syntaxes = tsf1_list + + auth_context = self.get_auth_context_creds(creds=creds, + auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + hdr_signing=False) + + # + # SUPPORT_HEADER_SIGN on auth3 doesn't activate header signing + # + ack = self.do_generic_bind(call_id=1, + ctx=ctx, + auth_context=auth_context, + pfc_flags_2nd=dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST | + dcerpc.DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN, + use_auth3=True) + + inq_if_ids = samba.dcerpc.mgmt.inq_if_ids() + self.do_single_request(call_id=2, ctx=ctx, io=inq_if_ids, + auth_context=auth_context) + + self.assertFalse(auth_context['hdr_signing']) + auth_context['hdr_signing'] = True + auth_context["gensec"].want_feature(gensec.FEATURE_SIGN_PKT_HEADER) + fault_status=dcerpc.DCERPC_FAULT_SEC_PKG_ERROR + + self.do_single_request(call_id=4, ctx=ctx, io=inq_if_ids, + auth_context=auth_context, + fault_status=fault_status) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + return + + def _test_lsa_multi_auth_connect1(self, smb_creds, + account_name0, authority_name0): + creds1 = self.get_anon_creds() + account_name1 = "ANONYMOUS LOGON" + authority_name1 = "NT AUTHORITY" + auth_type1 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level1 = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id1 = 1 + + creds2 = self.get_user_creds() + account_name2 = creds2.get_username() + authority_name2 = creds2.get_domain() + auth_type2 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level2 = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id2 = 2 + + abstract = samba.dcerpc.lsa.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + self.reconnect_smb_pipe(primary_address='\\pipe\\lsarpc', + secondary_address='\\pipe\\lsass', + transport_creds=smb_creds) + self.assertIsConnected() + + tsf1_list = [transfer] + ctx1 = samba.dcerpc.dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = abstract + ctx1.transfer_syntaxes = tsf1_list + + auth_context1 = self.get_auth_context_creds(creds=creds1, + auth_type=auth_type1, + auth_level=auth_level1, + auth_context_id=auth_context_id1, + hdr_signing=False) + auth_context2 = self.get_auth_context_creds(creds=creds2, + auth_type=auth_type2, + auth_level=auth_level2, + auth_context_id=auth_context_id2, + hdr_signing=False) + + get_user_name = samba.dcerpc.lsa.GetUserName() + get_user_name.in_system_name = self.target_hostname + get_user_name.in_account_name = None + get_user_name.in_authority_name = base.ndr_pointer(None) + + ack1 = self.do_generic_bind(call_id=0, + ctx=ctx1, + auth_context=auth_context1) + + # + # With just one explicit auth context and that + # uses AUTH_LEVEL_CONNECT context. + # + # We always get that by default instead of the one default one + # inherited from the transport + # + self.do_single_request(call_id=1, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=2, ctx=ctx1, io=get_user_name, + auth_context=auth_context1) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + ack2 = self.do_generic_bind(call_id=3, + ctx=ctx1, + auth_context=auth_context2, + assoc_group_id = ack1.u.assoc_group_id, + start_with_alter=True) + + # + # Now we have two explicit auth contexts + # + # If we don't specify one of them we get the default one + # inherited from the transport + # + self.do_single_request(call_id=4, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + self.do_single_request(call_id=5, ctx=ctx1, io=get_user_name, + auth_context=auth_context1) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=6, ctx=ctx1, io=get_user_name, + auth_context=auth_context2) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name2) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name2) + + self.do_single_request(call_id=7, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + return + + def test_lsa_multi_auth_connect1u(self): + smb_auth_creds = self.get_user_creds() + account_name0 = smb_auth_creds.get_username() + authority_name0 = smb_auth_creds.get_domain() + return self._test_lsa_multi_auth_connect1(smb_auth_creds, + account_name0, + authority_name0) + + def test_lsa_multi_auth_connect1a(self): + smb_auth_creds = self.get_anon_creds() + account_name0 = "ANONYMOUS LOGON" + authority_name0 = "NT AUTHORITY" + return self._test_lsa_multi_auth_connect1(smb_auth_creds, + account_name0, + authority_name0) + + def _test_lsa_multi_auth_connect2(self, smb_creds, + account_name0, authority_name0): + creds1 = self.get_anon_creds() + account_name1 = "ANONYMOUS LOGON" + authority_name1 = "NT AUTHORITY" + auth_type1 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level1 = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id1 = 1 + + creds2 = self.get_user_creds() + account_name2 = creds2.get_username() + authority_name2 = creds2.get_domain() + auth_type2 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level2 = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id2 = 2 + + abstract = samba.dcerpc.lsa.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + self.reconnect_smb_pipe(primary_address='\\pipe\\lsarpc', + secondary_address='\\pipe\\lsass', + transport_creds=smb_creds) + self.assertIsConnected() + + tsf1_list = [transfer] + ctx1 = samba.dcerpc.dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = abstract + ctx1.transfer_syntaxes = tsf1_list + + auth_context1 = self.get_auth_context_creds(creds=creds1, + auth_type=auth_type1, + auth_level=auth_level1, + auth_context_id=auth_context_id1, + hdr_signing=False) + auth_context2 = self.get_auth_context_creds(creds=creds2, + auth_type=auth_type2, + auth_level=auth_level2, + auth_context_id=auth_context_id2, + hdr_signing=False) + + get_user_name = samba.dcerpc.lsa.GetUserName() + get_user_name.in_system_name = self.target_hostname + get_user_name.in_account_name = None + get_user_name.in_authority_name = base.ndr_pointer(None) + + ack0 = self.do_generic_bind(call_id=0, ctx=ctx1) + + # + # We use the default auth context + # inherited from the transport + # + self.do_single_request(call_id=1, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + ack1 = self.do_generic_bind(call_id=2, + ctx=ctx1, + auth_context=auth_context1, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + + # + # With just one explicit auth context and that + # uses AUTH_LEVEL_CONNECT context. + # + # We always get that by default instead of the one default one + # inherited from the transport + # + self.do_single_request(call_id=3, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=4, ctx=ctx1, io=get_user_name, + auth_context=auth_context1) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + ack2 = self.do_generic_bind(call_id=5, + ctx=ctx1, + auth_context=auth_context2, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + + # + # Now we have two explicit auth contexts + # + # If we don't specify one of them we get the default one + # inherited from the transport (again) + # + self.do_single_request(call_id=6, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + self.do_single_request(call_id=7, ctx=ctx1, io=get_user_name, + auth_context=auth_context1) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=8, ctx=ctx1, io=get_user_name, + auth_context=auth_context2) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name2) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name2) + + self.do_single_request(call_id=9, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + return + + def test_lsa_multi_auth_connect2u(self): + smb_auth_creds = self.get_user_creds() + account_name0 = smb_auth_creds.get_username() + authority_name0 = smb_auth_creds.get_domain() + return self._test_lsa_multi_auth_connect2(smb_auth_creds, + account_name0, + authority_name0) + + def test_lsa_multi_auth_connect2a(self): + smb_auth_creds = self.get_anon_creds() + account_name0 = "ANONYMOUS LOGON" + authority_name0 = "NT AUTHORITY" + return self._test_lsa_multi_auth_connect2(smb_auth_creds, + account_name0, + authority_name0) + + def _test_lsa_multi_auth_connect3(self, smb_creds, + account_name0, authority_name0): + creds1 = self.get_anon_creds() + account_name1 = "ANONYMOUS LOGON" + authority_name1 = "NT AUTHORITY" + auth_type1 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level1 = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id1 = 1 + + creds2 = self.get_user_creds() + account_name2 = creds2.get_username() + authority_name2 = creds2.get_domain() + auth_type2 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level2 = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id2 = 2 + + abstract = samba.dcerpc.lsa.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + self.reconnect_smb_pipe(primary_address='\\pipe\\lsarpc', + secondary_address='\\pipe\\lsass', + transport_creds=smb_creds) + self.assertIsConnected() + + tsf1_list = [transfer] + ctx1 = samba.dcerpc.dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = abstract + ctx1.transfer_syntaxes = tsf1_list + + auth_context1 = self.get_auth_context_creds(creds=creds1, + auth_type=auth_type1, + auth_level=auth_level1, + auth_context_id=auth_context_id1, + hdr_signing=False) + auth_context2 = self.get_auth_context_creds(creds=creds2, + auth_type=auth_type2, + auth_level=auth_level2, + auth_context_id=auth_context_id2, + hdr_signing=False) + + get_user_name = samba.dcerpc.lsa.GetUserName() + get_user_name.in_system_name = self.target_hostname + get_user_name.in_account_name = None + get_user_name.in_authority_name = base.ndr_pointer(None) + + ack0 = self.do_generic_bind(call_id=0, ctx=ctx1) + + # + # We use the default auth context + # inherited from the transport + # + self.do_single_request(call_id=1, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + ack1 = self.do_generic_bind(call_id=2, + ctx=ctx1, + auth_context=auth_context1, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + + # + # With just one explicit auth context and that + # uses AUTH_LEVEL_CONNECT context. + # + # We always get that by default instead of the one default one + # inherited from the transport + # + # Until an explicit usage resets that mode + # + self.do_single_request(call_id=3, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=4, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=5, ctx=ctx1, io=get_user_name, + auth_context=auth_context1) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=6, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + ack2 = self.do_generic_bind(call_id=7, + ctx=ctx1, + auth_context=auth_context2, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + # + # A new auth context won't change that mode again. + # + self.do_single_request(call_id=8, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + self.do_single_request(call_id=9, ctx=ctx1, io=get_user_name, + auth_context=auth_context1) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=10, ctx=ctx1, io=get_user_name, + auth_context=auth_context2) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name2) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name2) + + self.do_single_request(call_id=11, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + return + + def test_lsa_multi_auth_connect3u(self): + smb_auth_creds = self.get_user_creds() + account_name0 = smb_auth_creds.get_username() + authority_name0 = smb_auth_creds.get_domain() + return self._test_lsa_multi_auth_connect3(smb_auth_creds, + account_name0, + authority_name0) + + def test_lsa_multi_auth_connect3a(self): + smb_auth_creds = self.get_anon_creds() + account_name0 = "ANONYMOUS LOGON" + authority_name0 = "NT AUTHORITY" + return self._test_lsa_multi_auth_connect3(smb_auth_creds, + account_name0, + authority_name0) + + def _test_lsa_multi_auth_connect4(self, smb_creds, + account_name0, authority_name0): + creds1 = self.get_anon_creds() + account_name1 = "ANONYMOUS LOGON" + authority_name1 = "NT AUTHORITY" + auth_type1 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level1 = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id1 = 1 + + creds2 = self.get_user_creds() + account_name2 = creds2.get_username() + authority_name2 = creds2.get_domain() + auth_type2 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level2 = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id2 = 2 + + creds3 = self.get_anon_creds() + account_name3 = "ANONYMOUS LOGON" + authority_name3 = "NT AUTHORITY" + auth_type3 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level3 = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id3 = 3 + + creds4 = self.get_user_creds() + account_name4 = creds4.get_username() + authority_name4 = creds4.get_domain() + auth_type4 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level4 = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id4 = 4 + + abstract = samba.dcerpc.lsa.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + self.reconnect_smb_pipe(primary_address='\\pipe\\lsarpc', + secondary_address='\\pipe\\lsass', + transport_creds=smb_creds) + self.assertIsConnected() + + tsf1_list = [transfer] + ctx1 = samba.dcerpc.dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = abstract + ctx1.transfer_syntaxes = tsf1_list + + auth_context1 = self.get_auth_context_creds(creds=creds1, + auth_type=auth_type1, + auth_level=auth_level1, + auth_context_id=auth_context_id1, + hdr_signing=False) + auth_context2 = self.get_auth_context_creds(creds=creds2, + auth_type=auth_type2, + auth_level=auth_level2, + auth_context_id=auth_context_id2, + hdr_signing=False) + auth_context3 = self.get_auth_context_creds(creds=creds3, + auth_type=auth_type3, + auth_level=auth_level3, + auth_context_id=auth_context_id3, + hdr_signing=False) + auth_context4 = self.get_auth_context_creds(creds=creds4, + auth_type=auth_type4, + auth_level=auth_level4, + auth_context_id=auth_context_id4, + hdr_signing=False) + + get_user_name = samba.dcerpc.lsa.GetUserName() + get_user_name.in_system_name = self.target_hostname + get_user_name.in_account_name = None + get_user_name.in_authority_name = base.ndr_pointer(None) + + ack0 = self.do_generic_bind(call_id=0, ctx=ctx1) + + # + # We use the default auth context + # inherited from the transport + # + self.do_single_request(call_id=1, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + ack1 = self.do_generic_bind(call_id=2, + ctx=ctx1, + auth_context=auth_context1, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + + # + # With just one explicit auth context and that + # uses AUTH_LEVEL_CONNECT context. + # + # We always get that by default instead of the one default one + # inherited from the transport + # + # Until a new explicit context resets the mode + # + self.do_single_request(call_id=3, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=4, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + ack2 = self.do_generic_bind(call_id=5, + ctx=ctx1, + auth_context=auth_context2, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + + # + # A new auth context with LEVEL_CONNECT resets the default. + # + self.do_single_request(call_id=6, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name2) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name2) + + self.do_single_request(call_id=7, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name2) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name2) + + ack3 = self.do_generic_bind(call_id=8, + ctx=ctx1, + auth_context=auth_context3, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + + # + # A new auth context with LEVEL_CONNECT resets the default. + # + self.do_single_request(call_id=9, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name3) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name3) + + self.do_single_request(call_id=10, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name3) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name3) + + ack4 = self.do_generic_bind(call_id=11, + ctx=ctx1, + auth_context=auth_context4, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + + # + # A new auth context with LEVEL_CONNECT resets the default. + # + self.do_single_request(call_id=12, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name4) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name4) + + self.do_single_request(call_id=13, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name4) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name4) + + # + # Only the explicit usage of any context reset that mode + # + self.do_single_request(call_id=14, ctx=ctx1, io=get_user_name, + auth_context=auth_context1) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=15, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + self.do_single_request(call_id=16, ctx=ctx1, io=get_user_name, + auth_context=auth_context1) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=17, ctx=ctx1, io=get_user_name, + auth_context=auth_context2) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name2) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name2) + + self.do_single_request(call_id=18, ctx=ctx1, io=get_user_name, + auth_context=auth_context3) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name3) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name3) + + self.do_single_request(call_id=19, ctx=ctx1, io=get_user_name, + auth_context=auth_context4) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name4) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name4) + + self.do_single_request(call_id=20, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + return + + def test_lsa_multi_auth_connect4u(self): + smb_auth_creds = self.get_user_creds() + account_name0 = smb_auth_creds.get_username() + authority_name0 = smb_auth_creds.get_domain() + return self._test_lsa_multi_auth_connect4(smb_auth_creds, + account_name0, + authority_name0) + + def test_lsa_multi_auth_connect4a(self): + smb_auth_creds = self.get_anon_creds() + account_name0 = "ANONYMOUS LOGON" + authority_name0 = "NT AUTHORITY" + return self._test_lsa_multi_auth_connect4(smb_auth_creds, + account_name0, + authority_name0) + + def _test_lsa_multi_auth_sign_connect1(self, smb_creds, + account_name0, authority_name0): + + creds1 = self.get_user_creds() + account_name1 = creds1.get_username() + authority_name1 = creds1.get_domain() + auth_type1 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level1 = dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY + auth_context_id1 = 1 + + creds2 = self.get_user_creds() + account_name2 = creds2.get_username() + authority_name2 = creds2.get_domain() + auth_type2 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level2 = dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY + auth_context_id2 = 2 + + creds3 = self.get_anon_creds() + account_name3 = "ANONYMOUS LOGON" + authority_name3 = "NT AUTHORITY" + auth_type3 = dcerpc.DCERPC_AUTH_TYPE_NTLMSSP + auth_level3 = dcerpc.DCERPC_AUTH_LEVEL_CONNECT + auth_context_id3 = 3 + + abstract = samba.dcerpc.lsa.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + self.reconnect_smb_pipe(primary_address='\\pipe\\lsarpc', + secondary_address='\\pipe\\lsass', + transport_creds=smb_creds) + self.assertIsConnected() + + tsf1_list = [transfer] + ctx1 = samba.dcerpc.dcerpc.ctx_list() + ctx1.context_id = 1 + ctx1.num_transfer_syntaxes = len(tsf1_list) + ctx1.abstract_syntax = abstract + ctx1.transfer_syntaxes = tsf1_list + + auth_context1 = self.get_auth_context_creds(creds=creds1, + auth_type=auth_type1, + auth_level=auth_level1, + auth_context_id=auth_context_id1, + hdr_signing=False) + auth_context2 = self.get_auth_context_creds(creds=creds2, + auth_type=auth_type2, + auth_level=auth_level2, + auth_context_id=auth_context_id2, + hdr_signing=False) + auth_context3 = self.get_auth_context_creds(creds=creds3, + auth_type=auth_type3, + auth_level=auth_level3, + auth_context_id=auth_context_id3, + hdr_signing=False) + + get_user_name = samba.dcerpc.lsa.GetUserName() + get_user_name.in_system_name = self.target_hostname + get_user_name.in_account_name = None + get_user_name.in_authority_name = base.ndr_pointer(None) + + ack1 = self.do_generic_bind(call_id=0, + ctx=ctx1, + auth_context=auth_context1) + + # + # With just one explicit auth context and that + # *not* uses AUTH_LEVEL_CONNECT context. + # + # We don't get the by default (auth_context1) + # + self.do_single_request(call_id=1, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + self.do_single_request(call_id=2, ctx=ctx1, io=get_user_name, + auth_context=auth_context1) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=3, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + ack2 = self.do_generic_bind(call_id=4, + ctx=ctx1, + auth_context=auth_context2, + assoc_group_id = ack1.u.assoc_group_id, + start_with_alter=True) + + # + # With just two explicit auth context and + # *none* uses AUTH_LEVEL_CONNECT context. + # + # We don't get auth_context1 or auth_context2 by default + # + self.do_single_request(call_id=5, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + self.do_single_request(call_id=6, ctx=ctx1, io=get_user_name, + auth_context=auth_context1) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=7, ctx=ctx1, io=get_user_name, + auth_context=auth_context2) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name2) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name2) + + self.do_single_request(call_id=8, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + ack3 = self.do_generic_bind(call_id=9, + ctx=ctx1, + auth_context=auth_context3, + assoc_group_id = ack1.u.assoc_group_id, + start_with_alter=True) + + # + # Now we have tree explicit auth contexts, + # but just one with AUTH_LEVEL_CONNECT + # + # If we don't specify one of them we get + # that one auth_level_connect context. + # + # Until an explicit usage of any auth context reset that mode. + # + self.do_single_request(call_id=10, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name3) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name3) + + self.do_single_request(call_id=11, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name3) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name3) + + self.do_single_request(call_id=12, ctx=ctx1, io=get_user_name, + auth_context=auth_context1) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name1) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name1) + + self.do_single_request(call_id=13, ctx=ctx1, io=get_user_name) + self.assertEqual(get_user_name.result[0], NT_STATUS_SUCCESS) + self.assertEqualsStrLower(get_user_name.out_account_name, account_name0) + self.assertEqualsStrLower(get_user_name.out_authority_name.value, authority_name0) + + return + + def test_lsa_multi_auth_sign_connect1u(self): + smb_auth_creds = self.get_user_creds() + account_name0 = smb_auth_creds.get_username() + authority_name0 = smb_auth_creds.get_domain() + return self._test_lsa_multi_auth_sign_connect1(smb_auth_creds, + account_name0, + authority_name0) + def test_lsa_multi_auth_sign_connect1a(self): + smb_auth_creds = self.get_anon_creds() + account_name0 = "ANONYMOUS LOGON" + authority_name0 = "NT AUTHORITY" + return self._test_lsa_multi_auth_sign_connect1(smb_auth_creds, + account_name0, + authority_name0) + + def test_spnego_multiple_auth_hdr_signing(self): + auth_type = dcerpc.DCERPC_AUTH_TYPE_SPNEGO + auth_level1 = dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY + auth_context_id1=1 + auth_level2 = dcerpc.DCERPC_AUTH_LEVEL_PACKET + auth_context_id2=2 + + creds = self.get_user_creds() + + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + tsf1_list = [transfer] + ctx = samba.dcerpc.dcerpc.ctx_list() + ctx.context_id = 1 + ctx.num_transfer_syntaxes = len(tsf1_list) + ctx.abstract_syntax = abstract + ctx.transfer_syntaxes = tsf1_list + + auth_context1 = self.get_auth_context_creds(creds=creds, + auth_type=auth_type, + auth_level=auth_level1, + auth_context_id=auth_context_id1, + hdr_signing=False) + auth_context2 = self.get_auth_context_creds(creds=creds, + auth_type=auth_type, + auth_level=auth_level2, + auth_context_id=auth_context_id2, + hdr_signing=False) + + ack0 = self.do_generic_bind(call_id=1, ctx=ctx) + + ack1 = self.do_generic_bind(call_id=2, + ctx=ctx, + auth_context=auth_context1, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + ack2 = self.do_generic_bind(call_id=3, + ctx=ctx, + auth_context=auth_context2, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + + inq_if_ids = samba.dcerpc.mgmt.inq_if_ids() + self.do_single_request(call_id=4, ctx=ctx, io=inq_if_ids) + self.do_single_request(call_id=5, ctx=ctx, io=inq_if_ids, + auth_context=auth_context1) + self.do_single_request(call_id=6, ctx=ctx, io=inq_if_ids, + auth_context=auth_context2) + + ack3 = self.do_generic_bind(call_id=7, ctx=ctx, + pfc_flags=dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST | + dcerpc.DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN, + assoc_group_id = ack0.u.assoc_group_id, + start_with_alter=True) + + self.assertFalse(auth_context1['hdr_signing']) + auth_context1['hdr_signing'] = True + auth_context1["gensec"].want_feature(gensec.FEATURE_SIGN_PKT_HEADER) + + self.do_single_request(call_id=8, ctx=ctx, io=inq_if_ids) + self.do_single_request(call_id=9, ctx=ctx, io=inq_if_ids, + auth_context=auth_context1) + self.do_single_request(call_id=10, ctx=ctx, io=inq_if_ids, + auth_context=auth_context2, + fault_status=dcerpc.DCERPC_FAULT_SEC_PKG_ERROR) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + + def test_multiple_auth_limit(self): + creds = self.get_user_creds() + + abstract = samba.dcerpc.mgmt.abstract_syntax() + transfer = base.transfer_syntax_ndr() + + tsf1_list = [transfer] + ctx = samba.dcerpc.dcerpc.ctx_list() + ctx.context_id = 1 + ctx.num_transfer_syntaxes = len(tsf1_list) + ctx.abstract_syntax = abstract + ctx.transfer_syntaxes = tsf1_list + + ack0 = self.do_generic_bind(call_id=0, ctx=ctx) + + is_server_listening = samba.dcerpc.mgmt.is_server_listening() + + max_num_auth_str = samba.tests.env_get_var_value('MAX_NUM_AUTH', allow_missing=True) + if max_num_auth_str is not None: + max_num_auth = int(max_num_auth_str) + else: + max_num_auth = 2049 + + for i in range(1, max_num_auth+2): + auth_type = dcerpc.DCERPC_AUTH_TYPE_SPNEGO + auth_level = dcerpc.DCERPC_AUTH_LEVEL_INTEGRITY + auth_context_id = i + + auth_context = self.get_auth_context_creds(creds=creds, + auth_type=auth_type, + auth_level=auth_level, + auth_context_id=auth_context_id, + hdr_signing=False) + + alter_fault = None + if i > max_num_auth: + alter_fault = dcerpc.DCERPC_NCA_S_PROTO_ERROR + + ack = self.do_generic_bind(call_id=auth_context_id, + ctx=ctx, + auth_context=auth_context, + assoc_group_id = ack0.u.assoc_group_id, + alter_fault=alter_fault, + start_with_alter=True, + ) + if alter_fault is not None: + break + + + self.do_single_request(call_id=auth_context_id, + ctx=ctx, io=is_server_listening, + auth_context=auth_context) + + # wait for a disconnect + rep = self.recv_pdu() + self.assertIsNone(rep) + self.assertNotConnected() + return + + +if __name__ == "__main__": + global_ndr_print = True + global_hexdump = True + import unittest + unittest.main() diff --git a/python/samba/tests/dcerpc/raw_testcase.py b/python/samba/tests/dcerpc/raw_testcase.py new file mode 100644 index 0000000..743fa0f --- /dev/null +++ b/python/samba/tests/dcerpc/raw_testcase.py @@ -0,0 +1,1177 @@ +# Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010 +# Copyright (C) Stefan Metzmacher 2014,2015 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import sys +import socket +import samba.dcerpc.dcerpc as dcerpc +import samba.dcerpc.base +import samba.dcerpc.epmapper +import samba.dcerpc.security as security +import samba.tests +from samba import gensec +from samba.credentials import Credentials +from samba.tests import TestCase +from samba.ndr import ndr_pack, ndr_unpack, ndr_unpack_out +from samba.ntstatus import ( + NT_STATUS_CONNECTION_DISCONNECTED, + NT_STATUS_PIPE_DISCONNECTED, + NT_STATUS_IO_TIMEOUT +) +from samba import NTSTATUSError +from samba.samba3 import param as s3param +from samba.samba3 import libsmb_samba_internal as libsmb +from samba.credentials import SMB_SIGNING_REQUIRED + +class smb_pipe_socket(object): + + def __init__(self, target_hostname, pipename, creds, impersonation_level, lp): + lp3 = s3param.get_context() + lp3.load(lp.configfile) + saved_signing_state = creds.get_smb_ipc_signing() + creds.set_smb_ipc_signing(SMB_SIGNING_REQUIRED) + self.smbconn = libsmb.Conn(target_hostname, 'IPC$', lp3, + creds=creds, ipc=True) + creds.set_smb_ipc_signing(saved_signing_state) + self.smbfid = self.smbconn.create(pipename, + DesiredAccess=0x12019f, + ShareAccess=0x7, + CreateDisposition=1, + CreateOptions=0x400040, + ImpersonationLevel=impersonation_level) + return + + def close(self): + self.smbconn.close(self.smbfid) + del self.smbconn + + def settimeout(self, timeo): + # The socket module we simulate there + # specifies the timeo as seconds as float. + msecs = int(timeo * 1000) + assert msecs >= 0 + self.smbconn.settimeout(msecs) + return + + def send(self, buf, flags=0): + return self.smbconn.write(self.smbfid, buffer=buf, offset=0, mode=8) + + def recv(self, len, flags=0): + try: + return self.smbconn.read(self.smbfid, offset=0, size=len) + except NTSTATUSError as e: + if e.args[0] == NT_STATUS_CONNECTION_DISCONNECTED: + return b'\0' * 0 + if e.args[0] == NT_STATUS_PIPE_DISCONNECTED: + return b'\0' * 0 + if e.args[0] == NT_STATUS_IO_TIMEOUT: + raise socket.timeout(str(e)) + raise e + +class RawDCERPCTest(TestCase): + """A raw DCE/RPC Test case.""" + + def _disconnect(self, reason): + if self.s is None: + return + self.s.close() + self.s = None + if self.do_hexdump: + sys.stderr.write("disconnect[%s]\n" % reason) + + def _connect_tcp(self): + tcp_port = int(self.primary_address) + try: + self.a = socket.getaddrinfo(self.host, tcp_port, socket.AF_UNSPEC, + socket.SOCK_STREAM, socket.SOL_TCP, + 0) + self.s = socket.socket(self.a[0][0], self.a[0][1], self.a[0][2]) + self.s.settimeout(10) + self.s.connect(self.a[0][4]) + except socket.error as e: + self.s.close() + raise + except IOError as e: + self.s.close() + raise + except Exception as e: + raise + finally: + pass + self.max_xmit_frag = 5840 + self.max_recv_frag = 5840 + if self.secondary_address is None: + self.secondary_address = self.primary_address + # compat for older tests + self.tcp_port = tcp_port + + def _connect_smb(self): + a = self.primary_address.split('\\') + self.assertEqual(len(a), 3) + self.assertEqual(a[0], "") + self.assertEqual(a[1], "pipe") + pipename = a[2] + self.s = smb_pipe_socket(self.target_hostname, + pipename, + self.transport_creds, + self.transport_impersonation, + self.lp_ctx) + self.max_xmit_frag = 4280 + self.max_recv_frag = 4280 + if self.secondary_address is None: + self.secondary_address = self.primary_address + + def connect(self): + self.assertNotConnected() + if self.primary_address.startswith("\\pipe\\"): + self._connect_smb() + else: + self._connect_tcp() + if self.secondary_address is None: + self.secondary_address = self.primary_address + return + + def setUp(self): + super().setUp() + self.do_ndr_print = False + self.do_hexdump = False + + self.ignore_random_pad = samba.tests.env_get_var_value('IGNORE_RANDOM_PAD', + allow_missing=True) + self.host = samba.tests.env_get_var_value('SERVER') + self.target_hostname = samba.tests.env_get_var_value('TARGET_HOSTNAME', allow_missing=True) + if self.target_hostname is None: + self.target_hostname = self.host + self.primary_address = "135" + self.secondary_address = None + self.transport_creds = self.get_anon_creds() + self.transport_impersonation = 0x2 + + self.settings = {} + self.settings["lp_ctx"] = self.lp_ctx = samba.tests.env_loadparm() + self.settings["target_hostname"] = self.target_hostname + + self.s = None + self.connect() + + def tearDown(self): + self._disconnect("tearDown") + super().tearDown() + + def noop(self): + return + + def reconnect_smb_pipe(self, primary_address, secondary_address=None, + transport_creds=None, transport_impersonation=None): + self._disconnect("reconnect_smb_pipe") + self.assertIsNotNone(primary_address) + self.primary_address = primary_address + if secondary_address is not None: + self.secondary_address = secondary_address + else: + self.secondary_address = None + + if transport_creds is not None: + self.transport_creds = transport_creds + + if transport_impersonation is not None: + self.transport_impersonation = transport_impersonation + + self.connect() + return + + def second_connection(self, primary_address=None, secondary_address=None, + transport_creds=None, transport_impersonation=None): + c = RawDCERPCTest(methodName='noop') + c.do_ndr_print = self.do_ndr_print + c.do_hexdump = self.do_hexdump + c.ignore_random_pad = self.ignore_random_pad + + c.host = self.host + c.target_hostname = self.target_hostname + if primary_address is not None: + c.primary_address = primary_address + if secondary_address is not None: + c.secondary_address = secondary_address + else: + c.secondary_address = None + else: + self.assertIsNone(secondary_address) + c.primary_address = self.primary_address + c.secondary_address = self.secondary_address + + if transport_creds is not None: + c.transport_creds = transport_creds + else: + c.transport_creds = self.transport_creds + + if transport_impersonation is not None: + c.transport_impersonation = transport_impersonation + else: + c.transport_impersonation = self.transport_impersonation + + c.lp_ctx = self.lp_ctx + c.settings = self.settings + + c.s = None + c.connect() + return c + + def get_user_creds(self): + c = Credentials() + c.guess() + domain = samba.tests.env_get_var_value('DOMAIN') + realm = samba.tests.env_get_var_value('REALM') + username = samba.tests.env_get_var_value('USERNAME') + password = samba.tests.env_get_var_value('PASSWORD') + c.set_domain(domain) + c.set_realm(realm) + c.set_username(username) + c.set_password(password) + return c + + def get_anon_creds(self): + c = Credentials() + c.set_anonymous() + return c + + def get_auth_context_creds(self, creds, auth_type, auth_level, + auth_context_id, + g_auth_level=None, + hdr_signing=False): + + if g_auth_level is None: + g_auth_level = auth_level + + g = gensec.Security.start_client(self.settings) + g.set_credentials(creds) + g.want_feature(gensec.FEATURE_DCE_STYLE) + g.start_mech_by_authtype(auth_type, g_auth_level) + + if auth_type == dcerpc.DCERPC_AUTH_TYPE_KRB5: + expect_3legs = True + elif auth_type == dcerpc.DCERPC_AUTH_TYPE_NTLMSSP: + expect_3legs = True + else: + expect_3legs = False + + auth_context = {} + auth_context["auth_type"] = auth_type + auth_context["auth_level"] = auth_level + auth_context["auth_context_id"] = auth_context_id + auth_context["g_auth_level"] = g_auth_level + auth_context["gensec"] = g + auth_context["hdr_signing"] = hdr_signing + auth_context["expect_3legs"] = expect_3legs + + return auth_context + + def do_generic_bind(self, ctx, auth_context=None, + pfc_flags=samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST, + assoc_group_id=0, call_id=0, + nak_reason=None, alter_fault=None, + start_with_alter=False, + pfc_flags_2nd=samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST, + use_auth3=False): + ctx_list = [ctx] + + if auth_context is not None: + if auth_context['hdr_signing']: + pfc_flags |= dcerpc.DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN + + expect_3legs = auth_context["expect_3legs"] + + from_server = b"" + (finished, to_server) = auth_context["gensec"].update(from_server) + self.assertFalse(finished) + + auth_info = self.generate_auth(auth_type=auth_context["auth_type"], + auth_level=auth_context["auth_level"], + auth_context_id=auth_context["auth_context_id"], + auth_blob=to_server) + else: + auth_info = b"" + + if start_with_alter: + req = self.generate_alter(call_id=call_id, + pfc_flags=pfc_flags, + ctx_list=ctx_list, + assoc_group_id=0xffffffff - assoc_group_id, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + if alter_fault is not None: + self.verify_pdu(rep, samba.dcerpc.dcerpc.DCERPC_PKT_FAULT, req.call_id, + pfc_flags=req.pfc_flags | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, alter_fault) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + return None + self.verify_pdu(rep, samba.dcerpc.dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + pfc_flags=req.pfc_flags) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertEqual(rep.u.assoc_group_id, assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertEqual(rep.u.secondary_address, '') + self.assertPadding(rep.u._pad1, 2) + else: + req = self.generate_bind(call_id=call_id, + pfc_flags=pfc_flags, + ctx_list=ctx_list, + assoc_group_id=assoc_group_id, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + if nak_reason is not None: + self.verify_pdu(rep, samba.dcerpc.dcerpc.DCERPC_PKT_BIND_NAK, req.call_id, + auth_length=0) + self.assertEqual(rep.u.reject_reason, nak_reason) + self.assertEqual(rep.u.num_versions, 1) + self.assertEqual(rep.u.versions[0].rpc_vers, req.rpc_vers) + self.assertEqual(rep.u.versions[0].rpc_vers_minor, req.rpc_vers_minor) + self.assertPadding(rep.u._pad, 3) + return + self.verify_pdu(rep, samba.dcerpc.dcerpc.DCERPC_PKT_BIND_ACK, req.call_id, + pfc_flags=pfc_flags) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + if assoc_group_id != 0: + self.assertEqual(rep.u.assoc_group_id, assoc_group_id) + else: + self.assertNotEqual(rep.u.assoc_group_id, 0) + assoc_group_id = rep.u.assoc_group_id + sda_str = self.secondary_address + sda_len = len(sda_str) + 1 + mod_len = (2 + sda_len) % 4 + if mod_len != 0: + sda_pad = 4 - mod_len + else: + sda_pad = 0 + self.assertEqual(rep.u.secondary_address_size, sda_len) + self.assertEqual(rep.u.secondary_address, sda_str) + self.assertPadding(rep.u._pad1, sda_pad) + + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + samba.dcerpc.dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + samba.dcerpc.dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ctx.transfer_syntaxes[0]) + ack = rep + if auth_context is None: + self.assertEqual(rep.auth_length, 0) + self.assertEqual(len(rep.u.auth_info), 0) + return ack + self.assertNotEqual(rep.auth_length, 0) + self.assertGreater(len(rep.u.auth_info), samba.dcerpc.dcerpc.DCERPC_AUTH_TRAILER_LENGTH) + self.assertEqual(rep.auth_length, len(rep.u.auth_info) - samba.dcerpc.dcerpc.DCERPC_AUTH_TRAILER_LENGTH) + + a = self.parse_auth(rep.u.auth_info, auth_context=auth_context) + + from_server = a.credentials + (finished, to_server) = auth_context["gensec"].update(from_server) + if expect_3legs: + self.assertTrue(finished) + if auth_context['hdr_signing']: + auth_context["gensec"].want_feature(gensec.FEATURE_SIGN_PKT_HEADER) + else: + self.assertFalse(use_auth3) + self.assertFalse(finished) + + auth_info = self.generate_auth(auth_type=auth_context["auth_type"], + auth_level=auth_context["auth_level"], + auth_context_id=auth_context["auth_context_id"], + auth_blob=to_server) + if use_auth3: + req = self.generate_auth3(call_id=call_id, + pfc_flags=pfc_flags_2nd, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu(timeout=0.01) + self.assertIsNone(rep) + self.assertIsConnected() + return ack + req = self.generate_alter(call_id=call_id, + ctx_list=ctx_list, + pfc_flags=pfc_flags_2nd, + assoc_group_id=0xffffffff - assoc_group_id, + auth_info=auth_info) + self.send_pdu(req) + rep = self.recv_pdu() + if alter_fault is not None: + self.verify_pdu(rep, samba.dcerpc.dcerpc.DCERPC_PKT_FAULT, req.call_id, + pfc_flags=req.pfc_flags | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_DID_NOT_EXECUTE, + auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, 0) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, alter_fault) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + return None + self.verify_pdu(rep, samba.dcerpc.dcerpc.DCERPC_PKT_ALTER_RESP, req.call_id, + pfc_flags=req.pfc_flags) + self.assertEqual(rep.u.max_xmit_frag, req.u.max_xmit_frag) + self.assertEqual(rep.u.max_recv_frag, req.u.max_recv_frag) + self.assertEqual(rep.u.assoc_group_id, assoc_group_id) + self.assertEqual(rep.u.secondary_address_size, 0) + self.assertEqual(rep.u.secondary_address, '') + self.assertPadding(rep.u._pad1, 2) + self.assertEqual(rep.u.num_results, 1) + self.assertEqual(rep.u.ctx_list[0].result, + samba.dcerpc.dcerpc.DCERPC_BIND_ACK_RESULT_ACCEPTANCE) + self.assertEqual(rep.u.ctx_list[0].reason, + samba.dcerpc.dcerpc.DCERPC_BIND_ACK_REASON_NOT_SPECIFIED) + self.assertNDRSyntaxEquals(rep.u.ctx_list[0].syntax, ctx.transfer_syntaxes[0]) + if finished: + self.assertEqual(rep.auth_length, 0) + else: + self.assertNotEqual(rep.auth_length, 0) + self.assertGreaterEqual(len(rep.u.auth_info), samba.dcerpc.dcerpc.DCERPC_AUTH_TRAILER_LENGTH) + self.assertEqual(rep.auth_length, len(rep.u.auth_info) - samba.dcerpc.dcerpc.DCERPC_AUTH_TRAILER_LENGTH) + + a = self.parse_auth(rep.u.auth_info, auth_context=auth_context) + + if finished: + return ack + + from_server = a.credentials + (finished, to_server) = auth_context["gensec"].update(from_server) + self.assertTrue(finished) + if auth_context['hdr_signing']: + auth_context["gensec"].want_feature(gensec.FEATURE_SIGN_PKT_HEADER) + + return ack + + def prepare_presentation(self, abstract, transfer, object=None, + context_id=0xffff, epmap=False, auth_context=None, + pfc_flags=samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST, + assoc_group_id=0, + return_ack=False): + if epmap: + self.epmap_reconnect(abstract, transfer=transfer, object=object) + + tsf1_list = [transfer] + ctx = samba.dcerpc.dcerpc.ctx_list() + ctx.context_id = context_id + ctx.num_transfer_syntaxes = len(tsf1_list) + ctx.abstract_syntax = abstract + ctx.transfer_syntaxes = tsf1_list + + ack = self.do_generic_bind(ctx=ctx, + auth_context=auth_context, + pfc_flags=pfc_flags, + assoc_group_id=assoc_group_id) + if ack is None: + ctx = None + + if return_ack: + return (ctx, ack) + return ctx + + def do_single_request(self, call_id, ctx, io, + auth_context=None, + object=None, + bigendian=False, ndr64=False, + allow_remaining=False, + send_req=True, + recv_rep=True, + fault_pfc_flags=( + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST), + fault_status=None, + fault_context_id=None, + timeout=None, + ndr_print=None, + hexdump=None): + + if fault_context_id is None: + fault_context_id = ctx.context_id + + if ndr_print is None: + ndr_print = self.do_ndr_print + if hexdump is None: + hexdump = self.do_hexdump + + if send_req: + if ndr_print: + sys.stderr.write("in: %s" % samba.ndr.ndr_print_in(io)) + stub_in = samba.ndr.ndr_pack_in(io, bigendian=bigendian, ndr64=ndr64) + if hexdump: + sys.stderr.write("stub_in: %d\n%s" % (len(stub_in), self.hexdump(stub_in))) + + pfc_flags = samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST + pfc_flags |= samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST + if object is not None: + pfc_flags |= samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_OBJECT_UUID + + req = self.generate_request_auth(call_id=call_id, + context_id=ctx.context_id, + pfc_flags=pfc_flags, + object=object, + opnum=io.opnum(), + stub=stub_in, + auth_context=auth_context) + self.send_pdu(req, ndr_print=ndr_print, hexdump=hexdump) + if recv_rep: + (rep, rep_blob) = self.recv_pdu_raw(timeout=timeout, + ndr_print=ndr_print, + hexdump=hexdump) + if fault_status: + self.verify_pdu(rep, samba.dcerpc.dcerpc.DCERPC_PKT_FAULT, call_id, + pfc_flags=fault_pfc_flags, auth_length=0) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, fault_context_id) + self.assertEqual(rep.u.cancel_count, 0) + self.assertEqual(rep.u.flags, 0) + self.assertEqual(rep.u.status, fault_status) + self.assertEqual(rep.u.reserved, 0) + self.assertEqual(len(rep.u.error_and_verifier), 0) + return + + expected_auth_length = 0 + if auth_context is not None and \ + auth_context["auth_level"] >= dcerpc.DCERPC_AUTH_LEVEL_PACKET: + if send_req: + expected_auth_length = req.auth_length + else: + expected_auth_length = rep.auth_length + + + self.verify_pdu(rep, samba.dcerpc.dcerpc.DCERPC_PKT_RESPONSE, call_id, + auth_length=expected_auth_length) + self.assertNotEqual(rep.u.alloc_hint, 0) + self.assertEqual(rep.u.context_id, ctx.context_id & 0xff) + self.assertEqual(rep.u.cancel_count, 0) + self.assertGreaterEqual(len(rep.u.stub_and_verifier), rep.u.alloc_hint) + stub_out = self.check_response_auth(rep, rep_blob, auth_context) + self.assertEqual(len(stub_out), rep.u.alloc_hint) + + if hexdump: + sys.stderr.write("stub_out: %d\n%s" % (len(stub_out), self.hexdump(stub_out))) + ndr_unpack_out(io, stub_out, bigendian=bigendian, ndr64=ndr64, + allow_remaining=allow_remaining) + if ndr_print: + sys.stderr.write("out: %s" % samba.ndr.ndr_print_out(io)) + + def epmap_reconnect(self, abstract, transfer=None, object=None): + ndr32 = samba.dcerpc.base.transfer_syntax_ndr() + + if transfer is None: + transfer = ndr32 + + if object is None: + object = samba.dcerpc.misc.GUID() + + ctx = self.prepare_presentation(samba.dcerpc.epmapper.abstract_syntax(), + transfer, context_id=0) + + data1 = ndr_pack(abstract) + lhs1 = samba.dcerpc.epmapper.epm_lhs() + lhs1.protocol = samba.dcerpc.epmapper.EPM_PROTOCOL_UUID + lhs1.lhs_data = data1[:18] + rhs1 = samba.dcerpc.epmapper.epm_rhs_uuid() + rhs1.unknown = data1[18:] + floor1 = samba.dcerpc.epmapper.epm_floor() + floor1.lhs = lhs1 + floor1.rhs = rhs1 + data2 = ndr_pack(transfer) + lhs2 = samba.dcerpc.epmapper.epm_lhs() + lhs2.protocol = samba.dcerpc.epmapper.EPM_PROTOCOL_UUID + lhs2.lhs_data = data2[:18] + rhs2 = samba.dcerpc.epmapper.epm_rhs_uuid() + rhs2.unknown = data1[18:] + floor2 = samba.dcerpc.epmapper.epm_floor() + floor2.lhs = lhs2 + floor2.rhs = rhs2 + lhs3 = samba.dcerpc.epmapper.epm_lhs() + lhs3.protocol = samba.dcerpc.epmapper.EPM_PROTOCOL_NCACN + lhs3.lhs_data = b"" + floor3 = samba.dcerpc.epmapper.epm_floor() + floor3.lhs = lhs3 + floor3.rhs.minor_version = 0 + lhs4 = samba.dcerpc.epmapper.epm_lhs() + lhs4.protocol = samba.dcerpc.epmapper.EPM_PROTOCOL_TCP + lhs4.lhs_data = b"" + floor4 = samba.dcerpc.epmapper.epm_floor() + floor4.lhs = lhs4 + floor4.rhs.port = int(self.primary_address) + lhs5 = samba.dcerpc.epmapper.epm_lhs() + lhs5.protocol = samba.dcerpc.epmapper.EPM_PROTOCOL_IP + lhs5.lhs_data = b"" + floor5 = samba.dcerpc.epmapper.epm_floor() + floor5.lhs = lhs5 + floor5.rhs.ipaddr = "0.0.0.0" + + floors = [floor1, floor2, floor3, floor4, floor5] + req_tower = samba.dcerpc.epmapper.epm_tower() + req_tower.num_floors = len(floors) + req_tower.floors = floors + req_twr = samba.dcerpc.epmapper.epm_twr_t() + req_twr.tower = req_tower + + epm_map = samba.dcerpc.epmapper.epm_Map() + epm_map.in_object = object + epm_map.in_map_tower = req_twr + epm_map.in_entry_handle = samba.dcerpc.misc.policy_handle() + epm_map.in_max_towers = 4 + + self.do_single_request(call_id=2, ctx=ctx, io=epm_map) + + self.assertGreaterEqual(epm_map.out_num_towers, 1) + rep_twr = epm_map.out_towers[0].twr + self.assertIsNotNone(rep_twr) + self.assertEqual(rep_twr.tower_length, 75) + self.assertEqual(rep_twr.tower.num_floors, 5) + self.assertEqual(len(rep_twr.tower.floors), 5) + self.assertEqual(rep_twr.tower.floors[3].lhs.protocol, + samba.dcerpc.epmapper.EPM_PROTOCOL_TCP) + self.assertEqual(rep_twr.tower.floors[3].lhs.protocol, + samba.dcerpc.epmapper.EPM_PROTOCOL_TCP) + + # reconnect to the given port + self._disconnect("epmap_reconnect") + self.primary_address = "%d" % rep_twr.tower.floors[3].rhs.port + self.secondary_address = None + self.connect() + + def send_pdu(self, req, ndr_print=None, hexdump=None): + if ndr_print is None: + ndr_print = self.do_ndr_print + if hexdump is None: + hexdump = self.do_hexdump + try: + req_pdu = ndr_pack(req) + if ndr_print: + sys.stderr.write("send_pdu: %s" % samba.ndr.ndr_print(req)) + if hexdump: + sys.stderr.write("send_pdu: %d\n%s" % (len(req_pdu), self.hexdump(req_pdu))) + while True: + sent = self.s.send(req_pdu, 0) + if sent == len(req_pdu): + break + req_pdu = req_pdu[sent:] + except socket.error as e: + self._disconnect("send_pdu: %s" % e) + raise + except IOError as e: + self._disconnect("send_pdu: %s" % e) + raise + except NTSTATUSError as e: + self._disconnect("send_pdu: %s" % e) + raise + finally: + pass + + def recv_raw(self, hexdump=None, timeout=None): + rep_pdu = None + if hexdump is None: + hexdump = self.do_hexdump + try: + if timeout is not None: + self.s.settimeout(timeout) + rep_pdu = self.s.recv(0xffff, 0) + self.s.settimeout(10) + if len(rep_pdu) == 0: + self._disconnect("recv_raw: EOF") + return None + if hexdump: + sys.stderr.write("recv_raw: %d\n%s" % (len(rep_pdu), self.hexdump(rep_pdu))) + except socket.timeout as e: + self.s.settimeout(10) + sys.stderr.write("recv_raw: TIMEOUT\n") + pass + except socket.error as e: + self._disconnect("recv_raw: %s" % e) + raise + except IOError as e: + self._disconnect("recv_raw: %s" % e) + raise + finally: + pass + return rep_pdu + + def recv_pdu_raw(self, ndr_print=None, hexdump=None, timeout=None): + rep_pdu = None + rep = None + if ndr_print is None: + ndr_print = self.do_ndr_print + if hexdump is None: + hexdump = self.do_hexdump + try: + rep_pdu = self.recv_raw(hexdump=hexdump, timeout=timeout) + if rep_pdu is None: + return (None, None) + rep = ndr_unpack(samba.dcerpc.dcerpc.ncacn_packet, rep_pdu, allow_remaining=True) + if ndr_print: + sys.stderr.write("recv_pdu: %s" % samba.ndr.ndr_print(rep)) + self.assertEqual(rep.frag_length, len(rep_pdu)) + finally: + pass + return (rep, rep_pdu) + + def recv_pdu(self, ndr_print=None, hexdump=None, timeout=None): + (rep, rep_pdu) = self.recv_pdu_raw(ndr_print=ndr_print, + hexdump=hexdump, + timeout=timeout) + return rep + + def generate_auth(self, + auth_type=None, + auth_level=None, + auth_pad_length=0, + auth_context_id=None, + auth_blob=None, + ndr_print=None, hexdump=None): + if ndr_print is None: + ndr_print = self.do_ndr_print + if hexdump is None: + hexdump = self.do_hexdump + + if auth_type is not None: + a = samba.dcerpc.dcerpc.auth() + a.auth_type = auth_type + a.auth_level = auth_level + a.auth_pad_length = auth_pad_length + a.auth_context_id = auth_context_id + a.credentials = auth_blob + + ai = ndr_pack(a) + if ndr_print: + sys.stderr.write("generate_auth: %s" % samba.ndr.ndr_print(a)) + if hexdump: + sys.stderr.write("generate_auth: %d\n%s" % (len(ai), self.hexdump(ai))) + else: + ai = b"" + + return ai + + def parse_auth(self, auth_info, ndr_print=None, hexdump=None, + auth_context=None, stub_len=0): + if ndr_print is None: + ndr_print = self.do_ndr_print + if hexdump is None: + hexdump = self.do_hexdump + + if (len(auth_info) <= samba.dcerpc.dcerpc.DCERPC_AUTH_TRAILER_LENGTH): + return None + + if hexdump: + sys.stderr.write("parse_auth: %d\n%s" % (len(auth_info), self.hexdump(auth_info))) + a = ndr_unpack(samba.dcerpc.dcerpc.auth, auth_info, allow_remaining=True) + if ndr_print: + sys.stderr.write("parse_auth: %s" % samba.ndr.ndr_print(a)) + + if auth_context is not None: + self.assertEqual(a.auth_type, auth_context["auth_type"]) + self.assertEqual(a.auth_level, auth_context["auth_level"]) + self.assertEqual(a.auth_reserved, 0) + self.assertEqual(a.auth_context_id, auth_context["auth_context_id"]) + + self.assertLessEqual(a.auth_pad_length, dcerpc.DCERPC_AUTH_PAD_ALIGNMENT) + self.assertLessEqual(a.auth_pad_length, stub_len) + + return a + + def check_response_auth(self, rep, rep_blob, auth_context=None, + auth_pad_length=None): + + if auth_context is None: + self.assertEqual(rep.auth_length, 0) + return rep.u.stub_and_verifier + + if auth_context["auth_level"] == dcerpc.DCERPC_AUTH_LEVEL_CONNECT: + self.assertEqual(rep.auth_length, 0) + return rep.u.stub_and_verifier + + self.assertGreater(rep.auth_length, 0) + + ofs_stub = dcerpc.DCERPC_REQUEST_LENGTH + ofs_sig = rep.frag_length - rep.auth_length + ofs_trailer = ofs_sig - dcerpc.DCERPC_AUTH_TRAILER_LENGTH + rep_data = rep_blob[ofs_stub:ofs_trailer] + rep_whole = rep_blob[0:ofs_sig] + rep_sig = rep_blob[ofs_sig:] + rep_auth_info_blob = rep_blob[ofs_trailer:] + + rep_auth_info = self.parse_auth(rep_auth_info_blob, + auth_context=auth_context, + stub_len=len(rep_data)) + if auth_pad_length is not None: + self.assertEqual(rep_auth_info.auth_pad_length, auth_pad_length) + self.assertEqual(rep_auth_info.credentials, rep_sig) + + if auth_context["auth_level"] >= dcerpc.DCERPC_AUTH_LEVEL_PRIVACY: + # TODO: not yet supported here + self.assertTrue(False) + elif auth_context["auth_level"] >= dcerpc.DCERPC_AUTH_LEVEL_PACKET: + auth_context["gensec"].check_packet(rep_data, rep_whole, rep_sig) + + stub_out = rep_data[0:len(rep_data)-rep_auth_info.auth_pad_length] + + return stub_out + + def generate_pdu(self, ptype, call_id, payload, + rpc_vers=5, + rpc_vers_minor=0, + pfc_flags=(samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST), + drep=None, + ndr_print=None, hexdump=None): + + if drep is None: + drep = [samba.dcerpc.dcerpc.DCERPC_DREP_LE, 0, 0, 0] + if getattr(payload, 'auth_info', None): + ai = payload.auth_info + else: + ai = b"" + + p = samba.dcerpc.dcerpc.ncacn_packet() + p.rpc_vers = rpc_vers + p.rpc_vers_minor = rpc_vers_minor + p.ptype = ptype + p.pfc_flags = pfc_flags + p.drep = drep + p.frag_length = 0 + if len(ai) > samba.dcerpc.dcerpc.DCERPC_AUTH_TRAILER_LENGTH: + p.auth_length = len(ai) - samba.dcerpc.dcerpc.DCERPC_AUTH_TRAILER_LENGTH + else: + p.auth_length = 0 + p.call_id = call_id + p.u = payload + + pdu = ndr_pack(p) + p.frag_length = len(pdu) + + return p + + def generate_request_auth(self, call_id, + pfc_flags=(dcerpc.DCERPC_PFC_FLAG_FIRST | + dcerpc.DCERPC_PFC_FLAG_LAST), + alloc_hint=None, + context_id=None, + opnum=None, + object=None, + stub=None, + auth_context=None, + ndr_print=None, hexdump=None): + + if stub is None: + stub = b"" + + sig_size = 0 + if auth_context is not None: + mod_len = len(stub) % dcerpc.DCERPC_AUTH_PAD_ALIGNMENT + auth_pad_length = 0 + if mod_len > 0: + auth_pad_length = dcerpc.DCERPC_AUTH_PAD_ALIGNMENT - mod_len + stub += b'\x00' * auth_pad_length + + if auth_context["g_auth_level"] >= samba.dcerpc.dcerpc.DCERPC_AUTH_LEVEL_PACKET: + sig_size = auth_context["gensec"].sig_size(len(stub)) + else: + sig_size = 16 + + zero_sig = b"\x00" * sig_size + auth_info = self.generate_auth(auth_type=auth_context["auth_type"], + auth_level=auth_context["auth_level"], + auth_pad_length=auth_pad_length, + auth_context_id=auth_context["auth_context_id"], + auth_blob=zero_sig) + else: + auth_info = b"" + + req = self.generate_request(call_id=call_id, + pfc_flags=pfc_flags, + alloc_hint=alloc_hint, + context_id=context_id, + opnum=opnum, + object=object, + stub=stub, + auth_info=auth_info, + ndr_print=ndr_print, + hexdump=hexdump) + if auth_context is None: + return req + + req_blob = samba.ndr.ndr_pack(req) + ofs_stub = dcerpc.DCERPC_REQUEST_LENGTH + ofs_sig = len(req_blob) - req.auth_length + ofs_trailer = ofs_sig - dcerpc.DCERPC_AUTH_TRAILER_LENGTH + req_data = req_blob[ofs_stub:ofs_trailer] + req_whole = req_blob[0:ofs_sig] + + if auth_context["g_auth_level"] >= dcerpc.DCERPC_AUTH_LEVEL_PRIVACY: + # TODO: not yet supported here + self.assertTrue(False) + elif auth_context["g_auth_level"] >= dcerpc.DCERPC_AUTH_LEVEL_PACKET: + req_sig = auth_context["gensec"].sign_packet(req_data, req_whole) + elif auth_context["g_auth_level"] >= dcerpc.DCERPC_AUTH_LEVEL_CONNECT: + self.assertEqual(auth_context["auth_type"], + dcerpc.DCERPC_AUTH_TYPE_NTLMSSP) + req_sig = b"\x01" +b"\x00" *15 + else: + return req + self.assertEqual(len(req_sig), req.auth_length) + self.assertEqual(len(req_sig), sig_size) + + stub_sig_ofs = len(req.u.stub_and_verifier) - sig_size + stub = req.u.stub_and_verifier[0:stub_sig_ofs] + req_sig + req.u.stub_and_verifier = stub + + return req + + def verify_pdu(self, p, ptype, call_id, + rpc_vers=5, + rpc_vers_minor=0, + pfc_flags=(samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST), + drep=None, + auth_length=None): + + if drep is None: + drep = [samba.dcerpc.dcerpc.DCERPC_DREP_LE, 0, 0, 0] + self.assertIsNotNone(p, "No valid pdu") + + if getattr(p.u, 'auth_info', None): + ai = p.u.auth_info + else: + ai = b"" + + self.assertEqual(p.rpc_vers, rpc_vers) + self.assertEqual(p.rpc_vers_minor, rpc_vers_minor) + self.assertEqual(p.ptype, ptype) + self.assertEqual(p.pfc_flags, pfc_flags) + self.assertEqual(p.drep, drep) + self.assertGreaterEqual(p.frag_length, + samba.dcerpc.dcerpc.DCERPC_NCACN_PAYLOAD_OFFSET) + if len(ai) > samba.dcerpc.dcerpc.DCERPC_AUTH_TRAILER_LENGTH: + self.assertEqual(p.auth_length, + len(ai) - samba.dcerpc.dcerpc.DCERPC_AUTH_TRAILER_LENGTH) + elif auth_length is not None: + self.assertEqual(p.auth_length, auth_length) + else: + self.assertEqual(p.auth_length, 0) + self.assertEqual(p.call_id, call_id) + + return + + def generate_bind(self, call_id, + pfc_flags=(samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST), + max_xmit_frag=None, + max_recv_frag=None, + assoc_group_id=0, + ctx_list=None, + auth_info=b"", + ndr_print=None, hexdump=None): + + if ctx_list is None: + ctx_list = [] + if max_xmit_frag is None: + max_xmit_frag=self.max_xmit_frag + if max_recv_frag is None: + max_recv_frag=self.max_recv_frag + + b = samba.dcerpc.dcerpc.bind() + b.max_xmit_frag = max_xmit_frag + b.max_recv_frag = max_recv_frag + b.assoc_group_id = assoc_group_id + b.num_contexts = len(ctx_list) + b.ctx_list = ctx_list + b.auth_info = auth_info + + p = self.generate_pdu(ptype=samba.dcerpc.dcerpc.DCERPC_PKT_BIND, + pfc_flags=pfc_flags, + call_id=call_id, + payload=b, + ndr_print=ndr_print, hexdump=hexdump) + + return p + + def generate_alter(self, call_id, + pfc_flags=(samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST), + max_xmit_frag=None, + max_recv_frag=None, + assoc_group_id=0, + ctx_list=None, + auth_info=b"", + ndr_print=None, hexdump=None): + + if ctx_list is None: + ctx_list = [] + if max_xmit_frag is None: + max_xmit_frag=self.max_xmit_frag + if max_recv_frag is None: + max_recv_frag=self.max_recv_frag + + a = samba.dcerpc.dcerpc.bind() + a.max_xmit_frag = max_xmit_frag + a.max_recv_frag = max_recv_frag + a.assoc_group_id = assoc_group_id + a.num_contexts = len(ctx_list) + a.ctx_list = ctx_list + a.auth_info = auth_info + + p = self.generate_pdu(ptype=samba.dcerpc.dcerpc.DCERPC_PKT_ALTER, + pfc_flags=pfc_flags, + call_id=call_id, + payload=a, + ndr_print=ndr_print, hexdump=hexdump) + + return p + + def generate_auth3(self, call_id, + pfc_flags=(samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST), + auth_info=b"", + ndr_print=None, hexdump=None): + + a = samba.dcerpc.dcerpc.auth3() + a.auth_info = auth_info + + p = self.generate_pdu(ptype=samba.dcerpc.dcerpc.DCERPC_PKT_AUTH3, + pfc_flags=pfc_flags, + call_id=call_id, + payload=a, + ndr_print=ndr_print, hexdump=hexdump) + + return p + + def generate_request(self, call_id, + pfc_flags=(samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST), + alloc_hint=None, + context_id=None, + opnum=None, + object=None, + stub=None, + auth_info=b"", + ndr_print=None, hexdump=None): + + if alloc_hint is None: + alloc_hint = len(stub) + + r = samba.dcerpc.dcerpc.request() + r.alloc_hint = alloc_hint + r.context_id = context_id + r.opnum = opnum + if object is not None: + r.object = object + r.stub_and_verifier = stub + auth_info + + p = self.generate_pdu(ptype=samba.dcerpc.dcerpc.DCERPC_PKT_REQUEST, + pfc_flags=pfc_flags, + call_id=call_id, + payload=r, + ndr_print=ndr_print, hexdump=hexdump) + + if len(auth_info) > samba.dcerpc.dcerpc.DCERPC_AUTH_TRAILER_LENGTH: + p.auth_length = len(auth_info) - samba.dcerpc.dcerpc.DCERPC_AUTH_TRAILER_LENGTH + + return p + + def generate_co_cancel(self, call_id, + pfc_flags=(samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST), + auth_info=b"", + ndr_print=None, hexdump=None): + + c = samba.dcerpc.dcerpc.co_cancel() + c.auth_info = auth_info + + p = self.generate_pdu(ptype=samba.dcerpc.dcerpc.DCERPC_PKT_CO_CANCEL, + pfc_flags=pfc_flags, + call_id=call_id, + payload=c, + ndr_print=ndr_print, hexdump=hexdump) + + return p + + def generate_orphaned(self, call_id, + pfc_flags=(samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST), + auth_info=b"", + ndr_print=None, hexdump=None): + + o = samba.dcerpc.dcerpc.orphaned() + o.auth_info = auth_info + + p = self.generate_pdu(ptype=samba.dcerpc.dcerpc.DCERPC_PKT_ORPHANED, + pfc_flags=pfc_flags, + call_id=call_id, + payload=o, + ndr_print=ndr_print, hexdump=hexdump) + + return p + + def generate_shutdown(self, call_id, + pfc_flags=(samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_FIRST | + samba.dcerpc.dcerpc.DCERPC_PFC_FLAG_LAST), + ndr_print=None, hexdump=None): + + s = samba.dcerpc.dcerpc.shutdown() + + p = self.generate_pdu(ptype=samba.dcerpc.dcerpc.DCERPC_PKT_SHUTDOWN, + pfc_flags=pfc_flags, + call_id=call_id, + payload=s, + ndr_print=ndr_print, hexdump=hexdump) + + return p + + def assertIsConnected(self): + self.assertIsNotNone(self.s, msg="Not connected") + return + + def assertNotConnected(self): + self.assertIsNone(self.s, msg="Is connected") + return + + def assertNDRSyntaxEquals(self, s1, s2): + self.assertEqual(s1.uuid, s2.uuid) + self.assertEqual(s1.if_version, s2.if_version) + return + + def assertPadding(self, pad, length): + self.assertEqual(len(pad), length) + # + # sometimes windows sends random bytes + # + # we have IGNORE_RANDOM_PAD=1 to + # disable the check + # + if self.ignore_random_pad: + return + zero_pad = b'\0' * length + self.assertEqual(pad, zero_pad) + + def assertEqualsStrLower(self, s1, s2): + self.assertEqual(str(s1).lower(), str(s2).lower()) diff --git a/python/samba/tests/dcerpc/registry.py b/python/samba/tests/dcerpc/registry.py new file mode 100644 index 0000000..be9e484 --- /dev/null +++ b/python/samba/tests/dcerpc/registry.py @@ -0,0 +1,51 @@ +# Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.dcerpc.registry.""" + +from samba.dcerpc import winreg +from samba.tests import RpcInterfaceTestCase + + +class WinregTests(RpcInterfaceTestCase): + + def setUp(self): + super().setUp() + self.conn = winreg.winreg("ncalrpc:", self.get_loadparm(), + self.get_credentials()) + + def get_hklm(self): + return self.conn.OpenHKLM(None, + winreg.KEY_QUERY_VALUE | winreg.KEY_ENUMERATE_SUB_KEYS) + + def test_hklm(self): + handle = self.conn.OpenHKLM(None, + winreg.KEY_QUERY_VALUE | winreg.KEY_ENUMERATE_SUB_KEYS) + self.conn.CloseKey(handle) + + def test_getversion(self): + handle = self.get_hklm() + version = self.conn.GetVersion(handle) + self.assertEqual(int, version.__class__) + self.conn.CloseKey(handle) + + def test_getkeyinfo(self): + handle = self.conn.OpenHKLM(None, + winreg.KEY_QUERY_VALUE | winreg.KEY_ENUMERATE_SUB_KEYS) + x = self.conn.QueryInfoKey(handle, winreg.String()) + self.assertEqual(9, len(x)) # should return a 9-tuple + self.conn.CloseKey(handle) diff --git a/python/samba/tests/dcerpc/rpc_talloc.py b/python/samba/tests/dcerpc/rpc_talloc.py new file mode 100644 index 0000000..4ad86a6 --- /dev/null +++ b/python/samba/tests/dcerpc/rpc_talloc.py @@ -0,0 +1,86 @@ +# test generated python code from pidl +# Copyright (C) Andrew Tridgell August 2010 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# +# to run this test, use one of these: +# +# python -m unittest samba.tests.dcerpc.rpc_talloc +# +# or if you have trial installed (from twisted), use +# +# trial samba.tests.dcerpc.rpc_talloc + +"""Tests for the talloc handling in the generated Python DCE/RPC bindings.""" + +import sys + +sys.path.insert(0, "bin/python") + +import samba +import samba.tests +from samba.dcerpc import drsuapi +import talloc + +talloc.enable_null_tracking() + + +class TallocTests(samba.tests.TestCase): + """test talloc behaviour of pidl generated python code""" + + def check_blocks(self, object, num_expected): + """check that the number of allocated blocks is correct""" + nblocks = talloc.total_blocks(object) + if object is None: + nblocks -= self.initial_blocks + self.assertEqual(nblocks, num_expected) + + def get_rodc_partial_attribute_set(self): + """get a list of attributes for RODC replication""" + partial_attribute_set = drsuapi.DsPartialAttributeSet() + + # we expect one block for the object + self.check_blocks(partial_attribute_set, 1) + + attids = [1, 2, 3] + partial_attribute_set.version = 1 + partial_attribute_set.attids = attids + partial_attribute_set.num_attids = len(attids) + + # we expect one block for the object, a structure, and a + # reference to the array + self.check_blocks(partial_attribute_set, 2) + + return partial_attribute_set + + def pas_test(self): + pas = self.get_rodc_partial_attribute_set() + self.check_blocks(pas, 2) + req8 = drsuapi.DsGetNCChangesRequest8() + self.check_blocks(req8, 1) + + # We expect the pas and req8, plus one block for each python object + self.check_blocks(None, 5) + req8.partial_attribute_set = pas + if req8.partial_attribute_set.attids[1] != 2: + raise Exception("Wrong value in attids[2]") + # we now get an additional reference + self.check_blocks(None, 6) + + def test_run(self): + self.initial_blocks = talloc.total_blocks(None) + self.check_blocks(None, 0) + self.pas_test() + self.check_blocks(None, 0) diff --git a/python/samba/tests/dcerpc/rpcecho.py b/python/samba/tests/dcerpc/rpcecho.py new file mode 100644 index 0000000..949e4e2 --- /dev/null +++ b/python/samba/tests/dcerpc/rpcecho.py @@ -0,0 +1,71 @@ +# Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.dceprc.rpcecho.""" + +from samba.dcerpc import echo +from samba.ndr import ndr_pack, ndr_unpack +from samba.tests import RpcInterfaceTestCase, TestCase + + +class RpcEchoTests(RpcInterfaceTestCase): + + def setUp(self): + super().setUp() + self.conn = echo.rpcecho("ncalrpc:", self.get_loadparm()) + + def test_two_contexts(self): + self.conn2 = echo.rpcecho("ncalrpc:", self.get_loadparm(), basis_connection=self.conn) + self.assertEqual(3, self.conn2.AddOne(2)) + + def test_abstract_syntax(self): + self.assertEqual(("60a15ec5-4de8-11d7-a637-005056a20182", 1), + self.conn.abstract_syntax) + + def test_addone(self): + self.assertEqual(2, self.conn.AddOne(1)) + + def test_echodata(self): + self.assertEqual([1, 2, 3], self.conn.EchoData([1, 2, 3])) + + def test_call(self): + self.assertEqual(u"foobar", self.conn.TestCall(u"foobar")) + + def test_surrounding(self): + surrounding_struct = echo.Surrounding() + surrounding_struct.x = 4 + surrounding_struct.surrounding = [1, 2, 3, 4] + y = self.conn.TestSurrounding(surrounding_struct) + self.assertEqual(8 * [0], y.surrounding) + + def test_manual_request(self): + self.assertEqual(b"\x01\x00\x00\x00", self.conn.request(0, chr(0) * 4)) + + def test_server_name(self): + self.assertEqual(None, self.conn.server_name) + + +class NdrEchoTests(TestCase): + + def test_info1_push(self): + x = echo.info1() + x.v = 42 + self.assertEqual(b"\x2a", ndr_pack(x)) + + def test_info1_pull(self): + x = ndr_unpack(echo.info1, b"\x42") + self.assertEqual(x.v, 66) diff --git a/python/samba/tests/dcerpc/sam.py b/python/samba/tests/dcerpc/sam.py new file mode 100644 index 0000000..9029cce --- /dev/null +++ b/python/samba/tests/dcerpc/sam.py @@ -0,0 +1,783 @@ +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.dcerpc.sam.""" + +from samba.dcerpc import samr, security, lsa +from samba.dcerpc.samr import DomainGeneralInformation +from samba.tests import RpcInterfaceTestCase +from samba.tests import env_loadparm, delete_force + +from samba.credentials import Credentials +from samba.auth import system_session +from samba.samdb import SamDB +from samba.dsdb import ( + ATYPE_NORMAL_ACCOUNT, + ATYPE_WORKSTATION_TRUST, + GTYPE_SECURITY_UNIVERSAL_GROUP, + GTYPE_SECURITY_GLOBAL_GROUP) +from samba import generate_random_password +from samba.ndr import ndr_unpack +import os + + +# FIXME: Pidl should be doing this for us +def toArray(handle, array, num_entries): + return [(entry.idx, entry.name) for entry in array.entries[:num_entries]] + + +# Extract the rid from an ldb message, assumes that the message has a +# objectSID attribute +# +def rid(msg): + sid = ndr_unpack(security.dom_sid, msg["objectSID"][0]) + (_, rid) = sid.split() + return rid + + +# Calculate the request size for EnumDomainUsers and EnumDomainGroups calls +# to hold the specified number of entries. +# We use the w2k3 element size value of 54, code under test +# rounds this up i.e. (1+(max_size/SAMR_ENUM_USERS_MULTIPLIER)) +# +def calc_max_size(num_entries): + return (num_entries - 1) * 54 + + +class SamrTests(RpcInterfaceTestCase): + + def setUp(self): + super().setUp() + self.conn = samr.samr("ncalrpc:", self.get_loadparm()) + self.open_samdb() + self.open_domain_handle() + + # + # Open the samba database + # + def open_samdb(self): + self.lp = env_loadparm() + self.domain = os.environ["DOMAIN"] + self.creds = Credentials() + self.creds.guess(self.lp) + self.session = system_session() + self.samdb = SamDB( + session_info=self.session, credentials=self.creds, lp=self.lp) + + # + # Open a SAMR Domain handle + def open_domain_handle(self): + self.handle = self.conn.Connect2( + None, security.SEC_FLAG_MAXIMUM_ALLOWED) + + self.domain_sid = self.conn.LookupDomain( + self.handle, lsa.String(self.domain)) + + self.domain_handle = self.conn.OpenDomain( + self.handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid) + + # Filter a list of records, removing those that are not part of the + # current domain. + # + def filter_domain(self, unfiltered): + def sid(msg): + sid = ndr_unpack(security.dom_sid, msg["objectSID"][0]) + (x, _) = sid.split() + return x + + dom_sid = security.dom_sid(self.samdb.get_domain_sid()) + return [x for x in unfiltered if sid(x) == dom_sid] + + def test_connect5(self): + (level, info, handle) =\ + self.conn.Connect5(None, 0, 1, samr.ConnectInfo1()) + + def test_connect2(self): + handle = self.conn.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED) + self.assertTrue(handle is not None) + + def test_EnumDomains(self): + handle = self.conn.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED) + toArray(*self.conn.EnumDomains(handle, 0, 4294967295)) + self.conn.Close(handle) + + # Create groups based on the id list supplied, the id is used to + # form a unique name and description. + # + # returns a list of the created dn's, which can be passed to delete_dns + # to clean up after the test has run. + def create_groups(self, ids): + dns = [] + for i in ids: + name = "SAMR_GRP%d" % i + dn = "cn=%s,cn=Users,%s" % (name, self.samdb.domain_dn()) + delete_force(self.samdb, dn) + + self.samdb.newgroup(name) + dns.append(dn) + return dns + + # Create user accounts based on the id list supplied, the id is used to + # form a unique name and description. + # + # returns a list of the created dn's, which can be passed to delete_dns + # to clean up after the test has run. + def create_users(self, ids): + dns = [] + for i in ids: + name = "SAMR_USER%d" % i + dn = "cn=%s,CN=USERS,%s" % (name, self.samdb.domain_dn()) + delete_force(self.samdb, dn) + + # We only need the user to exist, we don't need a password + self.samdb.newuser( + name, + password=None, + setpassword=False, + description="Description for " + name, + givenname="given%dname" % i, + surname="surname%d" % i) + dns.append(dn) + return dns + + # Create computer accounts based on the id list supplied, the id is used to + # form a unique name and description. + # + # returns a list of the created dn's, which can be passed to delete_dns + # to clean up after the test has run. + def create_computers(self, ids): + dns = [] + for i in ids: + name = "SAMR_CMP%d" % i + dn = "cn=%s,cn=COMPUTERS,%s" % (name, self.samdb.domain_dn()) + delete_force(self.samdb, dn) + + self.samdb.newcomputer(name, description="Description of " + name) + dns.append(dn) + return dns + + # Delete the specified dn's. + # + # Used to clean up entries created by individual tests. + # + def delete_dns(self, dns): + for dn in dns: + delete_force(self.samdb, dn) + + # Common tests for QueryDisplayInfo + # + def _test_QueryDisplayInfo( + self, level, check_results, select, attributes, add_elements): + # + # Get the expected results by querying the samdb database directly. + # We do this rather than use a list of expected results as this runs + # with other tests so we do not have a known fixed list of elements + expected = self.samdb.search(expression=select, attrs=attributes) + self.assertTrue(len(expected) > 0) + + # + # Perform QueryDisplayInfo with max results greater than the expected + # number of results. + (ts, rs, actual) = self.conn.QueryDisplayInfo( + self.domain_handle, level, 0, 1024, 4294967295) + + self.assertEqual(len(expected), ts) + self.assertEqual(len(expected), rs) + check_results(expected, actual.entries) + + # + # Perform QueryDisplayInfo with max results set to the number of + # results returned from the first query, should return the same results + (ts1, rs1, actual1) = self.conn.QueryDisplayInfo( + self.domain_handle, level, 0, rs, 4294967295) + self.assertEqual(ts, ts1) + self.assertEqual(rs, rs1) + check_results(expected, actual1.entries) + + # + # Perform QueryDisplayInfo and get the last two results. + # Note: We are assuming there are at least three entries + self.assertTrue(ts > 2) + (ts2, rs2, actual2) = self.conn.QueryDisplayInfo( + self.domain_handle, level, (ts - 2), 2, 4294967295) + self.assertEqual(ts, ts2) + self.assertEqual(2, rs2) + check_results(list(expected)[-2:], actual2.entries) + + # + # Perform QueryDisplayInfo and get the first two results. + # Note: We are assuming there are at least three entries + self.assertTrue(ts > 2) + (ts2, rs2, actual2) = self.conn.QueryDisplayInfo( + self.domain_handle, level, 0, 2, 4294967295) + self.assertEqual(ts, ts2) + self.assertEqual(2, rs2) + check_results(list(expected)[:2], actual2.entries) + + # + # Perform QueryDisplayInfo and get two results in the middle of the + # list i.e. not the first or the last entry. + # Note: We are assuming there are at least four entries + self.assertTrue(ts > 3) + (ts2, rs2, actual2) = self.conn.QueryDisplayInfo( + self.domain_handle, level, 1, 2, 4294967295) + self.assertEqual(ts, ts2) + self.assertEqual(2, rs2) + check_results(list(expected)[1:2], actual2.entries) + + # + # To check that cached values are being returned rather than the + # results being re-read from disk we add elements, and request all + # but the first result. + # + dns = add_elements([1000, 1002, 1003, 1004]) + + # + # Perform QueryDisplayInfo and get all but the first result. + # We should be using the cached results so the entries we just added + # should not be present + (ts3, rs3, actual3) = self.conn.QueryDisplayInfo( + self.domain_handle, level, 1, 1024, 4294967295) + self.assertEqual(ts, ts3) + self.assertEqual(len(expected) - 1, rs3) + check_results(list(expected)[1:], actual3.entries) + + # + # Perform QueryDisplayInfo and get all the results. + # As the start index is zero we should reread the data from disk and + # the added entries should be there + new = self.samdb.search(expression=select, attrs=attributes) + (ts4, rs4, actual4) = self.conn.QueryDisplayInfo( + self.domain_handle, level, 0, 1024, 4294967295) + self.assertEqual(len(expected) + len(dns), ts4) + self.assertEqual(len(expected) + len(dns), rs4) + check_results(new, actual4.entries) + + # Delete the added DN's and query all but the first entry. + # This should ensure the cached results are used and that the + # missing entry code is triggered. + self.delete_dns(dns) + (ts5, rs5, actual5) = self.conn.QueryDisplayInfo( + self.domain_handle, level, 1, 1024, 4294967295) + self.assertEqual(len(expected) + len(dns), ts5) + # The deleted results will be filtered from the result set so should + # be missing from the returned results. + # Note: depending on the GUID order, the first result in the cache may + # be a deleted entry, in which case the results will contain all + # the expected elements, otherwise the first expected result will + # be missing. + if rs5 == len(expected): + check_results(expected, actual5.entries) + elif rs5 == (len(expected) - 1): + check_results(list(expected)[1:], actual5.entries) + else: + self.fail("Incorrect number of entries {0}".format(rs5)) + + # + # Perform QueryDisplayInfo specifying an index past the end of the + # available data. + # Should return no data. + (ts6, rs6, actual6) = self.conn.QueryDisplayInfo( + self.domain_handle, level, ts5, 1, 4294967295) + self.assertEqual(ts5, ts6) + self.assertEqual(0, rs6) + + self.conn.Close(self.handle) + + # Test for QueryDisplayInfo, Level 1 + # Returns the sAMAccountName, displayName and description for all + # the user accounts. + # + def test_QueryDisplayInfo_level_1(self): + def check_results(expected, actual): + # Assume the QueryDisplayInfo and ldb.search return their results + # in the same order + for (e, a) in zip(expected, actual): + self.assertTrue(isinstance(a, samr.DispEntryGeneral)) + self.assertEqual(str(e["sAMAccountName"]), + str(a.account_name)) + + # The displayName and description are optional. + # In the expected results they will be missing, in + # samr.DispEntryGeneral the corresponding attribute will have a + # length of zero. + # + if a.full_name.length == 0: + self.assertFalse("displayName" in e) + else: + self.assertEqual(str(e["displayName"]), str(a.full_name)) + + if a.description.length == 0: + self.assertFalse("description" in e) + else: + self.assertEqual(str(e["description"]), + str(a.description)) + # Create four user accounts + # to ensure that we have the minimum needed for the tests. + dns = self.create_users([1, 2, 3, 4]) + + select = "(&(objectclass=user)(sAMAccountType={0}))".format( + ATYPE_NORMAL_ACCOUNT) + attributes = ["sAMAccountName", "displayName", "description"] + self._test_QueryDisplayInfo( + 1, check_results, select, attributes, self.create_users) + + self.delete_dns(dns) + + # Test for QueryDisplayInfo, Level 2 + # Returns the sAMAccountName and description for all + # the computer accounts. + # + def test_QueryDisplayInfo_level_2(self): + def check_results(expected, actual): + # Assume the QueryDisplayInfo and ldb.search return their results + # in the same order + for (e, a) in zip(expected, actual): + self.assertTrue(isinstance(a, samr.DispEntryFull)) + self.assertEqual(str(e["sAMAccountName"]), + str(a.account_name)) + + # The description is optional. + # In the expected results they will be missing, in + # samr.DispEntryGeneral the corresponding attribute will have a + # length of zero. + # + if a.description.length == 0: + self.assertFalse("description" in e) + else: + self.assertEqual(str(e["description"]), + str(a.description)) + + # Create four computer accounts + # to ensure that we have the minimum needed for the tests. + dns = self.create_computers([1, 2, 3, 4]) + + select = "(&(objectclass=user)(sAMAccountType={0}))".format( + ATYPE_WORKSTATION_TRUST) + attributes = ["sAMAccountName", "description"] + self._test_QueryDisplayInfo( + 2, check_results, select, attributes, self.create_computers) + + self.delete_dns(dns) + + # Test for QueryDisplayInfo, Level 3 + # Returns the sAMAccountName and description for all + # the groups. + # + def test_QueryDisplayInfo_level_3(self): + def check_results(expected, actual): + # Assume the QueryDisplayInfo and ldb.search return their results + # in the same order + for (e, a) in zip(expected, actual): + self.assertTrue(isinstance(a, samr.DispEntryFullGroup)) + self.assertEqual(str(e["sAMAccountName"]), + str(a.account_name)) + + # The description is optional. + # In the expected results they will be missing, in + # samr.DispEntryGeneral the corresponding attribute will have a + # length of zero. + # + if a.description.length == 0: + self.assertFalse("description" in e) + else: + self.assertEqual(str(e["description"]), + str(a.description)) + + # Create four groups + # to ensure that we have the minimum needed for the tests. + dns = self.create_groups([1, 2, 3, 4]) + + select = "(&(|(groupType=%d)(groupType=%d))(objectClass=group))" % ( + GTYPE_SECURITY_UNIVERSAL_GROUP, + GTYPE_SECURITY_GLOBAL_GROUP) + attributes = ["sAMAccountName", "description"] + self._test_QueryDisplayInfo( + 3, check_results, select, attributes, self.create_groups) + + self.delete_dns(dns) + + # Test for QueryDisplayInfo, Level 4 + # Returns the sAMAccountName (as an ASCII string) + # for all the user accounts. + # + def test_QueryDisplayInfo_level_4(self): + def check_results(expected, actual): + # Assume the QueryDisplayInfo and ldb.search return their results + # in the same order + for (e, a) in zip(expected, actual): + self.assertTrue(isinstance(a, samr.DispEntryAscii)) + self.assertTrue( + isinstance(a.account_name, lsa.AsciiStringLarge)) + self.assertEqual( + str(e["sAMAccountName"]), str(a.account_name.string)) + + # Create four user accounts + # to ensure that we have the minimum needed for the tests. + dns = self.create_users([1, 2, 3, 4]) + + select = "(&(objectclass=user)(sAMAccountType={0}))".format( + ATYPE_NORMAL_ACCOUNT) + attributes = ["sAMAccountName", "displayName", "description"] + self._test_QueryDisplayInfo( + 4, check_results, select, attributes, self.create_users) + + self.delete_dns(dns) + + # Test for QueryDisplayInfo, Level 5 + # Returns the sAMAccountName (as an ASCII string) + # for all the groups. + # + def test_QueryDisplayInfo_level_5(self): + def check_results(expected, actual): + # Assume the QueryDisplayInfo and ldb.search return their results + # in the same order + for (e, a) in zip(expected, actual): + self.assertTrue(isinstance(a, samr.DispEntryAscii)) + self.assertTrue( + isinstance(a.account_name, lsa.AsciiStringLarge)) + self.assertEqual( + str(e["sAMAccountName"]), str(a.account_name.string)) + + # Create four groups + # to ensure that we have the minimum needed for the tests. + dns = self.create_groups([1, 2, 3, 4]) + + select = "(&(|(groupType=%d)(groupType=%d))(objectClass=group))" % ( + GTYPE_SECURITY_UNIVERSAL_GROUP, + GTYPE_SECURITY_GLOBAL_GROUP) + attributes = ["sAMAccountName", "description"] + self._test_QueryDisplayInfo( + 5, check_results, select, attributes, self.create_groups) + + self.delete_dns(dns) + + def test_EnumDomainGroups(self): + def check_results(expected, actual): + for (e, a) in zip(expected, actual): + self.assertTrue(isinstance(a, samr.SamEntry)) + self.assertEqual( + str(e["sAMAccountName"]), str(a.name.string)) + + # Create four groups + # to ensure that we have the minimum needed for the tests. + dns = self.create_groups([1, 2, 3, 4]) + + # + # Get the expected results by querying the samdb database directly. + # We do this rather than use a list of expected results as this runs + # with other tests so we do not have a known fixed list of elements + select = "(&(|(groupType=%d)(groupType=%d))(objectClass=group))" % ( + GTYPE_SECURITY_UNIVERSAL_GROUP, + GTYPE_SECURITY_GLOBAL_GROUP) + attributes = ["sAMAccountName", "objectSID"] + unfiltered = self.samdb.search(expression=select, attrs=attributes) + filtered = self.filter_domain(unfiltered) + self.assertTrue(len(filtered) > 4) + + # Sort the expected results by rid + expected = sorted(list(filtered), key=rid) + + # + # Perform EnumDomainGroups with max size greater than the expected + # number of results. Allow for an extra 10 entries + # + max_size = calc_max_size(len(expected) + 10) + (resume_handle, actual, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, 0, max_size) + self.assertEqual(len(expected), num_entries) + check_results(expected, actual.entries) + + # + # Perform EnumDomainGroups with size set to so that it contains + # 4 entries. + # + max_size = calc_max_size(4) + (resume_handle, actual, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, 0, max_size) + self.assertEqual(4, num_entries) + check_results(expected[:4], actual.entries) + + # + # Try calling with resume_handle greater than number of entries + # Should return no results and a resume handle of 0 + max_size = calc_max_size(1) + rh = len(expected) + self.conn.Close(self.handle) + (resume_handle, a, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, rh, max_size) + + self.assertEqual(0, num_entries) + self.assertEqual(0, resume_handle) + + # + # Enumerate through the domain groups one element at a time. + # + max_size = calc_max_size(1) + actual = [] + (resume_handle, a, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, 0, max_size) + while resume_handle: + self.assertEqual(1, num_entries) + actual.append(a.entries[0]) + (resume_handle, a, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, resume_handle, max_size) + if num_entries: + actual.append(a.entries[0]) + + # + # Check that the cached results are being returned. + # Obtain a new resume_handle and insert new entries into the + # into the DB + # + actual = [] + max_size = calc_max_size(1) + (resume_handle, a, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, 0, max_size) + extra_dns = self.create_groups([1000, 1002, 1003, 1004]) + while resume_handle: + self.assertEqual(1, num_entries) + actual.append(a.entries[0]) + (resume_handle, a, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, resume_handle, max_size) + if num_entries: + actual.append(a.entries[0]) + + self.assertEqual(len(expected), len(actual)) + check_results(expected, actual) + + # + # Perform EnumDomainGroups, we should read the newly added domains + # + max_size = calc_max_size(len(expected) + len(extra_dns) + 10) + (resume_handle, actual, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, 0, max_size) + self.assertEqual(len(expected) + len(extra_dns), num_entries) + + # + # Get a new expected result set by querying the database directly + unfiltered01 = self.samdb.search(expression=select, attrs=attributes) + filtered01 = self.filter_domain(unfiltered01) + self.assertTrue(len(filtered01) > len(expected)) + + # Sort the expected results by rid + expected01 = sorted(list(filtered01), key=rid) + + # + # Now check that we read the new entries. + # + check_results(expected01, actual.entries) + + # + # Check that deleted results are handled correctly. + # Obtain a new resume_handle and delete entries from the DB. + # + actual = [] + max_size = calc_max_size(1) + (resume_handle, a, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, 0, max_size) + self.delete_dns(extra_dns) + while resume_handle and num_entries: + self.assertEqual(1, num_entries) + actual.append(a.entries[0]) + (resume_handle, a, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, resume_handle, max_size) + if num_entries: + actual.append(a.entries[0]) + + self.assertEqual(len(expected), len(actual)) + check_results(expected, actual) + + self.delete_dns(dns) + + def test_EnumDomainUsers(self): + def check_results(expected, actual): + for (e, a) in zip(expected, actual): + self.assertTrue(isinstance(a, samr.SamEntry)) + self.assertEqual( + str(e["sAMAccountName"]), str(a.name.string)) + + # Create four users + # to ensure that we have the minimum needed for the tests. + dns = self.create_users([1, 2, 3, 4]) + + # + # Get the expected results by querying the samdb database directly. + # We do this rather than use a list of expected results as this runs + # with other tests so we do not have a known fixed list of elements + select = "(objectClass=user)" + attributes = ["sAMAccountName", "objectSID", "userAccountConrol"] + unfiltered = self.samdb.search(expression=select, attrs=attributes) + filtered = self.filter_domain(unfiltered) + self.assertTrue(len(filtered) > 4) + + # Sort the expected results by rid + expected = sorted(list(filtered), key=rid) + + # + # Perform EnumDomainUsers with max_size greater than required for the + # expected number of results. We should get all the results. + # + max_size = calc_max_size(len(expected) + 10) + (resume_handle, actual, num_entries) = self.conn.EnumDomainUsers( + self.domain_handle, 0, 0, max_size) + self.assertEqual(len(expected), num_entries) + check_results(expected, actual.entries) + + # + # Perform EnumDomainUsers with size set to so that it contains + # 4 entries. + max_size = calc_max_size(4) + (resume_handle, actual, num_entries) = self.conn.EnumDomainUsers( + self.domain_handle, 0, 0, max_size) + self.assertEqual(4, num_entries) + check_results(expected[:4], actual.entries) + + # + # Try calling with resume_handle greater than number of entries + # Should return no results and a resume handle of 0 + rh = len(expected) + max_size = calc_max_size(1) + self.conn.Close(self.handle) + (resume_handle, a, num_entries) = self.conn.EnumDomainUsers( + self.domain_handle, rh, 0, max_size) + + self.assertEqual(0, num_entries) + self.assertEqual(0, resume_handle) + + # + # Enumerate through the domain users one element at a time. + # We should get all the results. + # + actual = [] + max_size = calc_max_size(1) + (resume_handle, a, num_entries) = self.conn.EnumDomainUsers( + self.domain_handle, 0, 0, max_size) + while resume_handle: + self.assertEqual(1, num_entries) + actual.append(a.entries[0]) + (resume_handle, a, num_entries) = self.conn.EnumDomainUsers( + self.domain_handle, resume_handle, 0, max_size) + if num_entries: + actual.append(a.entries[0]) + + self.assertEqual(len(expected), len(actual)) + check_results(expected, actual) + + # + # Check that the cached results are being returned. + # Obtain a new resume_handle and insert new entries into the + # into the DB. As the entries were added after the results were cached + # they should not show up in the returned results. + # + actual = [] + max_size = calc_max_size(1) + (resume_handle, a, num_entries) = self.conn.EnumDomainUsers( + self.domain_handle, 0, 0, max_size) + extra_dns = self.create_users([1000, 1002, 1003, 1004]) + while resume_handle: + self.assertEqual(1, num_entries) + actual.append(a.entries[0]) + (resume_handle, a, num_entries) = self.conn.EnumDomainUsers( + self.domain_handle, resume_handle, 0, max_size) + if num_entries: + actual.append(a.entries[0]) + + self.assertEqual(len(expected), len(actual)) + check_results(expected, actual) + + # + # Perform EnumDomainUsers, we should read the newly added groups + # As resume_handle is zero, the results will be read from disk. + # + max_size = calc_max_size(len(expected) + len(extra_dns) + 10) + (resume_handle, actual, num_entries) = self.conn.EnumDomainUsers( + self.domain_handle, 0, 0, max_size) + self.assertEqual(len(expected) + len(extra_dns), num_entries) + + # + # Get a new expected result set by querying the database directly + unfiltered01 = self.samdb.search(expression=select, attrs=attributes) + filtered01 = self.filter_domain(unfiltered01) + self.assertTrue(len(filtered01) > len(expected)) + + # Sort the expected results by rid + expected01 = sorted(list(filtered01), key=rid) + + # + # Now check that we read the new entries. + # + self.assertEqual(len(expected01), num_entries) + check_results(expected01, actual.entries) + + self.delete_dns(dns + extra_dns) + + def test_DomGeneralInformation_num_users(self): + info = self.conn.QueryDomainInfo( + self.domain_handle, DomainGeneralInformation) + # + # Enumerate through all the domain users and compare the number + # returned against QueryDomainInfo they should be the same + max_size = calc_max_size(1) + (resume_handle, a, num_entries) = self.conn.EnumDomainUsers( + self.domain_handle, 0, 0, max_size) + count = num_entries + while resume_handle: + self.assertEqual(1, num_entries) + (resume_handle, a, num_entries) = self.conn.EnumDomainUsers( + self.domain_handle, resume_handle, 0, max_size) + count += num_entries + + self.assertEqual(count, info.num_users) + + def test_DomGeneralInformation_num_groups(self): + info = self.conn.QueryDomainInfo( + self.domain_handle, DomainGeneralInformation) + # + # Enumerate through all the domain groups and compare the number + # returned against QueryDomainInfo they should be the same + max_size = calc_max_size(1) + (resume_handle, a, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, 0, max_size) + count = num_entries + while resume_handle: + self.assertEqual(1, num_entries) + (resume_handle, a, num_entries) = self.conn.EnumDomainGroups( + self.domain_handle, resume_handle, max_size) + count += num_entries + + self.assertEqual(count, info.num_groups) + + def test_DomGeneralInformation_num_aliases(self): + info = self.conn.QueryDomainInfo( + self.domain_handle, DomainGeneralInformation) + # + # Enumerate through all the domain aliases and compare the number + # returned against QueryDomainInfo they should be the same + max_size = calc_max_size(1) + (resume_handle, a, num_entries) = self.conn.EnumDomainAliases( + self.domain_handle, 0, max_size) + count = num_entries + while resume_handle: + self.assertEqual(1, num_entries) + (resume_handle, a, num_entries) = self.conn.EnumDomainAliases( + self.domain_handle, resume_handle, max_size) + count += num_entries + + self.assertEqual(count, info.num_aliases) diff --git a/python/samba/tests/dcerpc/samr_change_password.py b/python/samba/tests/dcerpc/samr_change_password.py new file mode 100644 index 0000000..f872bba --- /dev/null +++ b/python/samba/tests/dcerpc/samr_change_password.py @@ -0,0 +1,187 @@ +# Unix SMB/CIFS implementation. +# +# Copyright © 2020 Andreas Schneider <asn@samba.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.dcerpc.samr.password""" + +import ctypes +import samba.tests + +from samba import crypto, generate_random_password, generate_random_bytes, ntstatus +from samba.auth import system_session +from samba.credentials import Credentials +from samba.credentials import SMB_ENCRYPTION_REQUIRED +from samba.dcerpc import samr, security, lsa +from samba.samdb import SamDB +from samba.tests import RpcInterfaceTestCase + + +class SamrPasswordTests(RpcInterfaceTestCase): + def setUp(self): + super().setUp() + self.open_samdb() + + self.create_user_account(10000) + + self.remote_server = samba.tests.env_get_var_value('SERVER') + self.remote_domain = samba.tests.env_get_var_value('DOMAIN') + self.remote_user = samba.tests.env_get_var_value('USERNAME') + self.remote_password = samba.tests.env_get_var_value('PASSWORD') + self.remote_binding_string = "ncacn_np:%s[krb5]" % (self.remote_server) + + self.remote_creds = Credentials() + self.remote_creds.guess(self.lp) + self.remote_creds.set_username(self.remote_user) + self.remote_creds.set_password(self.remote_password) + + def tearDown(self): + super().tearDown() + + samr.Close(self.user_handle) + samr.Close(self.domain_handle) + samr.Close(self.handle) + + samba.tests.delete_force(self.samdb, self.user_dn) + + # + # Open the samba database + # + def open_samdb(self): + self.lp = samba.tests.env_loadparm() + + self.local_creds = Credentials() + self.local_creds.guess(self.lp) + self.session = system_session() + self.samdb = SamDB(session_info=self.session, + credentials=self.local_creds, + lp=self.lp) + + # + # Open a SAMR Domain handle + # + def open_domain_handle(self): + self.handle = self.conn.Connect2(None, + security.SEC_FLAG_MAXIMUM_ALLOWED) + + self.domain_sid = self.conn.LookupDomain(self.handle, + lsa.String(self.remote_domain)) + + self.domain_handle = self.conn.OpenDomain(self.handle, + security.SEC_FLAG_MAXIMUM_ALLOWED, + self.domain_sid) + + def open_user_handle(self): + name = lsa.String(self.user_name) + + rids = self.conn.LookupNames(self.domain_handle, [name]) + + self.user_handle = self.conn.OpenUser(self.domain_handle, + security.SEC_FLAG_MAXIMUM_ALLOWED, + rids[0].ids[0]) + # + # Create a test user account + # + def create_user_account(self, user_id): + self.user_name = ("SAMR_USER_%d" % user_id) + self.user_pass = generate_random_password(32, 32) + self.user_dn = "cn=%s,cn=users,%s" % (self.user_name, self.samdb.domain_dn()) + + samba.tests.delete_force(self.samdb, self.user_dn) + + self.samdb.newuser(self.user_name, + self.user_pass, + description="Password for " + self.user_name + " is " + self.user_pass, + givenname=self.user_name, + surname=self.user_name) + + + def init_samr_CryptPassword(self, password, session_key): + + def encode_pw_buffer(password): + data = bytearray([0] * 516) + + p = samba.string_to_byte_array(password.encode('utf-16-le')) + plen = len(p) + + b = generate_random_bytes(512 - plen) + + i = 512 - plen + data[0:i] = b + data[i:i+plen] = p + data[512:516] = plen.to_bytes(4, byteorder='little') + + return bytes(data) + + # This is a test, so always allow to encrypt using RC4 + try: + crypto.set_relax_mode() + encrypted_blob = samba.arcfour_encrypt(session_key, encode_pw_buffer(password)) + finally: + crypto.set_strict_mode() + + out_blob = samr.CryptPassword() + out_blob.data = list(encrypted_blob) + + return out_blob + + + def test_setUserInfo2_Password(self, password='P@ssw0rd'): + self.conn = samr.samr(self.remote_binding_string, + self.get_loadparm(), + self.remote_creds) + self.open_domain_handle() + self.open_user_handle() + + password='P@ssw0rd' + + level = 24 + info = samr.UserInfo24() + + info.password_expired = 0 + info.password = self.init_samr_CryptPassword(password, self.conn.session_key) + + # If the server is in FIPS mode, it should reject the password change! + try: + self.conn.SetUserInfo2(self.user_handle, level, info) + except samba.NTSTATUSError as e: + code = ctypes.c_uint32(e.args[0]).value + print(code) + if ((code == ntstatus.NT_STATUS_ACCESS_DENIED) and + (self.lp.weak_crypto == 'disallowed')): + pass + else: + raise + + + def test_setUserInfo2_Password_Encrypted(self, password='P@ssw0rd'): + self.remote_creds.set_smb_encryption(SMB_ENCRYPTION_REQUIRED) + + self.conn = samr.samr(self.remote_binding_string, + self.get_loadparm(), + self.remote_creds) + self.open_domain_handle() + self.open_user_handle() + + password='P@ssw0rd' + + level = 24 + info = samr.UserInfo24() + + info.password_expired = 0 + info.password = self.init_samr_CryptPassword(password, self.conn.session_key) + + self.conn.SetUserInfo2(self.user_handle, level, info) diff --git a/python/samba/tests/dcerpc/srvsvc.py b/python/samba/tests/dcerpc/srvsvc.py new file mode 100644 index 0000000..4baaec3 --- /dev/null +++ b/python/samba/tests/dcerpc/srvsvc.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# +# Unix SMB/CIFS implementation. +# Copyright © Dhananjay Sathe <dhanajaysathe@gmail.com> 2011 +# Copyright © Jelmer Vernooij <jelmer@samba.org> 2011 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.dcerpc.srvsvc.""" + +from samba.dcerpc import srvsvc +from samba.tests import RpcInterfaceTestCase + + +class SrvsvcTests(RpcInterfaceTestCase): + + def setUp(self): + super().setUp() + self.conn = srvsvc.srvsvc("ncalrpc:", self.get_loadparm()) + self.server_unc = "\\\\." + + def getDummyShareObject(self): + share = srvsvc.NetShareInfo2() + + share.name = u'test' + share.comment = u'test share' + share.type = srvsvc.STYPE_DISKTREE + share.current_users = 0x00000000 + share.max_users = -1 + share.password = None + share.path = u'C:\\tmp' # some random path + share.permissions = 123434566 + return share + + def test_NetShareAdd(self): + self.skipTest("Dangerous test") + share = self.getDummyShareObject() + self.conn.NetShareAdd(self.server_unc, 2, share, None) + + def test_NetShareSetInfo(self): + self.skipTest("Dangerous test") + share = self.getDummyShareObject() + parm_error = 0x00000000 + self.conn.NetShareAdd(self.server_unc, 502, share, parm_error) + name = share.name + share.comment = "now successfully modified " + parm_error = self.pipe.NetShareSetInfo(self.server_unc, name, + 502, share, parm_error) + + def test_NetShareDel(self): + self.skipTest("Dangerous test") + share = self.getDummyShareObject() + parm_error = 0x00000000 + self.expectFailure("NetShareAdd doesn't work properly from Python", + self.conn.NetShareAdd, self.server_unc, 502, share, parm_error) + self.conn.NetShareDel(self.server_unc, share.name, 0) diff --git a/python/samba/tests/dcerpc/string_tests.py b/python/samba/tests/dcerpc/string_tests.py new file mode 100644 index 0000000..a3426bb --- /dev/null +++ b/python/samba/tests/dcerpc/string_tests.py @@ -0,0 +1,132 @@ +# Unix SMB/CIFS implementation. +# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2016 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for string and unicode handling in PIDL generated bindings +samba.dcerpc.*""" + +from samba.dcerpc import drsblobs +import samba.tests +from samba.ndr import ndr_unpack, ndr_pack +import talloc +import gc + + +class TestException(Exception): + pass + + +class StringTests(samba.tests.TestCase): + + def setUp(self): + super().setUp() + talloc.enable_null_tracking() + self.startup_blocks = talloc.total_blocks() + + def tearDown(self): + super().tearDown() + gc.collect() + if talloc.total_blocks() != self.startup_blocks: + talloc.report_full() + self.fail("it appears we are leaking memory") + + def test_string_from_python(self): + info = drsblobs.repsFromTo2OtherInfo() + info.dns_name1 = "hello.example.com" + info.dns_name2 = "goodbye.example.com" + gc.collect() + self.assertIsNotNone(info) + self.assertEqual(info.dns_name1, "hello.example.com") + self.assertEqual(info.dns_name2, "goodbye.example.com") + + info.dns_name1 = "" + info.dns_name2 = "goodbye.example.com" + + self.assertEqual(info.dns_name1, "") + self.assertEqual(info.dns_name2, "goodbye.example.com") + + info.dns_name2 = None + + self.assertEqual(info.dns_name1, "") + self.assertIsNone(info.dns_name2) + + def test_string_with_exception(self): + try: + self.test_string_from_python() + raise TestException() + except TestException: + pass + + def test_string_from_python_function(self): + def get_info(): + info = drsblobs.repsFromTo2OtherInfo() + info.dns_name1 = "1.example.com" + info.dns_name2 = "2.example.com" + return info + + info = get_info() + gc.collect() + self.assertIsNotNone(info) + self.assertEqual(info.dns_name1, "1.example.com") + self.assertEqual(info.dns_name2, "2.example.com") + + def test_string_modify_in_place(self): + info = drsblobs.repsFromTo2OtherInfo() + info.dns_name1 = "1.example.com" + info.dns_name2 = "%s.example.com" + gc.collect() + self.assertIsNotNone(info) + self.assertEqual(info.dns_name1, "1.example.com") + self.assertEqual(info.dns_name2, "%s.example.com") + info.dns_name1 += ".co.nz" + info.dns_name2 %= 2 + self.assertEqual(info.dns_name1, "1.example.com.co.nz") + self.assertEqual(info.dns_name2, "2.example.com") + del info + + def test_string_delete(self): + gc.collect() + info = drsblobs.repsFromTo2OtherInfo() + info.dns_name1 = "1.example.com" + info.dns_name2 = "2.example.com" + info.dns_name1 = None + try: + del info.dns_name2 + except AttributeError: + pass + + self.assertIsNotNone(info) + self.assertIsNone(info.dns_name1) + self.assertIsNotNone(info.dns_name2) + + +class StringTestsWithoutLeakCheck(samba.tests.TestCase): + """We know that the ndr unpacking test leaves stuff in the + autofree_context, and we don't want to worry about that. So for + this test we don't make memory leak assertions.""" + + def test_string_from_ndr(self): + info = drsblobs.repsFromTo2OtherInfo() + info.dns_name1 = "1.example.com" + info.dns_name2 = "2.example.com" + packed = ndr_pack(info) + gc.collect() + + info_unpacked = ndr_unpack(drsblobs.repsFromTo2OtherInfo, packed) + + self.assertIsNotNone(info_unpacked) + self.assertEqual(info_unpacked.dns_name1, "1.example.com") + self.assertEqual(info_unpacked.dns_name2, "2.example.com") diff --git a/python/samba/tests/dcerpc/testrpc.py b/python/samba/tests/dcerpc/testrpc.py new file mode 100644 index 0000000..789ea9a --- /dev/null +++ b/python/samba/tests/dcerpc/testrpc.py @@ -0,0 +1,143 @@ +# test generated python code from pidl +# Copyright (C) Andrew Tridgell August 2010 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +import sys + +sys.path.insert(0, "bin/python") + +import samba +import samba.tests +from samba.dcerpc import drsuapi +import talloc + +talloc.enable_null_tracking() + + +class RpcTests(object): + """test type behaviour of pidl generated python RPC code""" + + def check_blocks(self, object, num_expected): + """check that the number of allocated blocks is correct""" + nblocks = talloc.total_blocks(object) + if object is None: + nblocks -= self.initial_blocks + leaked_blocks = (nblocks - num_expected) + if leaked_blocks != 0: + print("Leaked %d blocks" % leaked_blocks) + + def check_type(self, interface, typename, type): + print("Checking type %s" % typename) + v = type() + for n in dir(v): + if n[0] == '_': + continue + try: + value = getattr(v, n) + except TypeError as errstr: + if str(errstr) == "unknown union level": + print("ERROR: Unknown union level in %s.%s" % (typename, n)) + self.errcount += 1 + continue + print(str(errstr)[1:21]) + if str(errstr)[0:21] == "Can not convert C Type": + print("ERROR: Unknown C type for %s.%s" % (typename, n)) + self.errcount += 1 + continue + else: + print("ERROR: Failed to instantiate %s.%s" % (typename, n)) + self.errcount += 1 + continue + except Exception: + print("ERROR: Failed to instantiate %s.%s" % (typename, n)) + self.errcount += 1 + continue + + # now try setting the value back + try: + print("Setting %s.%s" % (typename, n)) + setattr(v, n, value) + except Exception as e: + if isinstance(e, AttributeError) and str(e).endswith("is read-only"): + # readonly, ignore + continue + else: + print("ERROR: Failed to set %s.%s: %r: %s" % (typename, n, e.__class__, e)) + self.errcount += 1 + continue + + # and try a comparison + try: + if value != getattr(v, n): + print("ERROR: Comparison failed for %s.%s: %r != %r" % (typename, n, value, getattr(v, n))) + continue + except Exception as e: + print("ERROR: compare exception for %s.%s: %r: %s" % (typename, n, e.__class__, e)) + continue + + def check_interface(self, interface, iname): + errcount = self.errcount + for n in dir(interface): + if n[0] == '_' or n == iname: + # skip the special ones + continue + value = getattr(interface, n) + if isinstance(value, str): + # print "%s=\"%s\"" % (n, value) + pass + elif isinstance(value, int): + # print "%s=%d" % (n, value) + pass + elif isinstance(value, type): + try: + initial_blocks = talloc.total_blocks(None) + self.check_type(interface, n, value) + self.check_blocks(None, initial_blocks) + except Exception as e: + print("ERROR: Failed to check_type %s.%s: %r: %s" % (iname, n, e.__class__, e)) + self.errcount += 1 + elif callable(value): + pass # Method + else: + print("UNKNOWN: %s=%s" % (n, value)) + if self.errcount - errcount != 0: + print("Found %d errors in %s" % (self.errcount - errcount, iname)) + + def check_all_interfaces(self): + for iname in dir(samba.dcerpc): + if iname[0] == '_': + continue + if iname == 'ClientConnection' or iname == 'base': + continue + print("Checking interface %s" % iname) + iface = getattr(samba.dcerpc, iname) + initial_blocks = talloc.total_blocks(None) + self.check_interface(iface, iname) + self.check_blocks(None, initial_blocks) + + def run(self): + self.initial_blocks = talloc.total_blocks(None) + self.errcount = 0 + self.check_all_interfaces() + return self.errcount + + +tests = RpcTests() +errcount = tests.run() +if errcount == 0: + sys.exit(0) +else: + print("%d failures" % errcount) + sys.exit(1) diff --git a/python/samba/tests/dcerpc/unix.py b/python/samba/tests/dcerpc/unix.py new file mode 100644 index 0000000..b7fa1f3 --- /dev/null +++ b/python/samba/tests/dcerpc/unix.py @@ -0,0 +1,43 @@ +# Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +"""Tests for samba.dcerpc.unixinfo.""" + + +from samba.dcerpc import unixinfo +from samba.tests import RpcInterfaceTestCase + +class UnixinfoTests(RpcInterfaceTestCase): + + def setUp(self): + super().setUp() + self.conn = unixinfo.unixinfo("ncalrpc:", self.get_loadparm()) + + def test_getpwuid_int(self): + infos = self.conn.GetPWUid(range(512)) + self.assertEqual(512, len(infos)) + self.assertEqual("/bin/false", infos[0].shell) + self.assertTrue(isinstance(infos[0].homedir, str)) + + def test_gidtosid(self): + self.conn.GidToSid(1000) + + def test_uidtosid(self): + self.conn.UidToSid(1000) + + def test_uidtosid_fail(self): + self.assertRaises(TypeError, self.conn.UidToSid, "100") |