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/>.
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"
34 from drs_base import AbstractLink
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
46 def _linked_attribute_compare(la1, la2):
47 """See CompareLinks() in MS-DRSR section 4.1.10.5.17"""
51 # Ascending host object GUID
52 c = cmp(ndr_pack(la1.identifier.guid), ndr_pack(la2.identifier.guid))
56 # Ascending attribute ID
57 if la1.attid != la2.attid:
58 return -1 if la1.attid < la2.attid else 1
60 la1_active = la1.flags & drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
61 la2_active = la2.flags & drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
63 # Ascending 'is present'
64 if la1_active != la2_active:
65 return 1 if la1_active else -1
67 # Ascending target object GUID
68 return cmp(ndr_pack(la1_target), ndr_pack(la2_target))
71 class DrsReplicaSyncTestCase(drs_base.DrsBaseTestCase):
72 """Intended as a semi-black box test case for DsGetNCChanges
73 implementation for extended operations. It should be testing
74 how DsGetNCChanges handles different input params (mostly invalid).
75 Final goal is to make DsGetNCChanges as binary compatible to
76 Windows implementation as possible"""
79 super(DrsReplicaSyncTestCase, self).setUp()
80 self.base_dn = self.ldb_dc1.get_default_basedn()
81 self.ou = "OU=test_getncchanges,%s" % self.base_dn
84 "objectclass": "organizationalUnit"})
85 (self.drs, self.drs_handle) = self._ds_bind(self.dnsname_dc1)
86 (self.default_hwm, self.default_utdv) = self._get_highest_hwm_utdv(self.ldb_dc1)
90 self.ldb_dc1.delete(self.ou, ["tree_delete:1"])
91 except ldb.LdbError as (enum, string):
92 if enum == ldb.ERR_NO_SUCH_OBJECT:
94 super(DrsReplicaSyncTestCase, self).tearDown()
96 def _determine_fSMORoleOwner(self, fsmo_obj_dn):
97 """Returns (owner, not_owner) pair where:
98 owner: dns name for FSMO owner
99 not_owner: dns name for DC not owning the FSMO"""
100 # collect info to return later
101 fsmo_info_1 = {"dns_name": self.dnsname_dc1,
102 "invocation_id": self.ldb_dc1.get_invocation_id(),
103 "ntds_guid": self.ldb_dc1.get_ntds_GUID(),
104 "server_dn": self.ldb_dc1.get_serverName()}
105 fsmo_info_2 = {"dns_name": self.dnsname_dc2,
106 "invocation_id": self.ldb_dc2.get_invocation_id(),
107 "ntds_guid": self.ldb_dc2.get_ntds_GUID(),
108 "server_dn": self.ldb_dc2.get_serverName()}
110 msgs = self.ldb_dc1.search(scope=ldb.SCOPE_BASE, base=fsmo_info_1["server_dn"], attrs=["serverReference"])
111 fsmo_info_1["server_acct_dn"] = ldb.Dn(self.ldb_dc1, msgs[0]["serverReference"][0])
112 fsmo_info_1["rid_set_dn"] = ldb.Dn(self.ldb_dc1, "CN=RID Set") + fsmo_info_1["server_acct_dn"]
114 msgs = self.ldb_dc2.search(scope=ldb.SCOPE_BASE, base=fsmo_info_2["server_dn"], attrs=["serverReference"])
115 fsmo_info_2["server_acct_dn"] = ldb.Dn(self.ldb_dc2, msgs[0]["serverReference"][0])
116 fsmo_info_2["rid_set_dn"] = ldb.Dn(self.ldb_dc2, "CN=RID Set") + fsmo_info_2["server_acct_dn"]
118 # determine the owner dc
119 res = self.ldb_dc1.search(fsmo_obj_dn,
120 scope=SCOPE_BASE, attrs=["fSMORoleOwner"])
121 assert len(res) == 1, "Only one fSMORoleOwner value expected for %s!"%fsmo_obj_dn
122 fsmo_owner = res[0]["fSMORoleOwner"][0]
123 if fsmo_owner == self.info_dc1["dsServiceName"][0]:
124 return (fsmo_info_1, fsmo_info_2)
125 return (fsmo_info_2, fsmo_info_1)
127 def _check_exop_failed(self, ctr6, expected_failure):
128 self.assertEqual(ctr6.extended_ret, expected_failure)
129 #self.assertEqual(ctr6.object_count, 0)
130 #self.assertEqual(ctr6.first_object, None)
131 self.assertEqual(ctr6.more_data, False)
132 self.assertEqual(ctr6.nc_object_count, 0)
133 self.assertEqual(ctr6.nc_linked_attributes_count, 0)
134 self.assertEqual(ctr6.linked_attributes_count, 0)
135 self.assertEqual(ctr6.linked_attributes, [])
136 self.assertEqual(ctr6.drs_error[0], 0)
138 def test_do_single_repl(self):
140 Make sure that DRSU_EXOP_REPL_OBJ never replicates more than
141 one object, even when we use DRS_GET_ANC.
144 ou1 = "OU=get_anc1,%s" % self.ou
147 "objectclass": "organizationalUnit"
149 ou1_id = self._get_identifier(self.ldb_dc1, ou1)
150 ou2 = "OU=get_anc2,%s" % ou1
153 "objectclass": "organizationalUnit"
155 ou2_id = self._get_identifier(self.ldb_dc1, ou2)
156 dc3 = "CN=test_anc_dc_%u,%s" % (random.randint(0, 4294967295), ou2)
159 "objectclass": "computer",
160 "userAccountControl": "%d" % (samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_SERVER_TRUST_ACCOUNT)
162 dc3_id = self._get_identifier(self.ldb_dc1, dc3)
164 req8 = self._exop_req8(dest_dsa=None,
165 invocation_id=self.ldb_dc1.get_invocation_id(),
167 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
168 replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP)
169 (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 8, req8)
170 self._check_ctr6(ctr, [ou1])
172 # DRSUAPI_DRS_WRIT_REP means that we should only replicate the dn we give (dc3).
173 # DRSUAPI_DRS_GET_ANC means that we should also replicate its ancestors, but
174 # Windows doesn't do this if we use both.
175 req8 = self._exop_req8(dest_dsa=None,
176 invocation_id=self.ldb_dc1.get_invocation_id(),
178 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
179 replica_flags=drsuapi.DRSUAPI_DRS_WRIT_REP |
180 drsuapi.DRSUAPI_DRS_GET_ANC)
181 (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 8, req8)
182 self._check_ctr6(ctr, [dc3])
184 # Even though the ancestor of ou2 (ou1) has changed since last hwm, and we're
185 # sending DRSUAPI_DRS_GET_ANC, the expected response is that it will only try
186 # and replicate the single object still.
187 req8 = self._exop_req8(dest_dsa=None,
188 invocation_id=self.ldb_dc1.get_invocation_id(),
190 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
191 replica_flags=drsuapi.DRSUAPI_DRS_CRITICAL_ONLY |
192 drsuapi.DRSUAPI_DRS_GET_ANC)
193 (level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, 8, req8)
194 self._check_ctr6(ctr, [ou2])
196 def test_link_utdv_hwm(self):
197 """Test verify the DRS_GET_ANC behavior."""
199 ou1 = "OU=get_anc1,%s" % self.ou
202 "objectclass": "organizationalUnit"
204 ou1_id = self._get_identifier(self.ldb_dc1, ou1)
205 ou2 = "OU=get_anc2,%s" % ou1
208 "objectclass": "organizationalUnit"
210 ou2_id = self._get_identifier(self.ldb_dc1, ou2)
211 dc3 = "CN=test_anc_dc_%u,%s" % (random.randint(0, 4294967295), ou2)
214 "objectclass": "computer",
215 "userAccountControl": "%d" % (samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_SERVER_TRUST_ACCOUNT)
217 dc3_id = self._get_identifier(self.ldb_dc1, dc3)
219 (hwm1, utdv1) = self._check_replication([ou1,ou2,dc3],
220 drsuapi.DRSUAPI_DRS_WRIT_REP)
222 self._check_replication([ou1,ou2,dc3],
223 drsuapi.DRSUAPI_DRS_WRIT_REP|
224 drsuapi.DRSUAPI_DRS_GET_ANC)
226 self._check_replication([dc3],
227 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
229 self._check_replication([ou1,ou2,dc3],
230 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
231 drsuapi.DRSUAPI_DRS_GET_ANC)
234 m.dn = ldb.Dn(self.ldb_dc1, ou1)
235 m["displayName"] = ldb.MessageElement("OU1", ldb.FLAG_MOD_ADD, "displayName")
236 self.ldb_dc1.modify(m)
238 (hwm2, utdv2) = self._check_replication([ou2,dc3,ou1],
239 drsuapi.DRSUAPI_DRS_WRIT_REP)
241 self._check_replication([ou1,ou2,dc3],
242 drsuapi.DRSUAPI_DRS_WRIT_REP|
243 drsuapi.DRSUAPI_DRS_GET_ANC)
245 self._check_replication([dc3],
246 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
248 self._check_replication([ou1,ou2,dc3],
249 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
250 drsuapi.DRSUAPI_DRS_GET_ANC)
252 self._check_replication([ou1],
253 drsuapi.DRSUAPI_DRS_WRIT_REP,
256 self._check_replication([ou1],
257 drsuapi.DRSUAPI_DRS_WRIT_REP|
258 drsuapi.DRSUAPI_DRS_GET_ANC,
261 self._check_replication([ou1],
262 drsuapi.DRSUAPI_DRS_WRIT_REP|
263 drsuapi.DRSUAPI_DRS_GET_ANC,
264 uptodateness_vector=utdv1)
267 m.dn = ldb.Dn(self.ldb_dc1, ou2)
268 m["displayName"] = ldb.MessageElement("OU2", ldb.FLAG_MOD_ADD, "displayName")
269 self.ldb_dc1.modify(m)
271 (hwm3, utdv3) = self._check_replication([dc3,ou1,ou2],
272 drsuapi.DRSUAPI_DRS_WRIT_REP)
274 self._check_replication([ou1,ou2,dc3],
275 drsuapi.DRSUAPI_DRS_WRIT_REP|
276 drsuapi.DRSUAPI_DRS_GET_ANC)
278 self._check_replication([dc3],
279 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
281 self._check_replication([ou1,ou2,dc3],
282 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
283 drsuapi.DRSUAPI_DRS_GET_ANC)
285 self._check_replication([ou1,ou2],
286 drsuapi.DRSUAPI_DRS_WRIT_REP,
289 self._check_replication([ou1,ou2],
290 drsuapi.DRSUAPI_DRS_WRIT_REP|
291 drsuapi.DRSUAPI_DRS_GET_ANC,
294 self._check_replication([ou1,ou2],
295 drsuapi.DRSUAPI_DRS_WRIT_REP|
296 drsuapi.DRSUAPI_DRS_GET_ANC,
297 uptodateness_vector=utdv1)
300 m.dn = ldb.Dn(self.ldb_dc1, self.ou)
301 m["displayName"] = ldb.MessageElement("OU", ldb.FLAG_MOD_ADD, "displayName")
302 self.ldb_dc1.modify(m)
304 (hwm4, utdv4) = self._check_replication([dc3,ou1,ou2,self.ou],
305 drsuapi.DRSUAPI_DRS_WRIT_REP)
307 self._check_replication([self.ou,ou1,ou2,dc3],
308 drsuapi.DRSUAPI_DRS_WRIT_REP|
309 drsuapi.DRSUAPI_DRS_GET_ANC)
311 self._check_replication([dc3],
312 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
314 self._check_replication([self.ou,ou1,ou2,dc3],
315 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
316 drsuapi.DRSUAPI_DRS_GET_ANC)
318 self._check_replication([self.ou,ou2],
319 drsuapi.DRSUAPI_DRS_WRIT_REP|
320 drsuapi.DRSUAPI_DRS_GET_ANC,
321 uptodateness_vector=utdv2)
323 cn3 = "CN=get_anc3,%s" % ou2
326 "objectclass": "container",
328 cn3_id = self._get_identifier(self.ldb_dc1, cn3)
330 (hwm5, utdv5) = self._check_replication([dc3,ou1,ou2,self.ou,cn3],
331 drsuapi.DRSUAPI_DRS_WRIT_REP)
333 self._check_replication([self.ou,ou1,ou2,dc3,cn3],
334 drsuapi.DRSUAPI_DRS_WRIT_REP|
335 drsuapi.DRSUAPI_DRS_GET_ANC)
337 self._check_replication([dc3],
338 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
340 self._check_replication([self.ou,ou1,ou2,dc3],
341 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
342 drsuapi.DRSUAPI_DRS_GET_ANC)
345 m.dn = ldb.Dn(self.ldb_dc1, ou2)
346 m["managedBy"] = ldb.MessageElement(dc3, ldb.FLAG_MOD_ADD, "managedBy")
347 self.ldb_dc1.modify(m)
348 ou2_managedBy_dc3 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
349 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
350 ou2_id.guid, dc3_id.guid)
352 (hwm6, utdv6) = self._check_replication([dc3,ou1,self.ou,cn3,ou2],
353 drsuapi.DRSUAPI_DRS_WRIT_REP,
354 expected_links=[ou2_managedBy_dc3])
356 # Can fail against Windows due to equal precedence of dc3, cn3
357 self._check_replication([self.ou,ou1,ou2,dc3,cn3],
358 drsuapi.DRSUAPI_DRS_WRIT_REP|
359 drsuapi.DRSUAPI_DRS_GET_ANC,
360 expected_links=[ou2_managedBy_dc3])
362 self._check_replication([dc3],
363 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
365 self._check_replication([self.ou,ou1,ou2,dc3],
366 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
367 drsuapi.DRSUAPI_DRS_GET_ANC)
369 self._check_replication([],
370 drsuapi.DRSUAPI_DRS_WRIT_REP,
371 uptodateness_vector=utdv5,
372 expected_links=[ou2_managedBy_dc3])
374 self._check_replication([],
375 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
376 uptodateness_vector=utdv5)
378 self._check_replication([],
379 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
380 uptodateness_vector=utdv5)
383 m.dn = ldb.Dn(self.ldb_dc1, dc3)
384 m["managedBy"] = ldb.MessageElement(ou1, ldb.FLAG_MOD_ADD, "managedBy")
385 self.ldb_dc1.modify(m)
386 dc3_managedBy_ou1 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
387 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
388 dc3_id.guid, ou1_id.guid)
390 (hwm7, utdv7) = self._check_replication([ou1,self.ou,cn3,ou2,dc3],
391 drsuapi.DRSUAPI_DRS_WRIT_REP,
392 expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1])
394 # Can fail against Windows due to equal precedence of dc3, cn3
395 #self._check_replication([self.ou,ou1,ou2,dc3,cn3],
396 # drsuapi.DRSUAPI_DRS_WRIT_REP|
397 # drsuapi.DRSUAPI_DRS_GET_ANC,
398 # expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1])
400 self._check_replication([dc3],
401 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
402 expected_links=[dc3_managedBy_ou1])
404 self._check_replication([dc3],
405 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
406 expected_links=[dc3_managedBy_ou1])
408 self._check_replication([self.ou,ou1,ou2,dc3],
409 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
410 drsuapi.DRSUAPI_DRS_GET_ANC,
411 expected_links=[dc3_managedBy_ou1])
413 self._check_replication([dc3],
414 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
415 more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
416 expected_links=[dc3_managedBy_ou1])
419 m.dn = ldb.Dn(self.ldb_dc1, dc3)
420 m["managedBy"] = ldb.MessageElement(ou2, ldb.FLAG_MOD_REPLACE, "managedBy")
421 self.ldb_dc1.modify(m)
422 dc3_managedBy_ou1.flags &= ~drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
423 dc3_managedBy_ou2 = AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
424 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
425 dc3_id.guid, ou2_id.guid)
427 (hwm8, utdv8) = self._check_replication([ou1,self.ou,cn3,ou2,dc3],
428 drsuapi.DRSUAPI_DRS_WRIT_REP,
429 expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1,dc3_managedBy_ou2])
431 # Can fail against Windows due to equal precedence of dc3, cn3
432 #self._check_replication([self.ou,ou1,ou2,dc3,cn3],
433 # drsuapi.DRSUAPI_DRS_WRIT_REP|
434 # drsuapi.DRSUAPI_DRS_GET_ANC,
435 # expected_links=[ou2_managedBy_dc3,dc3_managedBy_ou1,dc3_managedBy_ou2])
437 self._check_replication([dc3],
438 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
439 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2])
441 self._check_replication([self.ou,ou1,ou2,dc3],
442 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
443 drsuapi.DRSUAPI_DRS_GET_ANC,
444 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2])
446 self._check_replication([dc3],
447 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
448 more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
449 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2])
451 self._check_replication([],
452 drsuapi.DRSUAPI_DRS_WRIT_REP,
453 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
456 self._check_replication([],
457 drsuapi.DRSUAPI_DRS_WRIT_REP|
458 drsuapi.DRSUAPI_DRS_GET_ANC,
459 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
462 self._check_replication([],
463 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
464 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
467 self._check_replication([],
468 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
469 drsuapi.DRSUAPI_DRS_GET_ANC,
470 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
473 self._check_replication([],
474 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
475 more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
476 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
479 self._check_replication([],
480 drsuapi.DRSUAPI_DRS_WRIT_REP,
481 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
482 uptodateness_vector=utdv7)
484 self._check_replication([],
485 drsuapi.DRSUAPI_DRS_WRIT_REP|
486 drsuapi.DRSUAPI_DRS_GET_ANC,
487 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
488 uptodateness_vector=utdv7)
490 self._check_replication([],
491 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
492 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
493 uptodateness_vector=utdv7)
495 self._check_replication([],
496 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY|
497 drsuapi.DRSUAPI_DRS_GET_ANC,
498 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
499 uptodateness_vector=utdv7)
501 self._check_replication([],
502 drsuapi.DRSUAPI_DRS_CRITICAL_ONLY,
503 more_flags=drsuapi.DRSUAPI_DRS_GET_TGT,
504 expected_links=[dc3_managedBy_ou1,dc3_managedBy_ou2],
505 uptodateness_vector=utdv7)
507 def test_FSMONotOwner(self):
508 """Test role transfer with against DC not owner of the role"""
509 fsmo_dn = self.ldb_dc1.get_schema_basedn()
510 (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn)
512 req8 = self._exop_req8(dest_dsa=fsmo_owner["ntds_guid"],
513 invocation_id=fsmo_not_owner["invocation_id"],
515 exop=drsuapi.DRSUAPI_EXOP_FSMO_REQ_ROLE)
517 (drs, drs_handle) = self._ds_bind(fsmo_not_owner["dns_name"])
518 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
519 self.assertEqual(level, 6, "Expected level 6 response!")
520 self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER)
521 self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_not_owner["ntds_guid"]))
522 self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_not_owner["invocation_id"]))
524 def test_InvalidDestDSA(self):
525 """Test role transfer with invalid destination DSA guid"""
526 fsmo_dn = self.ldb_dc1.get_schema_basedn()
527 (fsmo_owner, fsmo_not_owner) = self._determine_fSMORoleOwner(fsmo_dn)
529 req8 = self._exop_req8(dest_dsa="9c637462-5b8c-4467-aef2-bdb1f57bc4ef",
530 invocation_id=fsmo_owner["invocation_id"],
532 exop=drsuapi.DRSUAPI_EXOP_FSMO_REQ_ROLE)
534 (drs, drs_handle) = self._ds_bind(fsmo_owner["dns_name"])
535 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
536 self.assertEqual(level, 6, "Expected level 6 response!")
537 self._check_exop_failed(ctr, drsuapi.DRSUAPI_EXOP_ERR_UNKNOWN_CALLER)
538 self.assertEqual(ctr.source_dsa_guid, misc.GUID(fsmo_owner["ntds_guid"]))
539 self.assertEqual(ctr.source_dsa_invocation_id, misc.GUID(fsmo_owner["invocation_id"]))
541 class DrsReplicaPrefixMapTestCase(drs_base.DrsBaseTestCase):
543 super(DrsReplicaPrefixMapTestCase, self).setUp()
544 self.base_dn = self.ldb_dc1.get_default_basedn()
545 self.ou = "ou=pfm_exop,%s" % self.base_dn
548 "objectclass": "organizationalUnit"})
549 self.user = "cn=testuser,%s" % self.ou
552 "objectclass": "user"})
555 super(DrsReplicaPrefixMapTestCase, self).tearDown()
557 self.ldb_dc1.delete(self.ou, ["tree_delete:1"])
558 except ldb.LdbError as (enum, string):
559 if enum == ldb.ERR_NO_SUCH_OBJECT:
562 def test_missing_prefix_map_dsa(self):
563 partial_attribute_set = self.get_partial_attribute_set()
565 dc_guid_1 = self.ldb_dc1.get_invocation_id()
567 drs, drs_handle = self._ds_bind(self.dnsname_dc1)
569 req8 = self._exop_req8(dest_dsa=None,
570 invocation_id=dc_guid_1,
572 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
573 partial_attribute_set=partial_attribute_set)
576 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
577 self.assertEqual(ctr.extended_ret, drsuapi.DRSUAPI_EXOP_ERR_SUCCESS)
579 self.fail("Missing prefixmap shouldn't have triggered an error")
581 def test_invalid_prefix_map_attid(self):
582 # Request for invalid attid
583 partial_attribute_set = self.get_partial_attribute_set([99999])
585 dc_guid_1 = self.ldb_dc1.get_invocation_id()
586 drs, drs_handle = self._ds_bind(self.dnsname_dc1)
589 pfm = self._samdb_fetch_pfm_and_schi()
591 # On Windows, prefixMap isn't available over LDAP
592 req8 = self._exop_req8(dest_dsa=None,
593 invocation_id=dc_guid_1,
595 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
596 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
597 pfm = ctr.mapping_ctr
599 req8 = self._exop_req8(dest_dsa=None,
600 invocation_id=dc_guid_1,
602 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
603 partial_attribute_set=partial_attribute_set,
607 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
608 self.fail("Invalid attid (99999) should have triggered an error")
609 except RuntimeError as (ecode, emsg):
610 self.assertEqual(ecode, 0x000020E2, "Error code should have been "
611 "WERR_DS_DRA_SCHEMA_MISMATCH")
613 def test_secret_prefix_map_attid(self):
614 # Request for a secret attid
615 partial_attribute_set = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_unicodePwd])
617 dc_guid_1 = self.ldb_dc1.get_invocation_id()
618 drs, drs_handle = self._ds_bind(self.dnsname_dc1)
621 pfm = self._samdb_fetch_pfm_and_schi()
623 # On Windows, prefixMap isn't available over LDAP
624 req8 = self._exop_req8(dest_dsa=None,
625 invocation_id=dc_guid_1,
627 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
628 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
629 pfm = ctr.mapping_ctr
632 req8 = self._exop_req8(dest_dsa=None,
633 invocation_id=dc_guid_1,
635 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
636 partial_attribute_set=partial_attribute_set,
639 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
642 for attr in ctr.first_object.object.attribute_ctr.attributes:
643 if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd:
647 self.assertTrue(found, "Ensure we get the unicodePwd attribute back")
649 for i, mapping in enumerate(pfm.mappings):
651 # objectClass: 2.5.4.0
652 if mapping.oid.binary_oid == [85, 4]:
654 # OID: 1.2.840.113556.1.4.*
655 # unicodePwd: 1.2.840.113556.1.4.90
656 elif mapping.oid.binary_oid == [42, 134, 72, 134, 247, 20, 1, 4]:
659 (pfm.mappings[idx1].id_prefix,
660 pfm.mappings[idx2].id_prefix) = (pfm.mappings[idx2].id_prefix,
661 pfm.mappings[idx1].id_prefix)
664 tmp[idx1], tmp[idx2] = tmp[idx2], tmp[idx1]
667 # 90 for unicodePwd (with new prefix = 0)
668 # 589824, 589827 for objectClass and CN
669 # Use of three ensures sorting is correct
670 partial_attribute_set = self.get_partial_attribute_set([90, 589824, 589827])
671 req8 = self._exop_req8(dest_dsa=None,
672 invocation_id=dc_guid_1,
674 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
675 partial_attribute_set=partial_attribute_set,
678 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
681 for attr in ctr.first_object.object.attribute_ctr.attributes:
682 if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd:
686 self.assertTrue(found, "Ensure we get the unicodePwd attribute back")
688 def test_regular_prefix_map_attid(self):
689 # Request for a regular (non-secret) attid
690 partial_attribute_set = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_name])
692 dc_guid_1 = self.ldb_dc1.get_invocation_id()
693 drs, drs_handle = self._ds_bind(self.dnsname_dc1)
696 pfm = self._samdb_fetch_pfm_and_schi()
698 # On Windows, prefixMap isn't available over LDAP
699 req8 = self._exop_req8(dest_dsa=None,
700 invocation_id=dc_guid_1,
702 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
703 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
704 pfm = ctr.mapping_ctr
707 req8 = self._exop_req8(dest_dsa=None,
708 invocation_id=dc_guid_1,
710 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
711 partial_attribute_set=partial_attribute_set,
714 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
717 for attr in ctr.first_object.object.attribute_ctr.attributes:
718 if attr.attid == drsuapi.DRSUAPI_ATTID_name:
722 self.assertTrue(found, "Ensure we get the name attribute back")
724 for i, mapping in enumerate(pfm.mappings):
726 # objectClass: 2.5.4.0
727 if mapping.oid.binary_oid == [85, 4]:
729 # OID: 1.2.840.113556.1.4.*
730 # name: 1.2.840.113556.1.4.1
731 elif mapping.oid.binary_oid == [42, 134, 72, 134, 247, 20, 1, 4]:
734 (pfm.mappings[idx1].id_prefix,
735 pfm.mappings[idx2].id_prefix) = (pfm.mappings[idx2].id_prefix,
736 pfm.mappings[idx1].id_prefix)
739 tmp[idx1], tmp[idx2] = tmp[idx2], tmp[idx1]
742 # 1 for name (with new prefix = 0)
743 partial_attribute_set = self.get_partial_attribute_set([1])
744 req8 = self._exop_req8(dest_dsa=None,
745 invocation_id=dc_guid_1,
747 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
748 partial_attribute_set=partial_attribute_set,
751 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
754 for attr in ctr.first_object.object.attribute_ctr.attributes:
755 if attr.attid == drsuapi.DRSUAPI_ATTID_name:
759 self.assertTrue(found, "Ensure we get the name attribute back")
761 def test_regular_prefix_map_ex_attid(self):
762 # Request for a regular (non-secret) attid
763 partial_attribute_set = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_name])
764 partial_attribute_set_ex = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_unicodePwd])
766 dc_guid_1 = self.ldb_dc1.get_invocation_id()
767 drs, drs_handle = self._ds_bind(self.dnsname_dc1)
770 pfm = self._samdb_fetch_pfm_and_schi()
772 # On Windows, prefixMap isn't available over LDAP
773 req8 = self._exop_req8(dest_dsa=None,
774 invocation_id=dc_guid_1,
776 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
777 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
778 pfm = ctr.mapping_ctr
781 req8 = self._exop_req8(dest_dsa=None,
782 invocation_id=dc_guid_1,
784 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
785 partial_attribute_set=partial_attribute_set,
786 partial_attribute_set_ex=partial_attribute_set_ex,
789 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
792 for attr in ctr.first_object.object.attribute_ctr.attributes:
793 if attr.attid == drsuapi.DRSUAPI_ATTID_name:
797 self.assertTrue(found, "Ensure we get the name attribute back")
800 for attr in ctr.first_object.object.attribute_ctr.attributes:
801 if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd:
805 self.assertTrue(found, "Ensure we get the unicodePwd attribute back")
807 for i, mapping in enumerate(pfm.mappings):
809 # objectClass: 2.5.4.0
810 if mapping.oid.binary_oid == [85, 4]:
812 # OID: 1.2.840.113556.1.4.*
813 # name: 1.2.840.113556.1.4.1
814 # unicodePwd: 1.2.840.113556.1.4.90
815 elif mapping.oid.binary_oid == [42, 134, 72, 134, 247, 20, 1, 4]:
818 (pfm.mappings[idx1].id_prefix,
819 pfm.mappings[idx2].id_prefix) = (pfm.mappings[idx2].id_prefix,
820 pfm.mappings[idx1].id_prefix)
823 tmp[idx1], tmp[idx2] = tmp[idx2], tmp[idx1]
826 # 1 for name (with new prefix = 0)
827 partial_attribute_set = self.get_partial_attribute_set([1])
828 # 90 for unicodePwd (with new prefix = 0)
829 # HOWEVER: Windows doesn't seem to respect incoming maps for PartialAttrSetEx
830 partial_attribute_set_ex = self.get_partial_attribute_set([drsuapi.DRSUAPI_ATTID_unicodePwd])
831 req8 = self._exop_req8(dest_dsa=None,
832 invocation_id=dc_guid_1,
834 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ,
835 partial_attribute_set=partial_attribute_set,
836 partial_attribute_set_ex=partial_attribute_set_ex,
839 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
842 for attr in ctr.first_object.object.attribute_ctr.attributes:
843 if attr.attid == drsuapi.DRSUAPI_ATTID_name:
847 self.assertTrue(found, "Ensure we get the name attribute back")
850 for attr in ctr.first_object.object.attribute_ctr.attributes:
851 if attr.attid == drsuapi.DRSUAPI_ATTID_unicodePwd:
855 self.assertTrue(found, "Ensure we get the unicodePwd attribute back")
857 def _samdb_fetch_pfm_and_schi(self):
858 """Fetch prefixMap and schemaInfo stored in SamDB using LDB connection"""
860 res = samdb.search(base=samdb.get_schema_basedn(), scope=SCOPE_BASE,
861 attrs=["prefixMap", "schemaInfo"])
863 pfm = ndr_unpack(drsblobs.prefixMapBlob,
864 str(res[0]['prefixMap']))
866 schi = drsuapi.DsReplicaOIDMapping()
869 if 'schemaInfo' in res[0]:
870 schi.oid.length = len(map(ord, str(res[0]['schemaInfo'])))
871 schi.oid.binary_oid = map(ord, str(res[0]['schemaInfo']))
873 schema_info = drsblobs.schemaInfoBlob()
874 schema_info.revision = 0
875 schema_info.marker = 0xFF
876 schema_info.invocation_id = misc.GUID(samdb.get_invocation_id())
877 schi.oid.length = len(map(ord, ndr_pack(schema_info)))
878 schi.oid.binary_oid = map(ord, ndr_pack(schema_info))
880 pfm.ctr.mappings = pfm.ctr.mappings + [schi]
881 pfm.ctr.num_mappings += 1
884 class DrsReplicaSyncSortTestCase(drs_base.DrsBaseTestCase):
886 super(DrsReplicaSyncSortTestCase, self).setUp()
887 self.base_dn = self.ldb_dc1.get_default_basedn()
888 self.ou = "ou=sort_exop,%s" % self.base_dn
891 "objectclass": "organizationalUnit"})
894 super(DrsReplicaSyncSortTestCase, self).tearDown()
895 # tidyup groups and users
897 self.ldb_dc1.delete(self.ou, ["tree_delete:1"])
898 except ldb.LdbError as (enum, string):
899 if enum == ldb.ERR_NO_SUCH_OBJECT:
902 def add_linked_attribute(self, src, dest, attr='member'):
904 m.dn = ldb.Dn(self.ldb_dc1, src)
905 m[attr] = ldb.MessageElement(dest, ldb.FLAG_MOD_ADD, attr)
906 self.ldb_dc1.modify(m)
908 def remove_linked_attribute(self, src, dest, attr='member'):
910 m.dn = ldb.Dn(self.ldb_dc1, src)
911 m[attr] = ldb.MessageElement(dest, ldb.FLAG_MOD_DELETE, attr)
912 self.ldb_dc1.modify(m)
914 def test_sort_behaviour_single_object(self):
915 """Testing sorting behaviour on single objects"""
917 user1_dn = "cn=test_user1,%s" % self.ou
918 user2_dn = "cn=test_user2,%s" % self.ou
919 user3_dn = "cn=test_user3,%s" % self.ou
920 group_dn = "cn=test_group,%s" % self.ou
922 self.ldb_dc1.add({"dn": user1_dn, "objectclass": "user"})
923 self.ldb_dc1.add({"dn": user2_dn, "objectclass": "user"})
924 self.ldb_dc1.add({"dn": user3_dn, "objectclass": "user"})
925 self.ldb_dc1.add({"dn": group_dn, "objectclass": "group"})
927 u1_guid = misc.GUID(self.ldb_dc1.search(base=user1_dn,
928 attrs=["objectGUID"])[0]['objectGUID'][0])
929 u2_guid = misc.GUID(self.ldb_dc1.search(base=user2_dn,
930 attrs=["objectGUID"])[0]['objectGUID'][0])
931 u3_guid = misc.GUID(self.ldb_dc1.search(base=user3_dn,
932 attrs=["objectGUID"])[0]['objectGUID'][0])
933 g_guid = misc.GUID(self.ldb_dc1.search(base=group_dn,
934 attrs=["objectGUID"])[0]['objectGUID'][0])
936 self.add_linked_attribute(group_dn, user1_dn,
938 self.add_linked_attribute(group_dn, user2_dn,
940 self.add_linked_attribute(group_dn, user3_dn,
942 self.add_linked_attribute(group_dn, user1_dn,
944 self.add_linked_attribute(group_dn, user2_dn,
945 attr='nonSecurityMember')
946 self.add_linked_attribute(group_dn, user3_dn,
947 attr='nonSecurityMember')
949 set_inactive = AbstractLink(drsuapi.DRSUAPI_ATTID_nonSecurityMember,
950 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
953 expected_links = set([set_inactive,
954 AbstractLink(drsuapi.DRSUAPI_ATTID_member,
955 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
958 AbstractLink(drsuapi.DRSUAPI_ATTID_member,
959 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
962 AbstractLink(drsuapi.DRSUAPI_ATTID_member,
963 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
966 AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy,
967 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
970 AbstractLink(drsuapi.DRSUAPI_ATTID_nonSecurityMember,
971 drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE,
976 dc_guid_1 = self.ldb_dc1.get_invocation_id()
978 drs, drs_handle = self._ds_bind(self.dnsname_dc1)
980 req8 = self._exop_req8(dest_dsa=None,
981 invocation_id=dc_guid_1,
983 exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ)
985 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
988 for link in ctr.linked_attributes:
989 target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3,
990 link.value.blob).guid
991 no_inactive.append((link, target_guid))
992 self.assertTrue(AbstractLink(link.attid, link.flags,
993 link.identifier.guid,
994 target_guid) in expected_links)
996 no_inactive.sort(cmp=_linked_attribute_compare)
998 # assert the two arrays are the same
999 self.assertEqual(len(expected_links), ctr.linked_attributes_count)
1000 self.assertEqual([x[0] for x in no_inactive], ctr.linked_attributes)
1002 self.remove_linked_attribute(group_dn, user3_dn,
1003 attr='nonSecurityMember')
1005 # Set the link inactive
1006 expected_links.remove(set_inactive)
1007 set_inactive.flags = 0
1008 expected_links.add(set_inactive)
1011 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
1012 for link in ctr.linked_attributes:
1013 target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3,
1014 link.value.blob).guid
1015 has_inactive.append((link, target_guid))
1016 self.assertTrue(AbstractLink(link.attid, link.flags,
1017 link.identifier.guid,
1018 target_guid) in expected_links)
1020 has_inactive.sort(cmp=_linked_attribute_compare)
1022 # assert the two arrays are the same
1023 self.assertEqual(len(expected_links), ctr.linked_attributes_count)
1024 self.assertEqual([x[0] for x in has_inactive], ctr.linked_attributes)
1026 def test_sort_behaviour_ncchanges(self):
1027 """Testing sorting behaviour on a group of objects."""
1028 user1_dn = "cn=test_user1,%s" % self.ou
1029 group_dn = "cn=test_group,%s" % self.ou
1030 self.ldb_dc1.add({"dn": user1_dn, "objectclass": "user"})
1031 self.ldb_dc1.add({"dn": group_dn, "objectclass": "group"})
1033 self.add_linked_attribute(group_dn, user1_dn,
1036 dc_guid_1 = self.ldb_dc1.get_invocation_id()
1038 drs, drs_handle = self._ds_bind(self.dnsname_dc1)
1040 # Make sure the max objects count is high enough
1041 req8 = self._exop_req8(dest_dsa=None,
1042 invocation_id=dc_guid_1,
1043 nc_dn_str=self.base_dn,
1046 exop=drsuapi.DRSUAPI_EXOP_NONE)
1048 # Loop until we get linked attributes, or we get to the end.
1049 # Samba sends linked attributes at the end, unlike Windows.
1051 (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8)
1052 if ctr.more_data == 0 or ctr.linked_attributes_count != 0:
1054 req8.highwatermark = ctr.new_highwatermark
1056 self.assertTrue(ctr.linked_attributes_count != 0)
1059 for link in ctr.linked_attributes:
1061 target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3,
1062 link.value.blob).guid
1064 target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3Binary,
1065 link.value.blob).guid
1066 no_inactive.append((link, target_guid))
1068 no_inactive.sort(cmp=_linked_attribute_compare)
1070 # assert the two arrays are the same
1071 self.assertEqual([x[0] for x in no_inactive], ctr.linked_attributes)