ctdb-scripts: Support storing statd-callout state in cluster filesystem
[samba4-gss.git] / source4 / torture / drs / python / getnc_exop.py
blob03b9365745819a83bc1a1306b8f5b47e948c5ee3
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
4 # Tests various schema replication scenarios
6 # Copyright (C) Kamen Mazdrashki <kamenim@samba.org> 2011
7 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2016
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # Usage:
25 # export DC1=dc1_dns_name
26 # export DC2=dc2_dns_name
27 # export SUBUNITRUN=$samba4srcdir/scripting/bin/subunitrun
28 # PYTHONPATH="$PYTHONPATH:$samba4srcdir/torture/drs/python" $SUBUNITRUN getnc_exop -U"$DOMAIN/$DC_USERNAME"%"$DC_PASSWORD"
31 import random
33 import drs_base
34 from drs_base import AbstractLink
36 import samba.tests
37 from samba import werror, WERRORError
39 import ldb
40 from ldb import SCOPE_BASE
42 from samba.dcerpc import drsuapi, misc, drsblobs
43 from samba.drs_utils import drs_DsBind
44 from samba.ndr import ndr_unpack, ndr_pack
45 from functools import cmp_to_key
46 from samba.common import cmp
49 def _linked_attribute_compare(la1, la2):
50 """See CompareLinks() in MS-DRSR section 4.1.10.5.17"""
51 la1, la1_target = la1
52 la2, la2_target = la2
54 # Ascending host object GUID
55 c = cmp(ndr_pack(la1.identifier.guid), ndr_pack(la2.identifier.guid))
56 if c != 0:
57 return c
59 # Ascending attribute ID
60 if la1.attid != la2.attid:
61 return -1 if la1.attid < la2.attid else 1
63 la1_active = la1.flags & drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
64 la2_active = la2.flags & drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
66 # Ascending 'is present'
67 if la1_active != la2_active:
68 return 1 if la1_active else -1
70 # Ascending target object GUID
71 return cmp(ndr_pack(la1_target), ndr_pack(la2_target))
74 class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase):
75 """Intended as a semi-black box test case for DsGetNCChanges
76 implementation for extended operations. It should be testing
77 how DsGetNCChanges handles different input params (mostly invalid).
78 Final goal is to make DsGetNCChanges as binary compatible to
79 Windows implementation as possible"""
81 def setUp(self):
82 super(DrsReplicaSyncTestCase, self).setUp()
83 self.base_dn = self.ldb_dc1.get_default_basedn()
84 self.ou = "OU=test_getncchanges%d,%s" % (random.randint(0, 4294967295),
85 self.base_dn)
86 self.ldb_dc1.add({
87 "dn": self.ou,
88 "objectclass": "organizationalUnit"})
89 (self.drs, self.drs_handle) = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
90 (self.default_hwm, self.default_utdv) = self._get_highest_hwm_utdv(self.ldb_dc1)
92 def tearDown(self):
93 try:
94 self.ldb_dc1.delete(self.ou, ["tree_delete:1"])
95 except ldb.LdbError as e:
96 (enum, string) = e.args
97 if enum == ldb.ERR_NO_SUCH_OBJECT:
98 pass
99 super(DrsReplicaSyncTestCase, self).tearDown()
101 def _determine_fSMORoleOwner(self, fsmo_obj_dn):
102 """Returns (owner, not_owner) pair where:
103 owner: dns name for FSMO owner
104 not_owner: dns name for DC not owning the FSMO"""
105 # collect info to return later
106 fsmo_info_1 = {"dns_name": self.dnsname_dc1,
107 "invocation_id": self.ldb_dc1.get_invocation_id(),
108 "ntds_guid": self.ldb_dc1.get_ntds_GUID(),
109 "server_dn": self.ldb_dc1.get_serverName()}
110 fsmo_info_2 = {"dns_name": self.dnsname_dc2,
111 "invocation_id": self.ldb_dc2.get_invocation_id(),
112 "ntds_guid": self.ldb_dc2.get_ntds_GUID(),
113 "server_dn": self.ldb_dc2.get_serverName()}
115 msgs = self.ldb_dc1.search(scope=ldb.SCOPE_BASE, base=fsmo_info_1["server_dn"], attrs=["serverReference"])
116 fsmo_info_1["server_acct_dn"] = ldb.Dn(self.ldb_dc1, msgs[0]["serverReference"][0].decode('utf8'))
117 fsmo_info_1["rid_set_dn"] = ldb.Dn(self.ldb_dc1, "CN=RID Set") + fsmo_info_1["server_acct_dn"]
119 msgs = self.ldb_dc2.search(scope=ldb.SCOPE_BASE, base=fsmo_info_2["server_dn"], attrs=["serverReference"])
120 fsmo_info_2["server_acct_dn"] = ldb.Dn(self.ldb_dc2, msgs[0]["serverReference"][0].decode('utf8'))
121 fsmo_info_2["rid_set_dn"] = ldb.Dn(self.ldb_dc2, "CN=RID Set") + fsmo_info_2["server_acct_dn"]
123 # determine the owner dc
124 res = self.ldb_dc1.search(fsmo_obj_dn,
125 scope=SCOPE_BASE, attrs=["fSMORoleOwner"])
126 assert len(res) == 1, "Only one fSMORoleOwner value expected for %s!" % fsmo_obj_dn
127 fsmo_owner = res[0]["fSMORoleOwner"][0]
128 if fsmo_owner == self.info_dc1["dsServiceName"][0]:
129 return (fsmo_info_1, fsmo_info_2)
130 return (fsmo_info_2, fsmo_info_1)
132 def _check_exop_failed(self, ctr6, expected_failure):
133 self.assertEqual(ctr6.extended_ret, expected_failure)
134 #self.assertEqual(ctr6.object_count, 0)
135 #self.assertEqual(ctr6.first_object, None)
136 self.assertEqual(ctr6.more_data, False)
137 self.assertEqual(ctr6.nc_object_count, 0)
138 self.assertEqual(ctr6.nc_linked_attributes_count, 0)
139 self.assertEqual(ctr6.linked_attributes_count, 0)
140 self.assertEqual(ctr6.linked_attributes, [])
141 self.assertEqual(ctr6.drs_error[0], 0)
143 def test_do_single_repl(self):
145 Make sure that DRSUAPI_EXOP_REPL_OBJ never replicates more than
146 one object, even when we use DRS_GET_ANC/GET_TGT.
149 ou1 = "OU=get_anc1,%s" % self.ou
150 self.ldb_dc1.add({
151 "dn": ou1,
152 "objectclass": "organizationalUnit"
154 ou1_id = self._get_identifier(self.ldb_dc1, ou1)
155 ou2 = "OU=get_anc2,%s" % ou1
156 self.ldb_dc1.add({
157 "dn": ou2,
158 "objectclass": "organizationalUnit"
160 ou2_id = self._get_identifier(self.ldb_dc1, ou2)
161 dc3 = "CN=test_anc_dc_%u,%s" % (random.randint(0, 4294967295), ou2)
162 self.ldb_dc1.add({
163 "dn": dc3,
164 "objectclass": "computer",
165 "userAccountControl": "%d" % (samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_SERVER_TRUST_ACCOUNT)
167 dc3_id = self._get_identifier(self.ldb_dc1, dc3)
169 # Add some linked attributes (for checking GET_TGT behaviour)
170 m = ldb.Message()
171 m.dn = ldb.Dn(self.ldb_dc2, ou1)
172 m["managedBy"] = ldb.MessageElement(ou2, ldb.FLAG_MOD_ADD, "managedBy")
173 self.ldb_dc1.modify(m)
174 ou1_link = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
175 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
176 ou1_id.guid, ou2_id.guid)
178 m.dn = ldb.Dn(self.ldb_dc2, dc3)
179 m["managedBy"] = ldb.MessageElement(ou2, ldb.FLAG_MOD_ADD, "managedBy")
180 self.ldb_dc1.modify(m)
181 dc3_link = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
182 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
183 dc3_id.guid, ou2_id.guid)
185 req = self._getnc_req10(dest_dsa=None,
186 invocation_id=self.ldb_dc1.get_invocation_id(),
187 nc_dn_str=ou1,
188 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
189 replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP,
190 more_flags=drsuapi.DRSUAPI_DRS_GET_TGT)
191 (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req)
192 self._check_ctr6(ctr, [ou1], expected_links=[ou1_link])
194 # DRSUAPI_DRS_WRIT_REP means that we should only replicate the dn we give (dc3).
195 # DRSUAPI_DRS_GET_ANC means that we should also replicate its ancestors, but
196 # Windows doesn't do this if we use both.
197 req = self._getnc_req10(dest_dsa=None,
198 invocation_id=self.ldb_dc1.get_invocation_id(),
199 nc_dn_str=dc3,
200 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
201 replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP |
202 drsuapi.DRSUAPI_DRS_GET_ANC,
203 more_flags=drsuapi.DRSUAPI_DRS_GET_TGT)
204 (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req)
205 self._check_ctr6(ctr, [dc3], expected_links=[dc3_link])
207 # Even though the ancestor of ou2 (ou1) has changed since last hwm, and we're
208 # sending DRSUAPI_DRS_GET_ANC, the expected response is that it will only try
209 # and replicate the single object still.
210 req = self._getnc_req10(dest_dsa=None,
211 invocation_id=self.ldb_dc1.get_invocation_id(),
212 nc_dn_str=ou2,
213 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
214 replica_flags=drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
215 drsuapi.DRSUAPI_DRS_GET_ANC,
216 more_flags=drsuapi.DRSUAPI_DRS_GET_TGT)
217 (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 10, req)
218 self._check_ctr6(ctr, [ou2])
220 def test_do_full_repl_on_ou(self):
222 Make sure that a full replication on a not-an-nc fails with
223 the right error code
226 non_nc_ou = "OU=not-an-NC,%s" % self.ou
227 self.ldb_dc1.add({
228 "dn": non_nc_ou,
229 "objectclass": "organizationalUnit"
231 req8 = self._exop_req8(dest_dsa=None,
232 invocation_id=self.ldb_dc1.get_invocation_id(),
233 nc_dn_str=non_nc_ou,
234 exop=drsuapi.DRSUAPI_EXOP_NONE,
235 replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP)
237 try:
238 (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 8, req8)
239 self.fail("Expected DsGetNCChanges to fail with WERR_DS_CANT_FIND_EXPECTED_NC")
240 except WERRORError as e1:
241 (enum, estr) = e1.args
242 self.assertEqual(enum, werror.WERR_DS_CANT_FIND_EXPECTED_NC)
244 def test_InvalidNC_DummyDN_InvalidGUID_REPL_OBJ(self):
245 """Test single object replication on a totally invalid GUID fails with the right error code"""
246 fsmo_dn = self.ldb_dc1.get_schema_basedn()
247 (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn)
249 req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
250 invocation_id=fsmo_owner["invocation_id"],
251 nc_dn_str="DummyDN",
252 nc_guid=misc.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"),
253 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
255 (drs, drs_handle) = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
256 try:
257 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
258 except WERRORError as e1:
259 (enum, estr) = e1.args
260 self.assertEqual(enum, werror.WERR_DS_DRA_BAD_DN)
262 def test_InvalidNC_DummyDN_InvalidGUID_REPL_SECRET(self):
263 """Test single object replication on a totally invalid GUID fails with the right error code"""
264 fsmo_dn = self.ldb_dc1.get_schema_basedn()
265 (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn)
267 req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
268 invocation_id=fsmo_owner["invocation_id"],
269 nc_dn_str="DummyDN",
270 nc_guid=misc.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"),
271 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
273 (drs, drs_handle) = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
274 try:
275 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
276 except WERRORError as e1:
277 (enum, estr) = e1.args
278 self.assertEqual(enum, werror.WERR_DS_DRA_BAD_DN)
280 def test_InvalidNC_DummyDN_InvalidGUID_RID_ALLOC(self):
281 """Test RID Allocation on a totally invalid GUID fails with the right error code"""
282 fsmo_dn = self.ldb_dc1.get_schema_basedn()
283 (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn)
285 req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
286 invocation_id=fsmo_owner["invocation_id"],
287 nc_dn_str="DummyDN",
288 nc_guid=misc.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"),
289 exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC)
291 (drs, drs_handle) = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
292 try:
293 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
294 except WERRORError as e1:
295 (enum, estr) = e1.args
296 self.assertEqual(enum, werror.WERR_DS_DRA_BAD_NC)
298 def test_valid_GUID_only_REPL_OBJ(self):
299 dc_guid_1 = self.ldb_dc1.get_invocation_id()
300 drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
302 res = self.ldb_dc1.search(base=self.ou, scope=SCOPE_BASE,
303 attrs=["objectGUID"])
305 guid = misc.GUID(res[0]["objectGUID"][0])
307 req8 = self._exop_req8(dest_dsa=None,
308 invocation_id=dc_guid_1,
309 nc_dn_str="",
310 nc_guid=guid,
311 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
313 try:
314 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
315 except WERRORError as e1:
316 (enum, estr) = e1.args
317 self.fail(f"Failed to call GetNCChanges with EXOP_REPL_OBJ and a GUID: {estr}")
319 self.assertEqual(ctr.first_object.object.identifier.guid, guid)
321 def test_DummyDN_valid_GUID_REPL_OBJ(self):
322 dc_guid_1 = self.ldb_dc1.get_invocation_id()
323 drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
325 res = self.ldb_dc1.search(base=self.ou, scope=SCOPE_BASE,
326 attrs=["objectGUID"])
328 guid = misc.GUID(res[0]["objectGUID"][0])
330 req8 = self._exop_req8(dest_dsa=None,
331 invocation_id=dc_guid_1,
332 nc_dn_str="DummyDN",
333 nc_guid=guid,
334 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
336 try:
337 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
338 except WERRORError as e1:
339 (enum, estr) = e1.args
340 self.fail(f"Failed to call GetNCChanges with EXOP_REPL_OBJ, DummyDN and a GUID: {estr}")
342 self.assertEqual(ctr.first_object.object.identifier.guid, guid)
344 def test_DummyDN_valid_GUID_REPL_SECRET(self):
345 dc_guid_1 = self.ldb_dc1.get_invocation_id()
346 drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
348 res = self.ldb_dc1.search(base=self.ou, scope=SCOPE_BASE,
349 attrs=["objectGUID"])
351 guid = misc.GUID(res[0]["objectGUID"][0])
353 req8 = self._exop_req8(dest_dsa=None,
354 invocation_id=dc_guid_1,
355 nc_dn_str="DummyDN",
356 nc_guid=guid,
357 exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET)
359 try:
360 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
361 except WERRORError as e1:
362 (enum, estr) = e1.args
364 # We expect to get as far as failing on the missing dest_dsa
365 self.assertEqual(enum, werror.WERR_DS_DRA_DB_ERROR)
367 def test_link_utdv_hwm(self):
368 """Test verify the DRS_GET_ANC behavior."""
370 ou1 = "OU=get_anc1,%s" % self.ou
371 self.ldb_dc1.add({
372 "dn": ou1,
373 "objectclass": "organizationalUnit"
375 ou1_id = self._get_identifier(self.ldb_dc1, ou1)
376 ou2 = "OU=get_anc2,%s" % ou1
377 self.ldb_dc1.add({
378 "dn": ou2,
379 "objectclass": "organizationalUnit"
381 ou2_id = self._get_identifier(self.ldb_dc1, ou2)
382 dc3 = "CN=test_anc_dc_%u,%s" % (random.randint(0, 4294967295), ou2)
383 self.ldb_dc1.add({
384 "dn": dc3,
385 "objectclass": "computer",
386 "userAccountControl": "%d" % (samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_SERVER_TRUST_ACCOUNT)
388 dc3_id = self._get_identifier(self.ldb_dc1, dc3)
390 (hwm1, utdv1) = self._check_replication([ou1, ou2, dc3],
391 drsuapi.DRSUAPI_DRS_WRIT_REP)
393 self._check_replication([ou1, ou2, dc3],
394 drsuapi.DRSUAPI_DRS_WRIT_REP |
395 drsuapi.DRSUAPI_DRS_GET_ANC)
397 self._check_replication([dc3],
398 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
400 self._check_replication([ou1, ou2, dc3],
401 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
402 drsuapi.DRSUAPI_DRS_GET_ANC)
404 m = ldb.Message()
405 m.dn = ldb.Dn(self.ldb_dc1, ou1)
406 m["displayName"] = ldb.MessageElement("OU1", ldb.FLAG_MOD_ADD, "displayName")
407 self.ldb_dc1.modify(m)
409 (hwm2, utdv2) = self._check_replication([ou2, dc3, ou1],
410 drsuapi.DRSUAPI_DRS_WRIT_REP)
412 self._check_replication([ou1, ou2, dc3],
413 drsuapi.DRSUAPI_DRS_WRIT_REP |
414 drsuapi.DRSUAPI_DRS_GET_ANC)
416 self._check_replication([dc3],
417 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
419 self._check_replication([ou1, ou2, dc3],
420 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
421 drsuapi.DRSUAPI_DRS_GET_ANC)
423 self._check_replication([ou1],
424 drsuapi.DRSUAPI_DRS_WRIT_REP,
425 highwatermark=hwm1)
427 self._check_replication([ou1],
428 drsuapi.DRSUAPI_DRS_WRIT_REP |
429 drsuapi.DRSUAPI_DRS_GET_ANC,
430 highwatermark=hwm1)
432 self._check_replication([ou1],
433 drsuapi.DRSUAPI_DRS_WRIT_REP |
434 drsuapi.DRSUAPI_DRS_GET_ANC,
435 uptodateness_vector=utdv1)
437 m = ldb.Message()
438 m.dn = ldb.Dn(self.ldb_dc1, ou2)
439 m["displayName"] = ldb.MessageElement("OU2", ldb.FLAG_MOD_ADD, "displayName")
440 self.ldb_dc1.modify(m)
442 (hwm3, utdv3) = self._check_replication([dc3, ou1, ou2],
443 drsuapi.DRSUAPI_DRS_WRIT_REP)
445 self._check_replication([ou1, ou2, dc3],
446 drsuapi.DRSUAPI_DRS_WRIT_REP |
447 drsuapi.DRSUAPI_DRS_GET_ANC)
449 self._check_replication([dc3],
450 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
452 self._check_replication([ou1, ou2, dc3],
453 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
454 drsuapi.DRSUAPI_DRS_GET_ANC)
456 self._check_replication([ou1, ou2],
457 drsuapi.DRSUAPI_DRS_WRIT_REP,
458 highwatermark=hwm1)
460 self._check_replication([ou1, ou2],
461 drsuapi.DRSUAPI_DRS_WRIT_REP |
462 drsuapi.DRSUAPI_DRS_GET_ANC,
463 highwatermark=hwm1)
465 self._check_replication([ou1, ou2],
466 drsuapi.DRSUAPI_DRS_WRIT_REP |
467 drsuapi.DRSUAPI_DRS_GET_ANC,
468 uptodateness_vector=utdv1)
470 m = ldb.Message()
471 m.dn = ldb.Dn(self.ldb_dc1, self.ou)
472 m["displayName"] = ldb.MessageElement("OU", ldb.FLAG_MOD_ADD, "displayName")
473 self.ldb_dc1.modify(m)
475 (hwm4, utdv4) = self._check_replication([dc3, ou1, ou2, self.ou],
476 drsuapi.DRSUAPI_DRS_WRIT_REP)
478 self._check_replication([self.ou, ou1, ou2, dc3],
479 drsuapi.DRSUAPI_DRS_WRIT_REP |
480 drsuapi.DRSUAPI_DRS_GET_ANC)
482 self._check_replication([dc3],
483 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
485 self._check_replication([self.ou, ou1, ou2, dc3],
486 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
487 drsuapi.DRSUAPI_DRS_GET_ANC)
489 self._check_replication([self.ou, ou2],
490 drsuapi.DRSUAPI_DRS_WRIT_REP |
491 drsuapi.DRSUAPI_DRS_GET_ANC,
492 uptodateness_vector=utdv2)
494 cn3 = "CN=get_anc3,%s" % ou2
495 self.ldb_dc1.add({
496 "dn": cn3,
497 "objectclass": "container",
500 (hwm5, utdv5) = self._check_replication([dc3, ou1, ou2, self.ou, cn3],
501 drsuapi.DRSUAPI_DRS_WRIT_REP)
503 self._check_replication([self.ou, ou1, ou2, dc3, cn3],
504 drsuapi.DRSUAPI_DRS_WRIT_REP |
505 drsuapi.DRSUAPI_DRS_GET_ANC)
507 self._check_replication([dc3],
508 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
510 self._check_replication([self.ou, ou1, ou2, dc3],
511 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
512 drsuapi.DRSUAPI_DRS_GET_ANC)
514 m = ldb.Message()
515 m.dn = ldb.Dn(self.ldb_dc1, ou2)
516 m["managedBy"] = ldb.MessageElement(dc3, ldb.FLAG_MOD_ADD, "managedBy")
517 self.ldb_dc1.modify(m)
518 ou2_managedBy_dc3 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
519 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
520 ou2_id.guid, dc3_id.guid)
522 (hwm6, utdv6) = self._check_replication([dc3, ou1, self.ou, cn3, ou2],
523 drsuapi.DRSUAPI_DRS_WRIT_REP,
524 expected_links=[ou2_managedBy_dc3])
526 # Can fail against Windows due to equal precedence of dc3, cn3
527 self._check_replication([self.ou, ou1, ou2, dc3, cn3],
528 drsuapi.DRSUAPI_DRS_WRIT_REP |
529 drsuapi.DRSUAPI_DRS_GET_ANC,
530 expected_links=[ou2_managedBy_dc3])
532 self._check_replication([dc3],
533 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
535 self._check_replication([self.ou, ou1, ou2, dc3],
536 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
537 drsuapi.DRSUAPI_DRS_GET_ANC)
539 self._check_replication([],
540 drsuapi.DRSUAPI_DRS_WRIT_REP,
541 uptodateness_vector=utdv5,
542 expected_links=[ou2_managedBy_dc3])
544 self._check_replication([],
545 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
546 uptodateness_vector=utdv5)
548 self._check_replication([],
549 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
550 uptodateness_vector=utdv5)
552 m = ldb.Message()
553 m.dn = ldb.Dn(self.ldb_dc1, dc3)
554 m["managedBy"] = ldb.MessageElement(ou1, ldb.FLAG_MOD_ADD, "managedBy")
555 self.ldb_dc1.modify(m)
556 dc3_managedBy_ou1 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
557 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
558 dc3_id.guid, ou1_id.guid)
560 (hwm7, utdv7) = self._check_replication([ou1, self.ou, cn3, ou2, dc3],
561 drsuapi.DRSUAPI_DRS_WRIT_REP,
562 expected_links=[ou2_managedBy_dc3, dc3_managedBy_ou1])
564 # Can fail against Windows due to equal precedence of dc3, cn3
565 # self._check_replication([self.ou,ou1,ou2,dc3,cn3],
566 # drsuapi.DRSUAPI_DRS_WRIT_REP|
567 # drsuapi.DRSUAPI_DRS_GET_ANC,
568 # expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1])
570 self._check_replication([dc3],
571 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
572 expected_links=[dc3_managedBy_ou1])
574 self._check_replication([dc3],
575 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
576 expected_links=[dc3_managedBy_ou1])
578 self._check_replication([self.ou, ou1, ou2, dc3],
579 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
580 drsuapi.DRSUAPI_DRS_GET_ANC,
581 expected_links=[dc3_managedBy_ou1])
583 # GET_TGT seems to override DRS_CRITICAL_ONLY and also returns any
584 # object(s) that relate to the linked attributes (similar to GET_ANC)
585 self._check_replication([ou1, dc3],
586 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
587 more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
588 expected_links=[dc3_managedBy_ou1], dn_ordered=False)
590 # Change DC3's managedBy to OU2 instead of OU1
591 # Note that the OU1 managedBy linked attribute will still exist as
592 # a tombstone object (and so will be returned in the replication still)
593 m = ldb.Message()
594 m.dn = ldb.Dn(self.ldb_dc1, dc3)
595 m["managedBy"] = ldb.MessageElement(ou2, ldb.FLAG_MOD_REPLACE, "managedBy")
596 self.ldb_dc1.modify(m)
597 dc3_managedBy_ou1.flags &= ~drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
598 dc3_managedBy_ou2 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
599 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
600 dc3_id.guid, ou2_id.guid)
602 (hwm8, utdv8) = self._check_replication([ou1, self.ou, cn3, ou2, dc3],
603 drsuapi.DRSUAPI_DRS_WRIT_REP,
604 expected_links=[ou2_managedBy_dc3, dc3_managedBy_ou1, dc3_managedBy_ou2])
606 # Can fail against Windows due to equal precedence of dc3, cn3
607 # self._check_replication([self.ou,ou1,ou2,dc3,cn3],
608 # drsuapi.DRSUAPI_DRS_WRIT_REP|
609 # drsuapi.DRSUAPI_DRS_GET_ANC,
610 # expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1,dc3_managedBy_ou2])
612 self._check_replication([dc3],
613 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
614 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2])
616 self._check_replication([self.ou, ou1, ou2, dc3],
617 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
618 drsuapi.DRSUAPI_DRS_GET_ANC,
619 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2])
621 # GET_TGT will also return any DNs referenced by the linked attributes
622 # (including the Tombstone attribute)
623 self._check_replication([ou1, ou2, dc3],
624 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
625 more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
626 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2], dn_ordered=False)
628 # Use the highwater-mark prior to changing ManagedBy - this should
629 # only return the old/Tombstone and new linked attributes (we already
630 # know all the DNs)
631 self._check_replication([],
632 drsuapi.DRSUAPI_DRS_WRIT_REP,
633 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2],
634 highwatermark=hwm7)
636 self._check_replication([],
637 drsuapi.DRSUAPI_DRS_WRIT_REP |
638 drsuapi.DRSUAPI_DRS_GET_ANC,
639 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2],
640 highwatermark=hwm7)
642 self._check_replication([],
643 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
644 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2],
645 highwatermark=hwm7)
647 self._check_replication([],
648 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
649 drsuapi.DRSUAPI_DRS_GET_ANC,
650 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2],
651 highwatermark=hwm7)
653 self._check_replication([],
654 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
655 more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
656 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2],
657 highwatermark=hwm7)
659 # Repeat the above set of tests using the uptodateness_vector
660 # instead of the highwater-mark
661 self._check_replication([],
662 drsuapi.DRSUAPI_DRS_WRIT_REP,
663 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2],
664 uptodateness_vector=utdv7)
666 self._check_replication([],
667 drsuapi.DRSUAPI_DRS_WRIT_REP |
668 drsuapi.DRSUAPI_DRS_GET_ANC,
669 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2],
670 uptodateness_vector=utdv7)
672 self._check_replication([],
673 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
674 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2],
675 uptodateness_vector=utdv7)
677 self._check_replication([],
678 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
679 drsuapi.DRSUAPI_DRS_GET_ANC,
680 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2],
681 uptodateness_vector=utdv7)
683 self._check_replication([],
684 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
685 more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
686 expected_links=[dc3_managedBy_ou1, dc3_managedBy_ou2],
687 uptodateness_vector=utdv7)
689 def test_FSMONotOwner(self):
690 """Test role transfer with against DC not owner of the role"""
691 fsmo_dn = self.ldb_dc1.get_schema_basedn()
692 (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn)
694 req8 = self._exop_req8(dest_dsa=fsmo_owner["ntds_guid"],
695 invocation_id=fsmo_not_owner["invocation_id"],
696 nc_dn_str=fsmo_dn,
697 exop=drsuapi.DRSUAPI_EXOP_FSMO_REQ_ROLE)
699 (drs, drs_handle) = self._ds_bind(fsmo_not_owner["dns_name"])
700 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
701 self.assertEqual(level, 6, "Expected level 6 response!")
702 self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER)
703 self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_not_owner["ntds_guid"]))
704 self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_not_owner["invocation_id"]))
706 def test_InvalidDestDSA(self):
707 """Test role transfer with invalid destination DSA guid"""
708 fsmo_dn = self.ldb_dc1.get_schema_basedn()
709 (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn)
711 req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
712 invocation_id=fsmo_owner["invocation_id"],
713 nc_dn_str=fsmo_dn,
714 exop=drsuapi.DRSUAPI_EXOP_FSMO_REQ_ROLE)
716 (drs, drs_handle) = self._ds_bind(fsmo_owner["dns_name"])
717 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
718 self.assertEqual(level, 6, "Expected level 6 response!")
719 self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER)
720 self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_owner["ntds_guid"]))
721 self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_owner["invocation_id"]))
723 def test_InvalidDestDSA_and_GUID(self):
724 """Test role transfer with invalid destination DSA guid"""
725 fsmo_dn = self.ldb_dc1.get_schema_basedn()
726 (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn)
728 req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
729 invocation_id=fsmo_owner["invocation_id"],
730 nc_dn_str="DummyDN",
731 nc_guid=misc.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"),
732 exop=drsuapi.DRSUAPI_EXOP_FSMO_REQ_ROLE)
734 (drs, drs_handle) = self._ds_bind(fsmo_owner["dns_name"])
735 try:
736 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
737 except WERRORError as e1:
738 (enum, estr) = e1.args
739 self.fail(f"DsGetNCChanges failed with {estr}")
740 self.assertEqual(level, 6, "Expected level 6 response!")
741 self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER)
742 self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_owner["ntds_guid"]))
743 self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_owner["invocation_id"]))
745 def test_InvalidDestDSA_and_GUID_RID_ALLOC(self):
746 """Test role transfer with invalid destination DSA guid"""
747 fsmo_dn = self.ldb_dc1.get_schema_basedn()
748 (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn)
750 req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
751 invocation_id=fsmo_owner["invocation_id"],
752 nc_dn_str="DummyDN",
753 nc_guid=misc.GUID("c2d2f745-1610-4e93-964b-d4ba73eb32f8"),
754 exop=drsuapi.DRSUAPI_EXOP_FSMO_RID_ALLOC)
756 (drs, drs_handle) = self._ds_bind(fsmo_owner["dns_name"])
757 try:
758 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
759 except WERRORError as e1:
760 (enum, estr) = e1.args
761 self.fail(f"DsGetNCChanges failed with {estr}")
762 self.assertEqual(level, 6, "Expected level 6 response!")
763 self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER)
764 self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_owner["ntds_guid"]))
765 self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_owner["invocation_id"]))
768 class DrsReplicaPrefixMapTestCase(drs_base.DrsBaseTestCase):
769 def setUp(self):
770 super(DrsReplicaPrefixMapTestCase, self).setUp()
771 self.base_dn = self.ldb_dc1.get_default_basedn()
772 self.ou = "ou=pfm_exop%d,%s" % (random.randint(0, 4294967295),
773 self.base_dn)
774 self.ldb_dc1.add({
775 "dn": self.ou,
776 "objectclass": "organizationalUnit"})
777 self.user = "cn=testuser,%s" % self.ou
778 self.ldb_dc1.add({
779 "dn": self.user,
780 "objectclass": "user"})
782 def tearDown(self):
783 super(DrsReplicaPrefixMapTestCase, self).tearDown()
784 try:
785 self.ldb_dc1.delete(self.ou, ["tree_delete:1"])
786 except ldb.LdbError as e2:
787 (enum, string) = e2.args
788 if enum == ldb.ERR_NO_SUCH_OBJECT:
789 pass
791 def test_missing_prefix_map_dsa(self):
792 partial_attribute_set = self.get_partial_attribute_set()
794 dc_guid_1 = self.ldb_dc1.get_invocation_id()
796 drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
798 req8 = self._exop_req8(dest_dsa=None,
799 invocation_id=dc_guid_1,
800 nc_dn_str=self.user,
801 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
802 partial_attribute_set=partial_attribute_set)
804 try:
805 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
806 self.assertEqual(ctr.extended_ret, drsuapi.DRSUAPI_EXOP_ERR_SUCCESS)
807 except RuntimeError:
808 self.fail("Missing prefixmap shouldn't have triggered an error")
810 def test_invalid_prefix_map_attid(self):
811 # Request for invalid attid
812 partial_attribute_set = self.get_partial_attribute_set([99999])
814 dc_guid_1 = self.ldb_dc1.get_invocation_id()
815 drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
817 try:
818 pfm = self._samdb_fetch_pfm_and_schi()
819 except KeyError:
820 # On Windows, prefixMap isn't available over LDAP
821 req8 = self._exop_req8(dest_dsa=None,
822 invocation_id=dc_guid_1,
823 nc_dn_str=self.user,
824 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
825 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
826 pfm = ctr.mapping_ctr
828 req8 = self._exop_req8(dest_dsa=None,
829 invocation_id=dc_guid_1,
830 nc_dn_str=self.user,
831 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
832 partial_attribute_set=partial_attribute_set,
833 mapping_ctr=pfm)
835 try:
836 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
837 self.fail("Invalid attid (99999) should have triggered an error")
838 except RuntimeError as e3:
839 (ecode, emsg) = e3.args
840 self.assertEqual(ecode, 0x000020E2, "Error code should have been "
841 "WERR_DS_DRA_SCHEMA_MISMATCH")
843 def test_secret_prefix_map_attid(self):
844 # Request for a secret attid
845 partial_attribute_set = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_unicodePwd])
847 dc_guid_1 = self.ldb_dc1.get_invocation_id()
848 drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
850 try:
851 pfm = self._samdb_fetch_pfm_and_schi()
852 except KeyError:
853 # On Windows, prefixMap isn't available over LDAP
854 req8 = self._exop_req8(dest_dsa=None,
855 invocation_id=dc_guid_1,
856 nc_dn_str=self.user,
857 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
858 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
859 pfm = ctr.mapping_ctr
861 req8 = self._exop_req8(dest_dsa=None,
862 invocation_id=dc_guid_1,
863 nc_dn_str=self.user,
864 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
865 partial_attribute_set=partial_attribute_set,
866 mapping_ctr=pfm)
868 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
870 found = False
871 for attr in ctr.first_object.object.attribute_ctr.attributes:
872 if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd:
873 found = True
874 break
876 self.assertTrue(found, "Ensure we get the unicodePwd attribute back")
878 for i, mapping in enumerate(pfm.mappings):
879 # OID: 2.5.4.*
880 # objectClass: 2.5.4.0
881 if mapping.oid.binary_oid == [85, 4]:
882 idx1 = i
883 # OID: 1.2.840.113556.1.4.*
884 # unicodePwd: 1.2.840.113556.1.4.90
885 elif mapping.oid.binary_oid == [42, 134, 72, 134, 247, 20, 1, 4]:
886 idx2 = i
888 (pfm.mappings[idx1].id_prefix,
889 pfm.mappings[idx2].id_prefix) = (pfm.mappings[idx2].id_prefix,
890 pfm.mappings[idx1].id_prefix)
892 tmp = pfm.mappings
893 tmp[idx1], tmp[idx2] = tmp[idx2], tmp[idx1]
894 pfm.mappings = tmp
896 # 90 for unicodePwd (with new prefix = 0)
897 # 589824, 589827 for objectClass and CN
898 # Use of three ensures sorting is correct
899 partial_attribute_set = self.get_partial_attribute_set([90, 589824, 589827])
900 req8 = self._exop_req8(dest_dsa=None,
901 invocation_id=dc_guid_1,
902 nc_dn_str=self.user,
903 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
904 partial_attribute_set=partial_attribute_set,
905 mapping_ctr=pfm)
907 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
909 found = False
910 for attr in ctr.first_object.object.attribute_ctr.attributes:
911 if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd:
912 found = True
913 break
915 self.assertTrue(found, "Ensure we get the unicodePwd attribute back")
917 def test_regular_prefix_map_attid(self):
918 # Request for a regular (non-secret) attid
919 partial_attribute_set = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_name])
921 dc_guid_1 = self.ldb_dc1.get_invocation_id()
922 drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
924 try:
925 pfm = self._samdb_fetch_pfm_and_schi()
926 except KeyError:
927 # On Windows, prefixMap isn't available over LDAP
928 req8 = self._exop_req8(dest_dsa=None,
929 invocation_id=dc_guid_1,
930 nc_dn_str=self.user,
931 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
932 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
933 pfm = ctr.mapping_ctr
935 req8 = self._exop_req8(dest_dsa=None,
936 invocation_id=dc_guid_1,
937 nc_dn_str=self.user,
938 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
939 partial_attribute_set=partial_attribute_set,
940 mapping_ctr=pfm)
942 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
944 found = False
945 for attr in ctr.first_object.object.attribute_ctr.attributes:
946 if attr.attid == drsuapi.DRSUAPI_ATTID_name:
947 found = True
948 break
950 self.assertTrue(found, "Ensure we get the name attribute back")
952 for i, mapping in enumerate(pfm.mappings):
953 # OID: 2.5.4.*
954 # objectClass: 2.5.4.0
955 if mapping.oid.binary_oid == [85, 4]:
956 idx1 = i
957 # OID: 1.2.840.113556.1.4.*
958 # name: 1.2.840.113556.1.4.1
959 elif mapping.oid.binary_oid == [42, 134, 72, 134, 247, 20, 1, 4]:
960 idx2 = i
962 (pfm.mappings[idx1].id_prefix,
963 pfm.mappings[idx2].id_prefix) = (pfm.mappings[idx2].id_prefix,
964 pfm.mappings[idx1].id_prefix)
966 tmp = pfm.mappings
967 tmp[idx1], tmp[idx2] = tmp[idx2], tmp[idx1]
968 pfm.mappings = tmp
970 # 1 for name (with new prefix = 0)
971 partial_attribute_set = self.get_partial_attribute_set([1])
972 req8 = self._exop_req8(dest_dsa=None,
973 invocation_id=dc_guid_1,
974 nc_dn_str=self.user,
975 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
976 partial_attribute_set=partial_attribute_set,
977 mapping_ctr=pfm)
979 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
981 found = False
982 for attr in ctr.first_object.object.attribute_ctr.attributes:
983 if attr.attid == drsuapi.DRSUAPI_ATTID_name:
984 found = True
985 break
987 self.assertTrue(found, "Ensure we get the name attribute back")
989 def test_regular_prefix_map_ex_attid(self):
990 # Request for a regular (non-secret) attid
991 partial_attribute_set = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_name])
992 partial_attribute_set_ex = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_unicodePwd])
994 dc_guid_1 = self.ldb_dc1.get_invocation_id()
995 drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
997 try:
998 pfm = self._samdb_fetch_pfm_and_schi()
999 except KeyError:
1000 # On Windows, prefixMap isn't available over LDAP
1001 req8 = self._exop_req8(dest_dsa=None,
1002 invocation_id=dc_guid_1,
1003 nc_dn_str=self.user,
1004 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
1005 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
1006 pfm = ctr.mapping_ctr
1008 req8 = self._exop_req8(dest_dsa=None,
1009 invocation_id=dc_guid_1,
1010 nc_dn_str=self.user,
1011 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
1012 partial_attribute_set=partial_attribute_set,
1013 partial_attribute_set_ex=partial_attribute_set_ex,
1014 mapping_ctr=pfm)
1016 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
1018 found = False
1019 for attr in ctr.first_object.object.attribute_ctr.attributes:
1020 if attr.attid == drsuapi.DRSUAPI_ATTID_name:
1021 found = True
1022 break
1024 self.assertTrue(found, "Ensure we get the name attribute back")
1026 found = False
1027 for attr in ctr.first_object.object.attribute_ctr.attributes:
1028 if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd:
1029 found = True
1030 break
1032 self.assertTrue(found, "Ensure we get the unicodePwd attribute back")
1034 for i, mapping in enumerate(pfm.mappings):
1035 # OID: 2.5.4.*
1036 # objectClass: 2.5.4.0
1037 if mapping.oid.binary_oid == [85, 4]:
1038 idx1 = i
1039 # OID: 1.2.840.113556.1.4.*
1040 # name: 1.2.840.113556.1.4.1
1041 # unicodePwd: 1.2.840.113556.1.4.90
1042 elif mapping.oid.binary_oid == [42, 134, 72, 134, 247, 20, 1, 4]:
1043 idx2 = i
1045 (pfm.mappings[idx1].id_prefix,
1046 pfm.mappings[idx2].id_prefix) = (pfm.mappings[idx2].id_prefix,
1047 pfm.mappings[idx1].id_prefix)
1049 tmp = pfm.mappings
1050 tmp[idx1], tmp[idx2] = tmp[idx2], tmp[idx1]
1051 pfm.mappings = tmp
1053 # 1 for name (with new prefix = 0)
1054 partial_attribute_set = self.get_partial_attribute_set([1])
1055 # 90 for unicodePwd (with new prefix = 0)
1056 # HOWEVER: Windows doesn't seem to respect incoming maps for PartialAttrSetEx
1057 partial_attribute_set_ex = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_unicodePwd])
1058 req8 = self._exop_req8(dest_dsa=None,
1059 invocation_id=dc_guid_1,
1060 nc_dn_str=self.user,
1061 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
1062 partial_attribute_set=partial_attribute_set,
1063 partial_attribute_set_ex=partial_attribute_set_ex,
1064 mapping_ctr=pfm)
1066 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
1068 found = False
1069 for attr in ctr.first_object.object.attribute_ctr.attributes:
1070 if attr.attid == drsuapi.DRSUAPI_ATTID_name:
1071 found = True
1072 break
1074 self.assertTrue(found, "Ensure we get the name attribute back")
1076 found = False
1077 for attr in ctr.first_object.object.attribute_ctr.attributes:
1078 if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd:
1079 found = True
1080 break
1082 self.assertTrue(found, "Ensure we get the unicodePwd attribute back")
1084 def _samdb_fetch_pfm_and_schi(self):
1085 """Fetch prefixMap and schemaInfo stored in SamDB using LDB connection"""
1086 samdb = self.ldb_dc1
1087 res = samdb.search(base=samdb.get_schema_basedn(), scope=SCOPE_BASE,
1088 attrs=["prefixMap", "schemaInfo"])
1090 pfm = ndr_unpack(drsblobs.prefixMapBlob,
1091 res[0]['prefixMap'][0])
1093 schi = drsuapi.DsReplicaOIDMapping()
1094 schi.id_prefix = 0
1095 if 'schemaInfo' in res[0]:
1096 binary_oid = list(res[0]['schemaInfo'][0])
1097 schi.oid.length = len(binary_oid)
1098 schi.oid.binary_oid = binary_oid
1099 else:
1100 schema_info = drsblobs.schemaInfoBlob()
1101 schema_info.revision = 0
1102 schema_info.marker = 0xFF
1103 schema_info.invocation_id = misc.GUID(samdb.get_invocation_id())
1105 binary_oid = list(ndr_pack(schema_info))
1106 # you have to set the length before setting binary_oid
1107 schi.oid.length = len(binary_oid)
1108 schi.oid.binary_oid = binary_oid
1110 pfm.ctr.mappings = pfm.ctr.mappings + [schi]
1111 pfm.ctr.num_mappings += 1
1112 return pfm.ctr
1115 class DrsReplicaSyncSortTestCase(drs_base.DrsBaseTestCase):
1116 def setUp(self):
1117 super(DrsReplicaSyncSortTestCase, self).setUp()
1118 self.base_dn = self.ldb_dc1.get_default_basedn()
1119 self.ou = "ou=sort_exop%d,%s" % (random.randint(0, 4294967295),
1120 self.base_dn)
1121 self.ldb_dc1.add({
1122 "dn": self.ou,
1123 "objectclass": "organizationalUnit"})
1125 def tearDown(self):
1126 super(DrsReplicaSyncSortTestCase, self).tearDown()
1127 # tidyup groups and users
1128 try:
1129 self.ldb_dc1.delete(self.ou, ["tree_delete:1"])
1130 except ldb.LdbError as e4:
1131 (enum, string) = e4.args
1132 if enum == ldb.ERR_NO_SUCH_OBJECT:
1133 pass
1135 def add_linked_attribute(self, src, dest, attr='member'):
1136 m = ldb.Message()
1137 m.dn = ldb.Dn(self.ldb_dc1, src)
1138 m[attr] = ldb.MessageElement(dest, ldb.FLAG_MOD_ADD, attr)
1139 self.ldb_dc1.modify(m)
1141 def remove_linked_attribute(self, src, dest, attr='member'):
1142 m = ldb.Message()
1143 m.dn = ldb.Dn(self.ldb_dc1, src)
1144 m[attr] = ldb.MessageElement(dest, ldb.FLAG_MOD_DELETE, attr)
1145 self.ldb_dc1.modify(m)
1147 def test_sort_behaviour_single_object(self):
1148 """Testing sorting behaviour on single objects"""
1150 user1_dn = "cn=test_user1,%s" % self.ou
1151 user2_dn = "cn=test_user2,%s" % self.ou
1152 user3_dn = "cn=test_user3,%s" % self.ou
1153 group_dn = "cn=test_group,%s" % self.ou
1155 self.ldb_dc1.add({"dn": user1_dn, "objectclass": "user"})
1156 self.ldb_dc1.add({"dn": user2_dn, "objectclass": "user"})
1157 self.ldb_dc1.add({"dn": user3_dn, "objectclass": "user"})
1158 self.ldb_dc1.add({"dn": group_dn, "objectclass": "group"})
1160 u1_guid = misc.GUID(self.ldb_dc1.search(base=user1_dn,
1161 attrs=["objectGUID"])[0]['objectGUID'][0])
1162 u2_guid = misc.GUID(self.ldb_dc1.search(base=user2_dn,
1163 attrs=["objectGUID"])[0]['objectGUID'][0])
1164 u3_guid = misc.GUID(self.ldb_dc1.search(base=user3_dn,
1165 attrs=["objectGUID"])[0]['objectGUID'][0])
1166 g_guid = misc.GUID(self.ldb_dc1.search(base=group_dn,
1167 attrs=["objectGUID"])[0]['objectGUID'][0])
1169 self.add_linked_attribute(group_dn, user1_dn,
1170 attr='member')
1171 self.add_linked_attribute(group_dn, user2_dn,
1172 attr='member')
1173 self.add_linked_attribute(group_dn, user3_dn,
1174 attr='member')
1175 self.add_linked_attribute(group_dn, user1_dn,
1176 attr='managedby')
1177 self.add_linked_attribute(group_dn, user2_dn,
1178 attr='nonSecurityMember')
1179 self.add_linked_attribute(group_dn, user3_dn,
1180 attr='nonSecurityMember')
1182 set_inactive = AbstractLink(drsuapi.DRSUAPI_ATTID_nonSecurityMember,
1183 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
1184 g_guid, u3_guid)
1186 expected_links = set([set_inactive,
1187 AbstractLink(drsuapi.DRSUAPI_ATTID_member,
1188 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
1189 g_guid,
1190 u1_guid),
1191 AbstractLink(drsuapi.DRSUAPI_ATTID_member,
1192 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
1193 g_guid,
1194 u2_guid),
1195 AbstractLink(drsuapi.DRSUAPI_ATTID_member,
1196 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
1197 g_guid,
1198 u3_guid),
1199 AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
1200 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
1201 g_guid,
1202 u1_guid),
1203 AbstractLink(drsuapi.DRSUAPI_ATTID_nonSecurityMember,
1204 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
1205 g_guid,
1206 u2_guid),
1209 dc_guid_1 = self.ldb_dc1.get_invocation_id()
1211 drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
1213 req8 = self._exop_req8(dest_dsa=None,
1214 invocation_id=dc_guid_1,
1215 nc_dn_str=group_dn,
1216 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
1218 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
1220 no_inactive = []
1221 for link in ctr.linked_attributes:
1222 target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3,
1223 link.value.blob).guid
1224 no_inactive.append((link, target_guid))
1225 self.assertTrue(AbstractLink(link.attid, link.flags,
1226 link.identifier.guid,
1227 target_guid) in expected_links)
1229 no_inactive.sort(key=cmp_to_key(_linked_attribute_compare))
1231 # assert the two arrays are the same
1232 self.assertEqual(len(expected_links), ctr.linked_attributes_count)
1233 self.assertEqual([x[0] for x in no_inactive], ctr.linked_attributes)
1235 self.remove_linked_attribute(group_dn, user3_dn,
1236 attr='nonSecurityMember')
1238 # Set the link inactive
1239 expected_links.remove(set_inactive)
1240 set_inactive.flags = 0
1241 expected_links.add(set_inactive)
1243 has_inactive = []
1244 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
1245 for link in ctr.linked_attributes:
1246 target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3,
1247 link.value.blob).guid
1248 has_inactive.append((link, target_guid))
1249 self.assertTrue(AbstractLink(link.attid, link.flags,
1250 link.identifier.guid,
1251 target_guid) in expected_links)
1253 has_inactive.sort(key=cmp_to_key(_linked_attribute_compare))
1255 # assert the two arrays are the same
1256 self.assertEqual(len(expected_links), ctr.linked_attributes_count)
1257 self.assertEqual([x[0] for x in has_inactive], ctr.linked_attributes)
1259 def test_sort_behaviour_ncchanges(self):
1260 """Testing sorting behaviour on a group of objects."""
1261 user1_dn = "cn=test_user1,%s" % self.ou
1262 group_dn = "cn=test_group,%s" % self.ou
1263 self.ldb_dc1.add({"dn": user1_dn, "objectclass": "user"})
1264 self.ldb_dc1.add({"dn": group_dn, "objectclass": "group"})
1266 self.add_linked_attribute(group_dn, user1_dn,
1267 attr='member')
1269 dc_guid_1 = self.ldb_dc1.get_invocation_id()
1271 drs, drs_handle = self._ds_bind(self.dnsname_dc1, ip=self.url_dc1)
1273 # Make sure the max objects count is high enough
1274 req8 = self._exop_req8(dest_dsa=None,
1275 invocation_id=dc_guid_1,
1276 nc_dn_str=self.base_dn,
1277 replica_flags=0,
1278 max_objects=100,
1279 exop=drsuapi.DRSUAPI_EXOP_NONE)
1281 # Loop until we get linked attributes, or we get to the end.
1282 # Samba sends linked attributes at the end, unlike Windows.
1283 while True:
1284 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
1285 if ctr.more_data == 0 or ctr.linked_attributes_count != 0:
1286 break
1287 req8.highwatermark = ctr.new_highwatermark
1289 self.assertTrue(ctr.linked_attributes_count != 0)
1291 no_inactive = []
1292 for link in ctr.linked_attributes:
1293 try:
1294 target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3,
1295 link.value.blob).guid
1296 except:
1297 target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3Binary,
1298 link.value.blob).guid
1299 no_inactive.append((link, target_guid))
1301 no_inactive.sort(key=cmp_to_key(_linked_attribute_compare))
1303 # assert the two arrays are the same
1304 self.assertEqual([x[0] for x in no_inactive], ctr.linked_attributes)