class DrsRodcTestCase(drs_base.DrsBaseTestCase): """Intended as a semi-black box test case for replication involving an RODC.""" def setUp(self): super(DrsRodcTestCase, self).setUp() self.base_dn = self.ldb_dc1.get_default_basedn() self.ou = samba.tests.create_test_ou(self.ldb_dc1, "test_drs_rodc") self.allowed_group = "CN=Allowed RODC Password Replication Group,CN=Users,%s" % self.base_dn self.site = self.ldb_dc1.server_site_name() self.rodc_name = "TESTRODCDRS%s" % random.randint(1, 10000000) self.rodc_pass = "******" self.computer_dn = "CN=%s,OU=Domain Controllers,%s" % (self.rodc_name, self.base_dn) self.rodc_ctx = DCJoinContext(server=self.ldb_dc1.host_dns_name(), creds=self.get_credentials(), lp=self.get_loadparm(), site=self.site, netbios_name=self.rodc_name, targetdir=None, domain=None, machinepass=self.rodc_pass) self._create_rodc(self.rodc_ctx) self.rodc_ctx.create_tmp_samdb() self.tmp_samdb = self.rodc_ctx.tmp_samdb rodc_creds = Credentials() rodc_creds.guess(self.rodc_ctx.lp) rodc_creds.set_username(self.rodc_name + '$') rodc_creds.set_password(self.rodc_pass) self.rodc_creds = rodc_creds (self.drs, self.drs_handle) = self._ds_bind(self.dnsname_dc1) (self.rodc_drs, self.rodc_drs_handle) = self._ds_bind(self.dnsname_dc1, rodc_creds) def tearDown(self): self.rodc_ctx.cleanup_old_join() super(DrsRodcTestCase, self).tearDown() def test_admin_repl_secrets(self): """ When a secret attribute is set to be replicated to an RODC with the admin credentials, it should always replicate regardless of whether or not it's in the Allowed RODC Password Replication Group. """ rand = random.randint(1, 10000000) expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, drsuapi.DRSUAPI_ATTID_supplementalCredentials, drsuapi.DRSUAPI_ATTID_ntPwdHistory, drsuapi.DRSUAPI_ATTID_unicodePwd, drsuapi.DRSUAPI_ATTID_dBCSPwd] user_name = "test_rodcA_%s" % rand user_dn = "CN=%s,%s" % (user_name, self.ou) self.ldb_dc1.add({ "dn": user_dn, "objectclass": "user", "sAMAccountName": user_name }) # Store some secret on this user self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, 'penguin12#', False, user_name) req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=user_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), max_objects=133, replica_flags=0) (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) # Check that the user has been added to msDSRevealedUsers self._assert_in_revealed_users(user_dn, expected_user_attributes) def test_rodc_repl_secrets(self): """ When a secret attribute is set to be replicated to an RODC with the RODC account credentials, it should not replicate if it's in the Allowed RODC Password Replication Group. Once it is added to the group, it should replicate. """ rand = random.randint(1, 10000000) expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, drsuapi.DRSUAPI_ATTID_supplementalCredentials, drsuapi.DRSUAPI_ATTID_ntPwdHistory, drsuapi.DRSUAPI_ATTID_unicodePwd, drsuapi.DRSUAPI_ATTID_dBCSPwd] user_name = "test_rodcB_%s" % rand user_dn = "CN=%s,%s" % (user_name, self.ou) self.ldb_dc1.add({ "dn": user_dn, "objectclass": "user", "sAMAccountName": user_name }) # Store some secret on this user self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, 'penguin12#', False, user_name) req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=user_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), max_objects=133, replica_flags=0) try: (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") except WERRORError as e: (enum, estr) = e.args self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED # send the same request again and we should get the same response try: (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") except WERRORError as e1: (enum, estr) = e1.args self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED # Retry with Administrator credentials, ignores password replication groups (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) # Check that the user has been added to msDSRevealedUsers self._assert_in_revealed_users(user_dn, expected_user_attributes) def test_rodc_repl_secrets_follow_on_req(self): """ Checks that an RODC can't subvert an existing (valid) GetNCChanges request to reveal secrets it shouldn't have access to. """ # send an acceptable request that will match as many GUIDs as possible. # Here we set the SPECIAL_SECRET_PROCESSING flag so that the request gets accepted. # (On the server, this builds up the getnc_state->guids array) req8 = self._exop_req8(dest_dsa=str(self.rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=self.ldb_dc1.domain_dn(), exop=drsuapi.DRSUAPI_EXOP_NONE, max_objects=1, replica_flags=drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 8, req8) # Get the next replication chunk, but set REPL_SECRET this time. This # is following on the the previous accepted request, but we've changed # exop to now request secrets. This request should fail try: req8 = self._exop_req8(dest_dsa=str(self.rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=self.ldb_dc1.domain_dn(), exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET) req8.highwatermark = ctr.new_highwatermark (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 8, req8) self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") except RuntimeError as e2: (enum, estr) = e2.args pass def test_msDSRevealedUsers_admin(self): """ When a secret attribute is to be replicated to an RODC, the contents of the attribute should be added to the msDSRevealedUsers attribute of the computer object corresponding to the RODC. """ rand = random.randint(1, 10000000) expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, drsuapi.DRSUAPI_ATTID_supplementalCredentials, drsuapi.DRSUAPI_ATTID_ntPwdHistory, drsuapi.DRSUAPI_ATTID_unicodePwd, drsuapi.DRSUAPI_ATTID_dBCSPwd] # Add a user on DC1, add it to allowed password replication # group, and replicate to RODC with EXOP_REPL_SECRETS user_name = "test_rodcC_%s" % rand password = "******" user_dn = "CN=%s,%s" % (user_name, self.ou) self.ldb_dc1.add({ "dn": user_dn, "objectclass": "user", "sAMAccountName": user_name }) # Store some secret on this user self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password, False, user_name) self.ldb_dc1.add_remove_group_members("Allowed RODC Password Replication Group", [user_name], add_members_operation=True) req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=user_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), max_objects=133, replica_flags=0) (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) # Check that the user has been added to msDSRevealedUsers (packed_attrs_1, unpacked_attrs_1) = self._assert_in_revealed_users(user_dn, expected_user_attributes) # Change the user's password on DC1 self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password + "1", False, user_name) (packed_attrs_2, unpacked_attrs_2) = self._assert_in_revealed_users(user_dn, expected_user_attributes) self._assert_attrlist_equals(unpacked_attrs_1, unpacked_attrs_2) # Replicate to RODC again with EXOP_REPL_SECRETS req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=user_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), max_objects=133, replica_flags=0) (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) # This is important for Windows, because the entry won't have been # updated in time if we don't have it. Even with this sleep, it only # passes some of the time... time.sleep(5) # Check that the entry in msDSRevealedUsers has been updated (packed_attrs_3, unpacked_attrs_3) = self._assert_in_revealed_users(user_dn, expected_user_attributes) self._assert_attrlist_changed(unpacked_attrs_2, unpacked_attrs_3, expected_user_attributes) # We should be able to delete the user self.ldb_dc1.deleteuser(user_name) res = self.ldb_dc1.search(scope=ldb.SCOPE_BASE, base=self.computer_dn, attrs=["msDS-RevealedUsers"]) self.assertFalse("msDS-RevealedUsers" in res[0]) def test_msDSRevealedUsers(self): """ When a secret attribute is to be replicated to an RODC, the contents of the attribute should be added to the msDSRevealedUsers attribute of the computer object corresponding to the RODC. """ rand = random.randint(1, 10000000) expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, drsuapi.DRSUAPI_ATTID_supplementalCredentials, drsuapi.DRSUAPI_ATTID_ntPwdHistory, drsuapi.DRSUAPI_ATTID_unicodePwd, drsuapi.DRSUAPI_ATTID_dBCSPwd] # Add a user on DC1, add it to allowed password replication # group, and replicate to RODC with EXOP_REPL_SECRETS user_name = "test_rodcD_%s" % rand password = "******" user_dn = "CN=%s,%s" % (user_name, self.ou) self.ldb_dc1.add({ "dn": user_dn, "objectclass": "user", "sAMAccountName": user_name }) # Store some secret on this user self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password, False, user_name) self.ldb_dc1.add_remove_group_members("Allowed RODC Password Replication Group", [user_name], add_members_operation=True) req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=user_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), max_objects=133, replica_flags=0) (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) # Check that the user has been added to msDSRevealedUsers (packed_attrs_1, unpacked_attrs_1) = self._assert_in_revealed_users(user_dn, expected_user_attributes) # Change the user's password on DC1 self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password + "1", False, user_name) (packed_attrs_2, unpacked_attrs_2) = self._assert_in_revealed_users(user_dn, expected_user_attributes) self._assert_attrlist_equals(unpacked_attrs_1, unpacked_attrs_2) # Replicate to RODC again with EXOP_REPL_SECRETS req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=user_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), max_objects=133, replica_flags=0) (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) # This is important for Windows, because the entry won't have been # updated in time if we don't have it. Even with this sleep, it only # passes some of the time... time.sleep(5) # Check that the entry in msDSRevealedUsers has been updated (packed_attrs_3, unpacked_attrs_3) = self._assert_in_revealed_users(user_dn, expected_user_attributes) self._assert_attrlist_changed(unpacked_attrs_2, unpacked_attrs_3, expected_user_attributes) # We should be able to delete the user self.ldb_dc1.deleteuser(user_name) res = self.ldb_dc1.search(scope=ldb.SCOPE_BASE, base=self.computer_dn, attrs=["msDS-RevealedUsers"]) self.assertFalse("msDS-RevealedUsers" in res[0]) def test_msDSRevealedUsers_pas(self): """ If we provide a Partial Attribute Set when replicating to an RODC, we should ignore it and replicate all of the secret attributes anyway msDSRevealedUsers attribute. """ rand = random.randint(1, 10000000) expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, drsuapi.DRSUAPI_ATTID_supplementalCredentials, drsuapi.DRSUAPI_ATTID_ntPwdHistory, drsuapi.DRSUAPI_ATTID_unicodePwd, drsuapi.DRSUAPI_ATTID_dBCSPwd] pas_exceptions = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, drsuapi.DRSUAPI_ATTID_supplementalCredentials, drsuapi.DRSUAPI_ATTID_ntPwdHistory, drsuapi.DRSUAPI_ATTID_dBCSPwd] # Add a user on DC1, add it to allowed password replication # group, and replicate to RODC with EXOP_REPL_SECRETS user_name = "test_rodcE_%s" % rand password = "******" user_dn = "CN=%s,%s" % (user_name, self.ou) self.ldb_dc1.add({ "dn": user_dn, "objectclass": "user", "sAMAccountName": user_name }) # Store some secret on this user self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password, False, user_name) self.ldb_dc1.add_remove_group_members("Allowed RODC Password Replication Group", [user_name], add_members_operation=True) pas = drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb, exceptions=pas_exceptions) req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=user_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, partial_attribute_set=pas, max_objects=133, replica_flags=0) (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req10) # Make sure that we still replicate the secrets for attribute in ctr.first_object.object.attribute_ctr.attributes: if attribute.attid in pas_exceptions: pas_exceptions.remove(attribute.attid) for attribute in pas_exceptions: self.fail("%d was not replicated even though the partial attribute set should be ignored." % attribute) # Check that the user has been added to msDSRevealedUsers (packed_attrs_1, unpacked_attrs_1) = self._assert_in_revealed_users(user_dn, expected_user_attributes) def test_msDSRevealedUsers_using_other_RODC(self): """ Ensure that the machine account is tied to the destination DSA. """ # Create a new identical RODC with just the first letter missing other_rodc_name = self.rodc_name[1:] other_rodc_ctx = DCJoinContext(server=self.ldb_dc1.host_dns_name(), creds=self.get_credentials(), lp=self.get_loadparm(), site=self.site, netbios_name=other_rodc_name, targetdir=None, domain=None, machinepass=self.rodc_pass) self._create_rodc(other_rodc_ctx) other_rodc_creds = Credentials() other_rodc_creds.guess(other_rodc_ctx.lp) other_rodc_creds.set_username(other_rodc_name + '$') other_rodc_creds.set_password(self.rodc_pass) (other_rodc_drs, other_rodc_drs_handle) = self._ds_bind(self.dnsname_dc1, other_rodc_creds) rand = random.randint(1, 10000000) expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, drsuapi.DRSUAPI_ATTID_supplementalCredentials, drsuapi.DRSUAPI_ATTID_ntPwdHistory, drsuapi.DRSUAPI_ATTID_unicodePwd, drsuapi.DRSUAPI_ATTID_dBCSPwd] user_name = "test_rodcF_%s" % rand user_dn = "CN=%s,%s" % (user_name, self.ou) self.ldb_dc1.add({ "dn": user_dn, "objectclass": "user", "sAMAccountName": user_name }) # Store some secret on this user self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, 'penguin12#', False, user_name) self.ldb_dc1.add_remove_group_members("Allowed RODC Password Replication Group", [user_name], add_members_operation=True) req10 = self._getnc_req10(dest_dsa=str(other_rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=user_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), max_objects=133, replica_flags=0) try: (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") except WERRORError as e3: (enum, estr) = e3.args self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=user_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), max_objects=133, replica_flags=0) try: (level, ctr) = other_rodc_drs.DsGetNCChanges(other_rodc_drs_handle, 10, req10) self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") except WERRORError as e4: (enum, estr) = e4.args self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED def test_msDSRevealedUsers_local_deny_allow(self): """ Ensure that the deny trumps allow, and we can modify these attributes directly instead of the global groups. This may fail on Windows due to tokenGroup calculation caching. """ rand = random.randint(1, 10000000) expected_user_attributes = [drsuapi.DRSUAPI_ATTID_lmPwdHistory, drsuapi.DRSUAPI_ATTID_supplementalCredentials, drsuapi.DRSUAPI_ATTID_ntPwdHistory, drsuapi.DRSUAPI_ATTID_unicodePwd, drsuapi.DRSUAPI_ATTID_dBCSPwd] # Add a user on DC1, add it to allowed password replication # group, and replicate to RODC with EXOP_REPL_SECRETS user_name = "test_rodcF_%s" % rand password = "******" user_dn = "CN=%s,%s" % (user_name, self.ou) self.ldb_dc1.add({ "dn": user_dn, "objectclass": "user", "sAMAccountName": user_name }) # Store some secret on this user self.ldb_dc1.setpassword("(sAMAccountName=%s)" % user_name, password, False, user_name) req10 = self._getnc_req10(dest_dsa=str(self.rodc_ctx.ntds_guid), invocation_id=self.ldb_dc1.get_invocation_id(), nc_dn_str=user_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, partial_attribute_set=drs_get_rodc_partial_attribute_set(self.ldb_dc1, self.tmp_samdb), max_objects=133, replica_flags=0) m = ldb.Message() m.dn = ldb.Dn(self.ldb_dc1, self.computer_dn) m["msDS-RevealOnDemandGroup"] = \ ldb.MessageElement(user_dn, ldb.FLAG_MOD_ADD, "msDS-RevealOnDemandGroup") self.ldb_dc1.modify(m) # In local allow, should be success try: (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) except: self.fail("Should have succeeded when in local allow group") self._assert_in_revealed_users(user_dn, expected_user_attributes) (self.rodc_drs, self.rodc_drs_handle) = self._ds_bind(self.dnsname_dc1, self.rodc_creds) m = ldb.Message() m.dn = ldb.Dn(self.ldb_dc1, self.computer_dn) m["msDS-NeverRevealGroup"] = \ ldb.MessageElement(user_dn, ldb.FLAG_MOD_ADD, "msDS-NeverRevealGroup") self.ldb_dc1.modify(m) # In local allow and deny, should be failure try: (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") except WERRORError as e5: (enum, estr) = e5.args self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED m = ldb.Message() m.dn = ldb.Dn(self.ldb_dc1, self.computer_dn) m["msDS-RevealOnDemandGroup"] = \ ldb.MessageElement(user_dn, ldb.FLAG_MOD_DELETE, "msDS-RevealOnDemandGroup") self.ldb_dc1.modify(m) # In local deny, should be failure (self.rodc_drs, self.rodc_drs_handle) = self._ds_bind(self.dnsname_dc1, self.rodc_creds) try: (level, ctr) = self.rodc_drs.DsGetNCChanges(self.rodc_drs_handle, 10, req10) self.fail("Successfully replicated secrets to an RODC that shouldn't have been replicated.") except WERRORError as e6: (enum, estr) = e6.args self.assertEqual(enum, 8630) # ERROR_DS_DRA_SECRETS_DENIED def _assert_in_revealed_users(self, user_dn, attrlist): res = self.ldb_dc1.search(scope=ldb.SCOPE_BASE, base=self.computer_dn, attrs=["msDS-RevealedUsers"]) revealed_users = res[0]["msDS-RevealedUsers"] actual_attrids = [] packed_attrs = [] unpacked_attrs = [] for attribute in revealed_users: attribute = attribute.decode('utf8') dsdb_dn = dsdb_Dn(self.ldb_dc1, attribute) metadata = ndr_unpack(drsblobs.replPropertyMetaData1, dsdb_dn.get_bytes()) if user_dn in attribute: unpacked_attrs.append(metadata) packed_attrs.append(dsdb_dn.get_bytes()) actual_attrids.append(metadata.attid) self.assertEqual(sorted(actual_attrids), sorted(attrlist)) return (packed_attrs, unpacked_attrs) def _assert_attrlist_equals(self, list_1, list_2): return self._assert_attrlist_changed(list_1, list_2, [], num_changes=0, expected_new_usn=False) def _assert_attrlist_changed(self, list_1, list_2, changed_attributes, num_changes=1, expected_new_usn=True): for i in range(len(list_2)): self.assertEqual(list_1[i].attid, list_2[i].attid) self.assertEqual(list_1[i].originating_invocation_id, list_2[i].originating_invocation_id) self.assertEqual(list_1[i].version + num_changes, list_2[i].version) if expected_new_usn: self.assertTrue(list_1[i].originating_usn < list_2[i].originating_usn) self.assertTrue(list_1[i].local_usn < list_2[i].local_usn) else: self.assertEqual(list_1[i].originating_usn, list_2[i].originating_usn) self.assertEqual(list_1[i].local_usn, list_2[i].local_usn) if list_1[i].attid in changed_attributes: # We do the changes too quickly, so unless we put sleeps # inbetween calls, these remain the same. Checking the USNs # is enough. pass #self.assertTrue(list_1[i].originating_change_time < list_2[i].originating_change_time) else: self.assertEqual(list_1[i].originating_change_time, list_2[i].originating_change_time) def _create_rodc(self, ctx): ctx.nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn] ctx.full_nc_list = [ctx.base_dn, ctx.config_dn, ctx.schema_dn] ctx.krbtgt_dn = "CN=krbtgt_%s,CN=Users,%s" % (ctx.myname, ctx.base_dn) ctx.never_reveal_sid = ["<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_DENY), "<SID=%s>" % security.SID_BUILTIN_ADMINISTRATORS, "<SID=%s>" % security.SID_BUILTIN_SERVER_OPERATORS, "<SID=%s>" % security.SID_BUILTIN_BACKUP_OPERATORS, "<SID=%s>" % security.SID_BUILTIN_ACCOUNT_OPERATORS] ctx.reveal_sid = "<SID=%s-%s>" % (ctx.domsid, security.DOMAIN_RID_RODC_ALLOW) mysid = ctx.get_mysid() admin_dn = "<SID=%s>" % mysid ctx.managedby = admin_dn ctx.userAccountControl = (samba.dsdb.UF_WORKSTATION_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION | samba.dsdb.UF_PARTIAL_SECRETS_ACCOUNT) ctx.connection_dn = "CN=RODC Connection (FRS),%s" % ctx.ntds_dn ctx.secure_channel_type = misc.SEC_CHAN_RODC ctx.RODC = True ctx.replica_flags = (drsuapi.DRSUAPI_DRS_INIT_SYNC | drsuapi.DRSUAPI_DRS_PER_SYNC | drsuapi.DRSUAPI_DRS_GET_ANC | drsuapi.DRSUAPI_DRS_NEVER_SYNCED | drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) ctx.join_add_objects()
class JoinTestCase(DNSTKeyTest): def setUp(self): self.server = samba.tests.env_get_var_value("SERVER") self.server_ip = samba.tests.env_get_var_value("SERVER_IP") super(JoinTestCase, self).setUp() self.lp = samba.tests.env_loadparm() self.creds = self.get_credentials() self.netbios_name = "jointest1" logger = get_logger() self.join_ctx = DCJoinContext(server=self.server, creds=self.creds, lp=self.get_loadparm(), netbios_name=self.netbios_name, targetdir=self.tempdir, domain=None, logger=logger, dns_backend="SAMBA_INTERNAL") self.join_ctx.userAccountControl = ( samba.dsdb.UF_SERVER_TRUST_ACCOUNT | samba.dsdb.UF_TRUSTED_FOR_DELEGATION) self.join_ctx.replica_flags |= ( drsuapi.DRSUAPI_DRS_WRIT_REP | drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) self.join_ctx.domain_replica_flags = self.join_ctx.replica_flags self.join_ctx.secure_channel_type = misc.SEC_CHAN_BDC self.join_ctx.cleanup_old_join() self.join_ctx.force_all_ips = True self.join_ctx.do_join() def tearDown(self): try: paths = self.join_ctx.paths except AttributeError: paths = None if paths is not None: shutil.rmtree(paths.private_dir) shutil.rmtree(paths.state_dir) shutil.rmtree(os.path.join(self.tempdir, "etc")) shutil.rmtree(os.path.join(self.tempdir, "msg.lock")) os.unlink(os.path.join(self.tempdir, "names.tdb")) shutil.rmtree(os.path.join(self.tempdir, "bind-dns")) self.join_ctx.cleanup_old_join(force=True) super(JoinTestCase, self).tearDown() def test_join_makes_records(self): "create a query packet containing one query record via TCP" p = self.make_name_packet(dns.DNS_OPCODE_QUERY) questions = [] name = self.join_ctx.dnshostname q = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN) questions.append(q) # Get expected IPs IPs = samba.interface_ips(self.lp) self.finish_name_packet(p, questions) (response, response_packet) = self.dns_transaction_tcp(p, host=self.server_ip) self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY) self.assertEquals(response.ancount, len(IPs)) questions = [] name = "%s._msdcs.%s" % (self.join_ctx.ntds_guid, self.join_ctx.dnsforest) q = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN) questions.append(q) self.finish_name_packet(p, questions) (response, response_packet) = self.dns_transaction_tcp(p, host=self.server_ip) self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY) self.assertEquals(response.ancount, 1 + len(IPs)) self.assertEquals(response.answers[0].rr_type, dns.DNS_QTYPE_CNAME) self.assertEquals(response.answers[0].rdata, self.join_ctx.dnshostname) self.assertEquals(response.answers[1].rr_type, dns.DNS_QTYPE_A) def test_join_records_can_update(self): dc_creds = Credentials() dc_creds.guess(self.join_ctx.lp) dc_creds.set_machine_account(self.join_ctx.lp) self.tkey_trans(creds=dc_creds) p = self.make_name_packet(dns.DNS_OPCODE_UPDATE) q = self.make_name_question(self.join_ctx.dnsdomain, dns.DNS_QTYPE_SOA, dns.DNS_QCLASS_IN) questions = [] questions.append(q) self.finish_name_packet(p, questions) updates = [] # Delete the old expected IPs IPs = samba.interface_ips(self.lp) for IP in IPs[1:]: if ":" in IP: r = dns.res_rec() r.name = self.join_ctx.dnshostname r.rr_type = dns.DNS_QTYPE_AAAA r.rr_class = dns.DNS_QCLASS_NONE r.ttl = 0 r.length = 0xffff rdata = IP else: r = dns.res_rec() r.name = self.join_ctx.dnshostname r.rr_type = dns.DNS_QTYPE_A r.rr_class = dns.DNS_QCLASS_NONE r.ttl = 0 r.length = 0xffff rdata = IP r.rdata = rdata updates.append(r) p.nscount = len(updates) p.nsrecs = updates mac = self.sign_packet(p, self.key_name) (response, response_p) = self.dns_transaction_udp(p, self.server_ip) self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) self.verify_packet(response, response_p, mac) p = self.make_name_packet(dns.DNS_OPCODE_QUERY) questions = [] name = self.join_ctx.dnshostname q = self.make_name_question(name, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN) questions.append(q) self.finish_name_packet(p, questions) (response, response_packet) = self.dns_transaction_tcp(p, host=self.server_ip) self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) self.assert_dns_opcode_equals(response, dns.DNS_OPCODE_QUERY) self.assertEquals(response.ancount, 1)