def operation_79(self, op): if self.update_exists(op): return self.raise_if_not_fix(op) ace = "(OA;CIIO;WP;ea1b7b93-5e48-46d5-bc6c-4df4fda78a35;bf967a86-0de6-11d0-a285-00aa003049e2;PS)" res = self.samdb.search(expression="(objectClass=samDomain)", attrs=["nTSecurityDescriptor"], controls=["search_options:1:2"]) for msg in res: existing_sd = ndr_unpack(security.descriptor, msg["nTSecurityDescriptor"][0]) existing_sddl = existing_sd.as_sddl(self.domain_sid) self.insert_ace_into_dacl(msg.dn, existing_sddl, ace) res = self.samdb.search(expression="(objectClass=domainDNS)", attrs=["nTSecurityDescriptor"], controls=["search_options:1:2"]) for msg in res: existing_sd = ndr_unpack(security.descriptor, msg["nTSecurityDescriptor"][0]) existing_sddl = existing_sd.as_sddl(self.domain_sid) self.insert_ace_into_dacl(msg.dn, existing_sddl, ace) if self.add_update_container: self.update_add(op)
def operation_81(self, op): if self.update_exists(op): return self.raise_if_not_fix(op) ace = "(OA;CIOI;RPWP;3f78c3e5-f79a-46bd-a0b8-9d18116ddc79;;PS)" res = self.samdb.search(expression="(objectClass=samDomain)", attrs=["nTSecurityDescriptor"], controls=["search_options:1:2"]) for msg in res: existing_sd = ndr_unpack(security.descriptor, msg["nTSecurityDescriptor"][0]) existing_sddl = existing_sd.as_sddl(self.domain_sid) self.insert_ace_into_dacl(msg.dn, existing_sddl, ace) res = self.samdb.search(expression="(objectClass=domainDNS)", attrs=["nTSecurityDescriptor"], controls=["search_options:1:2"]) for msg in res: existing_sd = ndr_unpack(security.descriptor, msg["nTSecurityDescriptor"][0]) existing_sddl = existing_sd.as_sddl(self.domain_sid) self.insert_ace_into_dacl(msg.dn, existing_sddl, ace) if self.add_update_container: self.update_add(op)
def test_dirsync_deleted_items(self): """Check that dirsync returnd deleted objects too""" # Let's create an OU ouname="OU=testou3,%s" % self.base_dn self.ouname = ouname self.ldb_admin.create_ou(ouname) res = self.ldb_admin.search(self.base_dn, expression="(&(objectClass=organizationalUnit)(!(isDeleted=*)))", controls=["dirsync:1:0:1"]) guid = None for e in res: if str(e["name"]) == "testou3": guid = str(ndr_unpack(misc.GUID,e.get("objectGUID")[0])) ctl = str(res.controls[0]).split(":") ctl[1] = "1" ctl[2] = "0" ctl[3] = "10000" control1 = str(":".join(ctl)) # So now delete the object and check that # we can see the object but deleted when admin delete_force(self.ldb_admin, ouname) res = self.ldb_admin.search(self.base_dn, expression="(objectClass=organizationalUnit)", controls=[control1]) self.assertEqual(len(res), 1) guid2 = str(ndr_unpack(misc.GUID,res[0].get("objectGUID")[0])) self.assertEqual(guid2, guid) self.assertTrue(res[0].get("isDeleted")) self.assertTrue(res[0].get("name") != None)
def setUp(self): super(DynamicTokenTest, self).setUp() self.admin_ldb = SamDB(url, credentials=creds, session_info=system_session(lp), lp=lp) self.base_dn = self.admin_ldb.domain_dn() self.test_user = "******" self.test_user_pass = "******" self.admin_ldb.newuser(self.test_user, self.test_user_pass) self.test_group0 = "tokengroups_group0" self.admin_ldb.newgroup(self.test_group0, grouptype=dsdb.GTYPE_SECURITY_DOMAIN_LOCAL_GROUP) res = self.admin_ldb.search(base="cn=%s,cn=users,%s" % (self.test_group0, self.base_dn), attrs=["objectSid"], scope=ldb.SCOPE_BASE) self.test_group0_sid = ndr_unpack(samba.dcerpc.security.dom_sid, res[0]["objectSid"][0]) self.admin_ldb.add_remove_group_members(self.test_group0, [self.test_user], add_members_operation=True) self.test_group1 = "tokengroups_group1" self.admin_ldb.newgroup(self.test_group1, grouptype=dsdb.GTYPE_SECURITY_GLOBAL_GROUP) res = self.admin_ldb.search(base="cn=%s,cn=users,%s" % (self.test_group1, self.base_dn), attrs=["objectSid"], scope=ldb.SCOPE_BASE) self.test_group1_sid = ndr_unpack(samba.dcerpc.security.dom_sid, res[0]["objectSid"][0]) self.admin_ldb.add_remove_group_members(self.test_group1, [self.test_user], add_members_operation=True) self.test_group2 = "tokengroups_group2" self.admin_ldb.newgroup(self.test_group2, grouptype=dsdb.GTYPE_SECURITY_UNIVERSAL_GROUP) res = self.admin_ldb.search(base="cn=%s,cn=users,%s" % (self.test_group2, self.base_dn), attrs=["objectSid"], scope=ldb.SCOPE_BASE) self.test_group2_sid = ndr_unpack(samba.dcerpc.security.dom_sid, res[0]["objectSid"][0]) self.admin_ldb.add_remove_group_members(self.test_group2, [self.test_user], add_members_operation=True) self.ldb = self.get_ldb_connection(self.test_user, self.test_user_pass) res = self.ldb.search("", scope=ldb.SCOPE_BASE, attrs=["tokenGroups"]) self.assertEquals(len(res), 1) self.user_sid_dn = "<SID=%s>" % str(ndr_unpack(samba.dcerpc.security.dom_sid, res[0]["tokenGroups"][0])) res = self.ldb.search(self.user_sid_dn, scope=ldb.SCOPE_BASE, attrs=[]) self.assertEquals(len(res), 1) self.test_user_dn = res[0].dn session_info_flags = ( AUTH_SESSION_INFO_DEFAULT_GROUPS | AUTH_SESSION_INFO_AUTHENTICATED | AUTH_SESSION_INFO_SIMPLE_PRIVILEGES) session = samba.auth.user_session(self.ldb, lp_ctx=lp, dn=self.user_sid_dn, session_info_flags=session_info_flags) token = session.security_token self.user_sids = [] for s in token.sids: self.user_sids.append(str(s))
def _get_identifier(self, ldb_conn, dn): res = ldb_conn.search(dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID", "objectSid"]) id = drsuapi.DsReplicaObjectIdentifier() id.guid = ndr_unpack(misc.GUID, res[0]['objectGUID'][0]) if "objectSid" in res[0]: id.sid = ndr_unpack(security.dom_sid, res[0]['objectSid'][0]) id.dn = str(res[0].dn) return id
def _check_ctr6(self, ctr6, expected_dns=[], expected_links=[], dn_ordered=True, links_ordered=True, more_data=False, nc_object_count=0, nc_linked_attributes_count=0, drs_error=0): """ Check that a ctr6 matches the specified parameters. """ self.assertEqual(ctr6.object_count, len(expected_dns)) self.assertEqual(ctr6.linked_attributes_count, len(expected_links)) self.assertEqual(ctr6.more_data, more_data) self.assertEqual(ctr6.nc_object_count, nc_object_count) self.assertEqual(ctr6.nc_linked_attributes_count, nc_linked_attributes_count) self.assertEqual(ctr6.drs_error[0], drs_error) ctr6_dns = [] next_object = ctr6.first_object for i in range(0, ctr6.object_count): ctr6_dns.append(next_object.object.identifier.dn) next_object = next_object.next_object self.assertEqual(next_object, None) i = 0 for dn in expected_dns: # Expect them back in the exact same order as specified. if dn_ordered: self.assertNotEqual(ctr6_dns[i], None) self.assertEqual(ctr6_dns[i], dn) i = i + 1 # Don't care what order else: self.assertTrue(dn in ctr6_dns, "Couldn't find DN '%s' anywhere in ctr6 response." % dn) ctr6_links = [] expected_links.sort() lidx = 0 for lidx in range(0, ctr6.linked_attributes_count): l = ctr6.linked_attributes[lidx] try: target = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3, l.value.blob) except: target = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3Binary, l.value.blob) al = AbstractLink(l.attid, l.flags, l.identifier.guid, target.guid) ctr6_links.append(al) lidx = 0 for el in expected_links: if links_ordered: self.assertEqual(el, ctr6_links[lidx]) lidx += 1 else: self.assertTrue(el in ctr6_links, "Couldn't find link '%s' anywhere in ctr6 response." % el)
def test_userPassword_cleartext_sha256(self): self.add_user(clear_text=True, options=[("password hash userPassword schemes", "CryptSHA256:rounds=100")]) sc = self.get_supplemental_creds() # Check that we got all the expected supplemental credentials # And they are in the expected order. size = len(sc.sub.packages) self.assertEquals(6, size) (pos, package) = get_package(sc, "Primary:Kerberos-Newer-Keys") self.assertEquals(1, pos) self.assertEquals("Primary:Kerberos-Newer-Keys", package.name) (pos, package) = get_package(sc, "Primary:Kerberos") self.assertEquals(2, pos) self.assertEquals("Primary:Kerberos", package.name) (pos, wd_package) = get_package(sc, "Primary:WDigest") self.assertEquals(3, pos) self.assertEquals("Primary:WDigest", wd_package.name) (pos, ct_package) = get_package(sc, "Primary:CLEARTEXT") self.assertEquals(4, pos) self.assertEquals("Primary:CLEARTEXT", ct_package.name) (pos, package) = get_package(sc, "Packages") self.assertEquals(5, pos) self.assertEquals("Packages", package.name) (pos, up_package) = get_package(sc, "Primary:userPassword") self.assertEquals(6, pos) self.assertEquals("Primary:userPassword", up_package.name) # Check that the WDigest values are correct. # digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, binascii.a2b_hex(wd_package.data)) self.check_wdigests(digests) # Check the clear text value is correct. ct = ndr_unpack(drsblobs.package_PrimaryCLEARTEXTBlob, binascii.a2b_hex(ct_package.data)) self.assertEquals(USER_PASS.encode('utf-16-le'), ct.cleartext) # Check that the userPassword hashes are computed correctly # up = ndr_unpack(drsblobs.package_PrimaryUserPasswordBlob, binascii.a2b_hex(up_package.data)) self.checkUserPassword(up, [("{CRYPT}", "5",100 )]) self.checkNtHash(USER_PASS, up.current_nt_hash.hash)
def test_userPassword_multiple_hashes_rounds_specified(self): self.add_user(options=[( "password hash userPassword schemes", "CryptSHA512:rounds=5120 CryptSHA256:rounds=2560 CryptSHA512:rounds=5122")]) sc = self.get_supplemental_creds() # Check that we got all the expected supplemental credentials # And they are in the expected order. size = len(sc.sub.packages) self.assertEquals(6, size) (pos, package) = get_package(sc, "Primary:Kerberos-Newer-Keys") self.assertEquals(1, pos) self.assertEquals("Primary:Kerberos-Newer-Keys", package.name) (pos, package) = get_package(sc, "Primary:Kerberos") self.assertEquals(2, pos) self.assertEquals("Primary:Kerberos", package.name) (pos, wp_package) = get_package(sc, "Primary:WDigest") self.assertEquals(3, pos) self.assertEquals("Primary:WDigest", wp_package.name) (pos, up_package) = get_package(sc, "Primary:userPassword") self.assertEquals(4, pos) self.assertEquals("Primary:userPassword", up_package.name) (pos, package) = get_package(sc, "Packages") self.assertEquals(5, pos) self.assertEquals("Packages", package.name) (pos, package) = get_package(sc, "Primary:SambaGPG") self.assertEquals(6, pos) self.assertEquals("Primary:SambaGPG", package.name) # Check that the WDigest values are correct. # digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, binascii.a2b_hex(wp_package.data)) self.check_wdigests(digests) # Check that the userPassword hashes are computed correctly # Expect three hashes to be calculated up = ndr_unpack(drsblobs.package_PrimaryUserPasswordBlob, binascii.a2b_hex(up_package.data)) self.checkUserPassword(up, [ ("{CRYPT}", "6", 5120), ("{CRYPT}", "5", 2560), ("{CRYPT}", "6", 5122) ]) self.checkNtHash(USER_PASS, up.current_nt_hash.hash)
def test_cname(self): s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1') ad = contact_real_server(server_ip, 53) name = "resolve.cname" p = self.make_name_packet(dns.DNS_OPCODE_QUERY) questions = [] q = self.make_name_question(name, dns.DNS_QTYPE_CNAME, dns.DNS_QCLASS_IN) questions.append(q) self.finish_name_packet(p, questions) p.operation |= dns.DNS_FLAG_RECURSION_DESIRED send_packet = ndr.ndr_pack(p) ad.send(send_packet, 0) ad.settimeout(timeout) try: data = ad.recv(0xffff + 2, 0) data = ndr.ndr_unpack(dns.name_packet, data) self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK) self.assertEqual(len(data.answers), 1) self.assertEqual('forwarder1', data.answers[0].rdata) except socket.timeout: self.fail("DNS server is too slow (timeout %s)" % timeout)
def test_modify_nonreplicated_reps_attributes(self): # some timestamp ones dn = self.base_dn m = ldb.Message() m.dn = ldb.Dn(self.samdb, dn) attr = 'repsFrom' res = self.samdb.search(dn, scope=ldb.SCOPE_BASE, attrs=['repsFrom']) rep = ndr_unpack(drsblobs.repsFromToBlob, res[0]['repsFrom'][0], allow_remaining=True) rep.ctr.result_last_attempt = -1 value = ndr_pack(rep) m[attr] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, attr) try: self.samdb.modify(m) self.fail("Failed to fail to modify %s %s" % (dn, attr)) except ldb.LdbError as e3: (ecode, emsg) = e3.args if ecode != ldb.ERR_REFERRAL: self.fail("Failed to REFER when trying to modify %s %s" % (dn, attr)) else: m = re.search(r'(ldap://[^>]+)>', emsg) if m is None: self.fail("referral seems not to refer to anything") address = m.group(1) if address.lower().startswith(self.samdb.domain_dns_name()): self.fail("referral address did not give a specific DC")
def test_array_from_ndr(self): rmd = drsblobs.replPropertyMetaDataBlob() rmd.version = 1 rmd.ctr = drsblobs.replPropertyMetaDataCtr1() rmd.ctr.count = 3 rmd1 = drsblobs.replPropertyMetaData1() rmd1.attid = 1 rmd1.version = 2 rmd2 = drsblobs.replPropertyMetaData1() rmd2.attid = 2 rmd2.version = 2 rmd3 = drsblobs.replPropertyMetaData1() rmd3.attid = 3 rmd3.version = 2 rmd.ctr.array = [rmd1, rmd2, rmd3] packed = ndr_pack(rmd) gc.collect() rmd_unpacked = ndr_unpack(drsblobs.replPropertyMetaDataBlob, packed) self.assertIsNotNone(rmd_unpacked) self.assertEqual(rmd_unpacked.version, 1) self.assertIsNotNone(rmd_unpacked.ctr) self.assertEqual(rmd_unpacked.ctr.count, 3) self.assertEqual(len(rmd_unpacked.ctr.array), rmd_unpacked.ctr.count) self.assertIsNotNone(rmd_unpacked.ctr.array[0]) self.assertEqual(rmd_unpacked.ctr.array[0].attid, 1) self.assertEqual(rmd.ctr.array[0].attid, rmd_unpacked.ctr.array[0].attid)
def _check_metadata(self, metadata, expected): repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(metadata[0])) repl_array = [] for o in repl.ctr.array: repl_array.append((o.attid, o.version)) repl_set = set(repl_array) expected_set = set(expected) self.assertEqual(len(repl_set), len(expected), "Unexpected metadata, missing from expected (%s), extra (%s)), repl: \n%s" % ( str(expected_set.difference(repl_set)), str(repl_set.difference(expected_set)), ndr_print(repl))) i = 0 for o in repl.ctr.array: e = expected[i] (attid, version) = e self.assertEquals(attid, o.attid, "(LDAP) Wrong attid " "for expected value %d, wanted 0x%08x got 0x%08x, " "repl: \n%s" % (i, attid, o.attid, ndr_print(repl))) # Allow version to be skipped when it does not matter if version is not None: self.assertEquals(o.version, version, "(LDAP) Wrong version for expected value %d, " "attid 0x%08x, " "wanted %d got %d, repl: \n%s" % (i, o.attid, version, o.version, ndr_print(repl))) i = i + 1
def dns_transaction_tcp(self, packet, host, dump=False, timeout=None): "send a DNS query and read the reply, also return the raw packet" s = None if timeout is None: timeout = self.timeout try: send_packet = ndr.ndr_pack(packet) if dump: print(self.hexdump(send_packet)) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) s.settimeout(timeout) s.connect((host, 53)) tcp_packet = struct.pack('!H', len(send_packet)) tcp_packet += send_packet s.sendall(tcp_packet) recv_packet = s.recv(0xffff + 2, 0) if dump: print(self.hexdump(recv_packet)) response = ndr.ndr_unpack(dns.name_packet, recv_packet[2:]) finally: if s is not None: s.close() # unpacking and packing again should produce same bytestream my_packet = ndr.ndr_pack(response) self.assertEquals(my_packet, recv_packet[2:]) return (response, recv_packet[2:])
def test_cname_forwarding_with_server_down(self): if len(dns_servers) < 2: print "Ignoring test_cname_forwarding_with_server_down" return s2 = self.start_toy_server(dns_servers[1], 53, 'forwarder2') name1 = 'resolve1.cname.%s' % self.get_dns_domain() name2 = 'resolve2.cname.%s' % self.get_dns_domain() self.make_cname_update(name1, name2) self.make_cname_update(name2, "dsfsfds.dsfsdfs") ad = contact_real_server(server_ip, 53) p = self.make_name_packet(dns.DNS_OPCODE_QUERY) questions = [] q = self.make_name_question(name1, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN) questions.append(q) self.finish_name_packet(p, questions) p.operation |= dns.DNS_FLAG_RECURSION_DESIRED send_packet = ndr.ndr_pack(p) ad.send(send_packet, 0) ad.settimeout(timeout) try: data = ad.recv(0xffff + 2, 0) data = ndr.ndr_unpack(dns.name_packet, data) self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK) self.assertEqual('forwarder2', data.answers[-1].rdata) except socket.timeout: self.fail("DNS server is too slow (timeout %s)" % timeout)
def test_double_forwarder_both_slow(self): if len(dns_servers) < 2: print "Ignoring test_double_forwarder_both_slow" return s1 = self.start_toy_server(dns_servers[0], 53, 'forwarder1') s2 = self.start_toy_server(dns_servers[1], 53, 'forwarder2') s1.send('timeout 1.5', 0) s2.send('timeout 1.5', 0) ad = contact_real_server(server_ip, 53) name = "dsfsfds.dsfsdfs" p = self.make_name_packet(dns.DNS_OPCODE_QUERY) questions = [] q = self.make_name_question(name, dns.DNS_QTYPE_CNAME, dns.DNS_QCLASS_IN) questions.append(q) self.finish_name_packet(p, questions) p.operation |= dns.DNS_FLAG_RECURSION_DESIRED send_packet = ndr.ndr_pack(p) ad.send(send_packet, 0) ad.settimeout(timeout) try: data = ad.recv(0xffff + 2, 0) data = ndr.ndr_unpack(dns.name_packet, data) self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK) self.assertEqual('forwarder1', data.answers[0].rdata) except socket.timeout: self.fail("DNS server is too slow (timeout %s)" % timeout)
def test_no_flag_recursive_forwarder(self): ad = contact_real_server(server_ip, 53) name = "dsfsfds.dsfsdfs" p = self.make_name_packet(dns.DNS_OPCODE_QUERY) questions = [] q = self.make_name_question(name, dns.DNS_QTYPE_TXT, dns.DNS_QCLASS_IN) questions.append(q) self.finish_name_packet(p, questions) send_packet = ndr.ndr_pack(p) self.finish_name_packet(p, questions) # Leave off the recursive flag send_packet = ndr.ndr_pack(p) ad.send(send_packet, 0) ad.settimeout(timeout) try: data = ad.recv(0xffff + 2, 0) data = ndr.ndr_unpack(dns.name_packet, data) self.assert_dns_rcode_equals(data, dns.DNS_RCODE_NXDOMAIN) self.assertEqual(data.ancount, 0) except socket.timeout: self.fail("DNS server is too slow (timeout %s)" % timeout)
def test_no_active_forwarder(self): ad = contact_real_server(server_ip, 53) name = "dsfsfds.dsfsdfs" p = self.make_name_packet(dns.DNS_OPCODE_QUERY) questions = [] q = self.make_name_question(name, dns.DNS_QTYPE_TXT, dns.DNS_QCLASS_IN) questions.append(q) self.finish_name_packet(p, questions) send_packet = ndr.ndr_pack(p) self.finish_name_packet(p, questions) p.operation |= dns.DNS_FLAG_RECURSION_DESIRED send_packet = ndr.ndr_pack(p) ad.send(send_packet, 0) ad.settimeout(timeout) try: data = ad.recv(0xffff + 2, 0) data = ndr.ndr_unpack(dns.name_packet, data) self.assert_dns_rcode_equals(data, dns.DNS_RCODE_SERVFAIL) self.assertEqual(data.ancount, 0) except socket.timeout: self.fail("DNS server is too slow (timeout %s)" % timeout)
def setUp(self): super(UserAccountControlTests, self).setUp() self.admin_creds = creds self.admin_samdb = SamDB(url=ldaphost, session_info=system_session(), credentials=self.admin_creds, lp=lp) self.domain_sid = security.dom_sid(self.admin_samdb.get_domain_sid()) self.base_dn = self.admin_samdb.domain_dn() self.unpriv_user = "******" self.unpriv_user_pw = "samba123@" self.unpriv_creds = self.get_creds(self.unpriv_user, self.unpriv_user_pw) delete_force(self.admin_samdb, "CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn)) delete_force(self.admin_samdb, "OU=test_computer_ou1,%s" % (self.base_dn)) delete_force(self.admin_samdb, "CN=%s,CN=Users,%s" % (self.unpriv_user, self.base_dn)) self.admin_samdb.newuser(self.unpriv_user, self.unpriv_user_pw) res = self.admin_samdb.search("CN=%s,CN=Users,%s" % (self.unpriv_user, self.admin_samdb.domain_dn()), scope=SCOPE_BASE, attrs=["objectSid"]) self.assertEqual(1, len(res)) self.unpriv_user_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]) self.unpriv_user_dn = res[0].dn self.samdb = SamDB(url=ldaphost, credentials=self.unpriv_creds, lp=lp) self.samr = samr.samr("ncacn_ip_tcp:%s[seal]" % host, lp, self.unpriv_creds) self.samr_handle = self.samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED) self.samr_domain = self.samr.OpenDomain(self.samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, self.domain_sid) self.sd_utils = sd_utils.SDUtils(self.admin_samdb) self.admin_samdb.create_ou("OU=test_computer_ou1," + self.base_dn) self.unpriv_user_sid = self.sd_utils.get_object_sid(self.unpriv_user_dn) mod = "(OA;;CC;bf967a86-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.unpriv_user_sid) old_sd = self.sd_utils.read_sd_on_dn("OU=test_computer_ou1," + self.base_dn) self.sd_utils.dacl_add_ace("OU=test_computer_ou1," + self.base_dn, mod) self.add_computer_ldap("testcomputer-t") self.sd_utils.modify_sd_on_dn("OU=test_computer_ou1," + self.base_dn, old_sd) self.computernames = ["testcomputer-0"] # Get the SD of the template account, then force it to match # what we expect for SeMachineAccountPrivilege accounts, so we # can confirm we created the accounts correctly self.sd_reference_cc = self.sd_utils.read_sd_on_dn("CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn)) self.sd_reference_modify = self.sd_utils.read_sd_on_dn("CN=testcomputer-t,OU=test_computer_ou1,%s" % (self.base_dn)) for ace in self.sd_reference_modify.dacl.aces: if ace.type == security.SEC_ACE_TYPE_ACCESS_ALLOWED and ace.trustee == self.unpriv_user_sid: ace.access_mask = ace.access_mask | security.SEC_ADS_SELF_WRITE | security.SEC_ADS_WRITE_PROP # Now reconnect without domain admin rights self.samdb = SamDB(url=ldaphost, credentials=self.unpriv_creds, lp=lp)
def read_descriptor(self, samdb, object_dn): res = samdb.search(base=object_dn, scope=SCOPE_BASE, attrs=["nTSecurityDescriptor"]) # we should theoretically always have an SD assert(len(res) == 1) desc = res[0]["nTSecurityDescriptor"][0] return ndr_unpack(security.descriptor, desc)
def findprovisionrange(samdb, basedn): """ Find ranges of usn grouped by invocation id and then by timestamp rouned at 1 minute :param samdb: An LDB object pointing to the samdb :param basedn: The DN of the forest :return: A two level dictionary with invoication id as the first level, timestamp as the second one and then max, min, and number as subkeys, representing respectivily the maximum usn for the range, the minimum usn and the number of object with usn in this range. """ nb_obj = 0 hash_id = {} res = samdb.search( base=basedn, expression="objectClass=*", scope=ldb.SCOPE_SUBTREE, attrs=["replPropertyMetaData"], controls=["search_options:1:2"], ) for e in res: nb_obj = nb_obj + 1 obj = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(e["replPropertyMetaData"])).ctr for o in obj.array: # like a timestamp but with the resolution of 1 minute minutestamp = _glue.nttime2unix(o.originating_change_time) / 60 hash_ts = hash_id.get(str(o.originating_invocation_id)) if hash_ts is None: ob = {} ob["min"] = o.originating_usn ob["max"] = o.originating_usn ob["num"] = 1 ob["list"] = [str(e.dn)] hash_ts = {} else: ob = hash_ts.get(minutestamp) if ob is None: ob = {} ob["min"] = o.originating_usn ob["max"] = o.originating_usn ob["num"] = 1 ob["list"] = [str(e.dn)] else: if ob["min"] > o.originating_usn: ob["min"] = o.originating_usn if ob["max"] < o.originating_usn: ob["max"] = o.originating_usn if not (str(e.dn) in ob["list"]): ob["num"] = ob["num"] + 1 ob["list"].append(str(e.dn)) hash_ts[minutestamp] = ob hash_id[str(o.originating_invocation_id)] = hash_ts return (hash_id, nb_obj)
def test_default_supplementalCredentials(self): self.add_user() sc = self.get_supplemental_creds() # Check that we got all the expected supplemental credentials # And they are in the expected order. size = len(sc.sub.packages) self.assertEquals(3, size) (pos, package) = get_package(sc, "Primary:Kerberos") self.assertEquals(1, pos) self.assertEquals("Primary:Kerberos", package.name) (pos, package) = get_package(sc, "Packages") self.assertEquals(2, pos) self.assertEquals("Packages", package.name) (pos, package) = get_package(sc, "Primary:WDigest") self.assertEquals(3, pos) self.assertEquals("Primary:WDigest", package.name) # Check that the WDigest values are correct. # digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, binascii.a2b_hex(package.data)) self.check_wdigests(digests)
def test_cleanup_multi_srv_record(self): """ Test dns cleanup command for multi-valued SRV record. Steps: - Add 2 A records host1 and host2 - Add a SRV record srv1 and points to both host1 and host2 - Run cleanup command for host1 - Check records for srv1, data for host1 should be gone and host2 is kept. """ hosts = ['host1', 'host2'] # A record names srv_name = 'srv1' # add A records for host in hosts: self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone, host, "A", self.testip, self.creds_string) # the above A record points to this host dnshostname = '{}.{}'.format(host, self.zone.lower()) # add a SRV record points to above host srv_record = "{} 65530 65530 65530".format(dnshostname) self.runsubcmd("dns", "add", os.environ["SERVER"], self.zone, srv_name, "SRV", srv_record, self.creds_string) records = self.samdb.search( base="DC=DomainDnsZones,{}".format(self.samdb.get_default_basedn()), scope=ldb.SCOPE_SUBTREE, expression="(&(objectClass=dnsNode)(name={}))".format(srv_name), attrs=['dnsRecord']) # should have 2 records here self.assertEqual(len(records[0]['dnsRecord']), 2) # cleanup record for dns host1 dnshostname1 = 'host1.{}'.format(self.zone.lower()) self.runsubcmd("dns", "cleanup", os.environ["SERVER"], dnshostname1, self.creds_string) records = self.samdb.search( base="DC=DomainDnsZones,{}".format(self.samdb.get_default_basedn()), scope=ldb.SCOPE_SUBTREE, expression="(&(objectClass=dnsNode)(name={}))".format(srv_name), attrs=['dnsRecord', 'dNSTombstoned']) # dnsRecord for host1 should be deleted self.assertEqual(len(records[0]['dnsRecord']), 1) # unpack data dns_record_bin = records[0]['dnsRecord'][0] dns_record_obj = ndr_unpack(dnsp.DnssrvRpcRecord, dns_record_bin) # dnsRecord for host2 is still there and is the only one dnshostname2 = 'host2.{}'.format(self.zone.lower()) self.assertEqual(dns_record_obj.data.nameTarget, dnshostname2) # assert that the record isn't spuriously tombstoned self.assertTrue('dNSTombstoned' not in records[0] or str(record['dNSTombstoned']) == 'FALSE')
def run(self, gpo, H=None, sambaopts=None, credopts=None, versionopts=None): self.lp = sambaopts.get_loadparm() self.creds = credopts.get_credentials(self.lp, fallback_machine=True) self.url = dc_url(self.lp, self.creds, H) samdb_connect(self) try: msg = get_gpo_info(self.samdb, gpo)[0] except Exception: raise CommandError("GPO '%s' does not exist" % gpo) try: secdesc_ndr = msg['nTSecurityDescriptor'][0] secdesc = ndr_unpack(security.descriptor, secdesc_ndr) secdesc_sddl = secdesc.as_sddl() except Exception: secdesc_sddl = "<hidden>" self.outf.write("GPO : %s\n" % msg['name'][0]) self.outf.write("display name : %s\n" % msg['displayName'][0]) self.outf.write("path : %s\n" % msg['gPCFileSysPath'][0]) self.outf.write("dn : %s\n" % msg.dn) self.outf.write("version : %s\n" % attr_default(msg, 'versionNumber', '0')) self.outf.write("flags : %s\n" % gpo_flags_string(int(attr_default(msg, 'flags', 0)))) self.outf.write("ACL : %s\n" % secdesc_sddl) self.outf.write("\n")
def getntacl(lp, file, backend=None, eadbfile=None, direct_db_access=True): if direct_db_access: (backend_obj, dbname) = checkset_backend(lp, backend, eadbfile) if dbname is not None: try: attribute = backend_obj.wrap_getxattr(dbname, file, xattr.XATTR_NTACL_NAME) except Exception: # FIXME: Don't catch all exceptions, just those related to opening # xattrdb print "Fail to open %s" % dbname attribute = samba.xattr_native.wrap_getxattr(file, xattr.XATTR_NTACL_NAME) else: attribute = samba.xattr_native.wrap_getxattr(file, xattr.XATTR_NTACL_NAME) ntacl = ndr_unpack(xattr.NTACL, attribute) if ntacl.version == 1: return ntacl.info elif ntacl.version == 2: return ntacl.info.sd elif ntacl.version == 3: return ntacl.info.sd elif ntacl.version == 4: return ntacl.info.sd else: return smbd.get_nt_acl(file, security.SECINFO_OWNER | security.SECINFO_GROUP | security.SECINFO_DACL | security.SECINFO_SACL)
def _samdb_fetch_pfm_and_schi(self): """Fetch prefixMap and schemaInfo stored in SamDB using LDB connection""" samdb = self.ldb_dc1 res = samdb.search(base=samdb.get_schema_basedn(), scope=SCOPE_BASE, attrs=["prefixMap", "schemaInfo"]) pfm = ndr_unpack(drsblobs.prefixMapBlob, str(res[0]['prefixMap'])) schi = drsuapi.DsReplicaOIDMapping() schi.id_prefix = 0 if 'schemaInfo' in res[0]: schi.oid.length = len(map(ord, str(res[0]['schemaInfo']))) schi.oid.binary_oid = map(ord, str(res[0]['schemaInfo'])) else: schema_info = drsblobs.schemaInfoBlob() schema_info.revision = 0 schema_info.marker = 0xFF schema_info.invocation_id = misc.GUID(samdb.get_invocation_id()) schi.oid.length = len(map(ord, ndr_pack(schema_info))) schi.oid.binary_oid = map(ord, ndr_pack(schema_info)) pfm.ctr.mappings = pfm.ctr.mappings + [schi] pfm.ctr.num_mappings += 1 return pfm.ctr
def _open_samr_user(self, res): self.assertTrue("objectSid" in res[0]) (domain_sid, rid) = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]).split() self.assertEquals(self.domain_sid, domain_sid) return self.samr.OpenUser(self.samr_domain, security.SEC_FLAG_MAXIMUM_ALLOWED, rid)
def get_attribute_replmetadata_version(self, dn, att): """Get the version field trom the replPropertyMetaData for the given field :param dn: The on which we want to get the version :param att: The name of the attribute :return: The value of the version field in the replPropertyMetaData for the given attribute. None if the attribute is not replicated """ res = self.search(expression="distinguishedName=%s" % dn, scope=ldb.SCOPE_SUBTREE, controls=["search_options:1:2"], attrs=["replPropertyMetaData"]) if len(res) == 0: return None repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(res[0]["replPropertyMetaData"])) ctr = repl.ctr if len(self.hash_oid_name.keys()) == 0: self._populate_oid_attid() for o in ctr.array: # Search for Description att_oid = self.get_oid_from_attid(o.attid) if self.hash_oid_name.has_key(att_oid) and\ att.lower() == self.hash_oid_name[att_oid].lower(): return o.version return None
def decode(self): keys = [] krb = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, self._raw) assert krb.version == 4 for key in krb.ctr.keys: keys.append({'type': key.keytype, 'value': key.value, 'salt': krb.ctr.salt.string}) self.keys = keys
def check_rootdse(self): '''check the @ROOTDSE special object''' dn = ldb.Dn(self.samdb, '@ROOTDSE') if self.verbose: self.report("Checking object %s" % dn) res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE) if len(res) != 1: self.report("Object %s disappeared during check" % dn) return 1 obj = res[0] error_count = 0 # check that the dsServiceName is in GUID form if not 'dsServiceName' in obj: self.report('ERROR: dsServiceName missing in @ROOTDSE') return error_count+1 if not obj['dsServiceName'][0].startswith('<GUID='): self.report('ERROR: dsServiceName not in GUID form in @ROOTDSE') error_count += 1 if not self.confirm('Change dsServiceName to GUID form?'): return error_count res = self.samdb.search(base=ldb.Dn(self.samdb, obj['dsServiceName'][0]), scope=ldb.SCOPE_BASE, attrs=['objectGUID']) guid_str = str(ndr_unpack(misc.GUID, res[0]['objectGUID'][0])) m = ldb.Message() m.dn = dn m['dsServiceName'] = ldb.MessageElement("<GUID=%s>" % guid_str, ldb.FLAG_MOD_REPLACE, 'dsServiceName') if self.do_modify(m, [], "Failed to change dsServiceName to GUID form", validate=False): self.report("Changed dsServiceName to GUID form") return error_count
def test_supplementalCredentials_cleartext(self): self.add_user(clear_text=True) if not self.lp.get("password hash gpg key ids"): self.skipTest("No password hash gpg key ids, " + "Primary:SambaGPG will not be generated"); sc = self.get_supplemental_creds() # Check that we got all the expected supplemental credentials # And they are in the expected order. size = len(sc.sub.packages) self.assertEquals(6, size) (pos, package) = get_package(sc, "Primary:Kerberos-Newer-Keys") self.assertEquals(1, pos) self.assertEquals("Primary:Kerberos-Newer-Keys", package.name) (pos, package) = get_package(sc, "Primary:Kerberos") self.assertEquals(2, pos) self.assertEquals("Primary:Kerberos", package.name) (pos, wd_package) = get_package(sc, "Primary:WDigest") self.assertEquals(3, pos) self.assertEquals("Primary:WDigest", wd_package.name) (pos, ct_package) = get_package(sc, "Primary:CLEARTEXT") self.assertEquals(4, pos) self.assertEquals("Primary:CLEARTEXT", ct_package.name) (pos, package) = get_package(sc, "Packages") self.assertEquals(5, pos) self.assertEquals("Packages", package.name) (pos, package) = get_package(sc, "Primary:SambaGPG") self.assertEquals(6, pos) self.assertEquals("Primary:SambaGPG", package.name) # Check that the WDigest values are correct. # digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, binascii.a2b_hex(wd_package.data)) self.check_wdigests(digests) # Check the clear text value is correct. ct = ndr_unpack(drsblobs.package_PrimaryCLEARTEXTBlob, binascii.a2b_hex(ct_package.data)) self.assertEquals(USER_PASS.encode('utf-16-le'), ct.cleartext)
def test_encrypted_secrets(self): """Test that secret attributes are stored encrypted on disk""" basedn = self.ldb.domain_dn() backend_filename = "%s.ldb" % basedn.upper() backend_subpath = os.path.join("sam.ldb.d", backend_filename) backend_path = self.lp.private_path(backend_subpath) backenddb = ldb.Ldb("ldb://" + backend_path, flags=ldb.FLG_DONT_CREATE_DB) dn = "CN=Administrator,CN=Users,%s" % basedn res = backenddb.search(scope=ldb.SCOPE_BASE, base=dn, attrs=["unicodePwd"]) self.assertIs(True, len(res) > 0) obj = res[0] blob = obj["unicodePwd"][0] self.assertTrue(len(blob) > 30) # Now verify that the header contains the correct magic value. encrypted = ndr_unpack(drsblobs.EncryptedSecret, blob) magic = 0xca5caded self.assertEqual(magic, encrypted.header.magic)
def get_record_from_db(self, zone_name, record_name): zones = self.samdb.search(base="DC=DomainDnsZones,%s" % self.samdb.get_default_basedn(), scope=ldb.SCOPE_SUBTREE, expression="(objectClass=dnsZone)", attrs=["cn"]) for zone in zones: if zone_name in str(zone.dn): zone_dn = zone.dn break records = self.samdb.search(base=zone_dn, scope=ldb.SCOPE_SUBTREE, expression="(objectClass=dnsNode)", attrs=["dnsRecord"]) for old_packed_record in records: if record_name in str(old_packed_record.dn): return (old_packed_record.dn, ndr_unpack(dnsp.DnssrvRpcRecord, old_packed_record["dnsRecord"][0]))
def operation_129(self, op): if self.update_exists(op): return self.raise_if_not_fix(op) ace = "(OA;CIOI;RPWP;3f78c3e5-f79a-46bd-a0b8-9d18116ddc79;;PS)" schema_dn = ldb.Dn(self.samdb, "CN=Sam-Domain,%s" % str(self.schema_dn)) self.insert_ace_into_string(schema_dn, ace, attr='defaultSecurityDescriptor') res = self.samdb.search(expression="(objectClass=samDomain)", attrs=["nTSecurityDescriptor"], controls=["search_options:1:2"]) for msg in res: existing_sd = ndr_unpack(security.descriptor, msg["nTSecurityDescriptor"][0]) existing_sddl = existing_sd.as_sddl(self.domain_sid) self.insert_ace_into_dacl(msg.dn, existing_sddl, ace) if self.add_update_container: self.update_add(op)
def test_tokenGroupsGlobalAndUniversal_manual(self): # Manually run the tokenGroups algorithm from MS-ADTS 3.1.1.4.5.19 and MS-DRSR 4.1.8.3 # and compare the result # The variable names come from MS-ADTS May 15, 2014 S = set() S.add(self.test_user_dn.get_casefold()) self.filtered_closure(S, GTYPE_SECURITY_GLOBAL_GROUP) T = set() # Not really a SID, we do this on DNs... for sid in S: X = set() X.add(sid) self.filtered_closure(X, GTYPE_SECURITY_UNIVERSAL_GROUP) T = T.union(X) T.remove(self.test_user_dn.get_casefold()) tokenGroupsSet = set() res = self.ldb.search(self.user_sid_dn, scope=ldb.SCOPE_BASE, attrs=["tokenGroupsGlobalAndUniversal"]) self.assertEquals(len(res), 1) dn_tokengroups = [] for sid in res[0]['tokenGroupsGlobalAndUniversal']: sid = ndr_unpack(samba.dcerpc.security.dom_sid, sid) res3 = self.admin_ldb.search(base="<SID=%s>" % sid, scope=ldb.SCOPE_BASE, attrs=[]) tokenGroupsSet.add(res3[0].dn.get_casefold()) if len(T.difference(tokenGroupsSet)): self.fail(msg="additional calculated: %s" % T.difference(tokenGroupsSet)) if len(tokenGroupsSet.difference(T)): self.fail(msg="additional tokenGroupsGlobalAndUniversal: %s" % tokenGroupsSet.difference(T))
def decrypt_supplementalCredentials(connector, spl_crypt): assert len(spl_crypt) >= 20 confounder = spl_crypt[0:16] enc_buffer = spl_crypt[16:] m5 = hashlib.md5() m5.update(connector.drs.user_session_key) m5.update(confounder) enc_key = m5.digest() rc4 = Crypto.Cipher.ARC4.new(enc_key) plain_buffer = rc4.decrypt(enc_buffer) (crc32_v) = struct.unpack("<L", plain_buffer[0:4]) attr_val = plain_buffer[4:] crc32_c = binascii.crc32(attr_val) & 0xffffffff assert int( crc32_v[0]) == int(crc32_c), "CRC32 0x%08X != 0x%08X" % (crc32_v[0], crc32_c) return ndr_unpack(drsblobs.supplementalCredentialsBlob, attr_val)
def _expected_user_del_attributes(self, username, _guid, _sid): guid = ndr_unpack(misc.GUID, _guid) dn = "CN=%s\\0ADEL:%s,CN=Deleted Objects,%s" % (username, guid, self.base_dn) cn = "%s\nDEL:%s" % (username, guid) return {'dn': dn, 'objectClass': '**', 'cn': cn, 'distinguishedName': dn, 'isDeleted': 'TRUE', 'isRecycled': 'TRUE', 'instanceType': '4', 'whenCreated': '**', 'whenChanged': '**', 'uSNCreated': '**', 'uSNChanged': '**', 'name': cn, 'objectGUID': _guid, 'userAccountControl': '546', 'objectSid': _sid, 'sAMAccountName': username, 'lastKnownParent': 'CN=Users,%s' % self.base_dn, }
def dns_transaction_tcp(self, packet, host=os.getenv('SERVER_IP'), dump=False): "send a DNS query and read the reply" s = None try: send_packet = ndr.ndr_pack(packet) if dump: print self.hexdump(send_packet) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) s.connect((host, 53)) tcp_packet = struct.pack('!H', len(send_packet)) tcp_packet += send_packet s.send(tcp_packet, 0) recv_packet = s.recv(0xffff + 2, 0) if dump: print self.hexdump(recv_packet) return ndr.ndr_unpack(dns.name_packet, recv_packet[2:]) finally: if s is not None: s.close()
def test_single_forwarder_not_actually_there(self): ad = contact_real_server(server_ip, 53) name = "dsfsfds.dsfsdfs" p = self.make_name_packet(dns.DNS_OPCODE_QUERY) questions = [] q = self.make_name_question(name, dns.DNS_QTYPE_CNAME, dns.DNS_QCLASS_IN) questions.append(q) self.finish_name_packet(p, questions) p.operation |= dns.DNS_FLAG_RECURSION_DESIRED send_packet = ndr.ndr_pack(p) ad.send(send_packet, 0) ad.settimeout(timeout) try: data = ad.recv(0xffff + 2, 0) data = ndr.ndr_unpack(dns.name_packet, data) self.assert_dns_rcode_equals(data, dns.DNS_RCODE_SERVFAIL) except socket.timeout: self.fail("DNS server is too slow (timeout %s)" % timeout)
def dns_transaction_udp(self, packet, host=server_ip, dump=False, timeout=timeout): "send a DNS query and read the reply" s = None try: send_packet = ndr.ndr_pack(packet) if dump: print(self.hexdump(send_packet)) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) s.settimeout(timeout) s.connect((host, 53)) s.send(send_packet, 0) recv_packet = s.recv(2048, 0) if dump: print(self.hexdump(recv_packet)) return ndr.ndr_unpack(dns.name_packet, recv_packet) finally: if s is not None: s.close()
def test_dn_tokenGroups(self): print("Getting tokenGroups from user DN") res = self.ldb.search(self.user_sid_dn, scope=ldb.SCOPE_BASE, attrs=["tokenGroups"]) self.assertEquals(len(res), 1) dn_tokengroups = [] for sid in res[0]['tokenGroups']: dn_tokengroups.append( str(ndr_unpack(samba.dcerpc.security.dom_sid, sid))) sidset1 = set(dn_tokengroups) sidset2 = set(self.user_sids) if len(sidset1.difference(sidset2)): print("token sids don't match") print("tokengroups: %s" % tokengroups) print("calculated : %s" % sids) print("difference : %s" % sidset1.difference(sidset2)) self.fail( msg="calculated groups don't match against user DN tokenGroups" )
def run(self, groupname, credopts=None, sambaopts=None, versionopts=None, H=None): lp = sambaopts.get_loadparm() creds = credopts.get_credentials(lp, fallback_machine=True) try: samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp) search_filter = "(&(objectClass=group)(samaccountname=%s))" % groupname res = samdb.search(samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression=(search_filter), attrs=["objectSid"]) if (len(res) != 1): return group_dn = res[0].get('dn', idx=0) object_sid = res[0].get('objectSid', idx=0) object_sid = ndr_unpack(security.dom_sid, object_sid) (group_dom_sid, rid) = object_sid.split() search_filter = "(|(primaryGroupID=%s)(memberOf=%s))" % (rid, group_dn) res = samdb.search(samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression=(search_filter), attrs=["samAccountName", "cn"]) if (len(res) == 0): return for msg in res: member_name = msg.get("samAccountName", idx=0) if member_name is None: member_name = msg.get("cn", idx=0) self.outf.write("%s\n" % member_name) except Exception as e: raise CommandError('Failed to list members of "%s" group ' % groupname, e)
def _test_pac_align_with_args(self, length): samdb = self.get_samdb() account_name = self.base_name + 'a' * (length - len(self.base_name)) creds, _ = self.create_account(samdb, account_name) tgt = self.get_tgt(creds, expect_pac=True) pac_data = self.get_ticket_pac(tgt) self.assertIsNotNone(pac_data) self.assertEqual(0, len(pac_data) & 7) pac = ndr_unpack(krb5pac.PAC_DATA_RAW, pac_data) for pac_buffer in pac.buffers: buffer_type = pac_buffer.type buffer_size = pac_buffer.ndr_size with self.subTest(buffer_type=buffer_type): if buffer_type == krb5pac.PAC_TYPE_LOGON_NAME: self.assertEqual(length * 2 + 10, buffer_size) elif buffer_type == krb5pac.PAC_TYPE_REQUESTER_SID: self.assertEqual(28, buffer_size) elif buffer_type in { krb5pac.PAC_TYPE_SRV_CHECKSUM, krb5pac.PAC_TYPE_KDC_CHECKSUM, krb5pac.PAC_TYPE_TICKET_CHECKSUM }: self.assertEqual( 0, buffer_size & 3, f'buffer type was: {buffer_type}, ' f'buffer size was: {buffer_size}') else: self.assertEqual( 0, buffer_size & 7, f'buffer type was: {buffer_type}, ' f'buffer size was: {buffer_size}') rounded_len = (buffer_size + 7) & ~7 self.assertEqual(rounded_len, len(pac_buffer.info.remaining))
def setUp(self): super(StaticTokenTest, self).setUp() self.ldb = SamDB(url, credentials=creds, session_info=system_session(lp), lp=lp) self.base_dn = self.ldb.domain_dn() res = self.ldb.search("", scope=ldb.SCOPE_BASE, attrs=["tokenGroups"]) self.assertEquals(len(res), 1) self.user_sid_dn = "<SID=%s>" % str(ndr_unpack(samba.dcerpc.security.dom_sid, res[0]["tokenGroups"][0])) session_info_flags = (AUTH_SESSION_INFO_DEFAULT_GROUPS | AUTH_SESSION_INFO_AUTHENTICATED | AUTH_SESSION_INFO_SIMPLE_PRIVILEGES) if creds.get_kerberos_state() == DONT_USE_KERBEROS: session_info_flags |= AUTH_SESSION_INFO_NTLM session = samba.auth.user_session(self.ldb, lp_ctx=lp, dn=self.user_sid_dn, session_info_flags=session_info_flags) token = session.security_token self.user_sids = [] for s in token.sids: self.user_sids.append(str(s))
def operation_88(self, op): if self.update_exists(op): return self.raise_if_not_fix(op) ace = "(OA;CIIO;WP;ea1b7b93-5e48-46d5-bc6c-4df4fda78a35;bf967a86-0de6-11d0-a285-00aa003049e2;PS)" schema_dn = ldb.Dn(self.samdb, "CN=Sam-Domain,%s" % str(self.schema_dn)) self.insert_ace_into_string(schema_dn, ace, attr="defaultSecurityDescriptor") res = self.samdb.search(expression="(objectClass=samDomain)", attrs=["nTSecurityDescriptor"], controls=["search_options:1:2"]) for msg in res: existing_sd = ndr_unpack(security.descriptor, msg["nTSecurityDescriptor"][0]) existing_sddl = existing_sd.as_sddl(self.domain_sid) self.insert_ace_into_dacl(msg.dn, existing_sddl, ace) if self.add_update_container: self.update_add(op)
def test_twoatt_replpropertymetadata(self): res = self.samdb.search(expression="cn=Administrator", scope=ldb.SCOPE_SUBTREE, attrs=["replPropertyMetaData", "uSNChanged"]) repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob, str(res[0]["replPropertyMetaData"])) ctr = repl.ctr for o in ctr.array: # Search for Description if o.attid == 13: old_version = o.version o.version = o.version + 1 o.local_usn = long(str(res[0]["uSNChanged"])) + 1 replBlob = ndr_pack(repl) msg = ldb.Message() msg.dn = res[0].dn msg["replPropertyMetaData"] = ldb.MessageElement( replBlob, ldb.FLAG_MOD_REPLACE, "replPropertyMetaData") msg["description"] = ldb.MessageElement("new val", ldb.FLAG_MOD_REPLACE, "description") self.assertRaises(ldb.LdbError, self.samdb.modify, msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
def test_default_supplementalCredentials(self): self.add_user() if not self.lp.get("password hash gpg key ids"): self.skipTest("No password hash gpg key ids, " + "Primary:SambaGPG will not be generated") sc = self.get_supplemental_creds() # Check that we got all the expected supplemental credentials # And they are in the expected order. size = len(sc.sub.packages) self.assertEquals(5, size) (pos, package) = get_package(sc, "Primary:Kerberos-Newer-Keys") self.assertEquals(1, pos) self.assertEquals("Primary:Kerberos-Newer-Keys", package.name) (pos, package) = get_package(sc, "Primary:Kerberos") self.assertEquals(2, pos) self.assertEquals("Primary:Kerberos", package.name) (pos, wd_package) = get_package(sc, "Primary:WDigest") self.assertEquals(3, pos) self.assertEquals("Primary:WDigest", wd_package.name) (pos, package) = get_package(sc, "Packages") self.assertEquals(4, pos) self.assertEquals("Packages", package.name) (pos, package) = get_package(sc, "Primary:SambaGPG") self.assertEquals(5, pos) self.assertEquals("Primary:SambaGPG", package.name) # Check that the WDigest values are correct. # digests = ndr_unpack(drsblobs.package_PrimaryWDigestBlob, binascii.a2b_hex(wd_package.data)) self.check_wdigests(digests)
def check_rootdse(self): '''check the @ROOTDSE special object''' dn = ldb.Dn(self.samdb, '@ROOTDSE') if self.verbose: self.report("Checking object %s" % dn) res = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE) if len(res) != 1: self.report("Object %s disappeared during check" % dn) return 1 obj = res[0] error_count = 0 # check that the dsServiceName is in GUID form if not 'dsServiceName' in obj: self.report('ERROR: dsServiceName missing in @ROOTDSE') return error_count + 1 if not obj['dsServiceName'][0].startswith('<GUID='): self.report('ERROR: dsServiceName not in GUID form in @ROOTDSE') error_count += 1 if not self.confirm('Change dsServiceName to GUID form?'): return error_count res = self.samdb.search(base=ldb.Dn(self.samdb, obj['dsServiceName'][0]), scope=ldb.SCOPE_BASE, attrs=['objectGUID']) guid_str = str(ndr_unpack(misc.GUID, res[0]['objectGUID'][0])) m = ldb.Message() m.dn = dn m['dsServiceName'] = ldb.MessageElement("<GUID=%s>" % guid_str, ldb.FLAG_MOD_REPLACE, 'dsServiceName') if self.do_modify(m, [], "Failed to change dsServiceName to GUID form", validate=False): self.report("Changed dsServiceName to GUID form") return error_count
def test_cname_forwarding_with_lots_of_cnames(self): name3 = 'resolve3.cname.%s' % self.get_dns_domain() s1 = self.start_toy_server(dns_servers[0], 53, name3) name1 = 'resolve1.cname.%s' % self.get_dns_domain() name2 = 'resolve2.cname.%s' % self.get_dns_domain() self.make_cname_update(name1, name2) self.make_cname_update(name3, name1) self.make_cname_update(name2, "dsfsfds.dsfsdfs") ad = contact_real_server(server_ip, 53) p = self.make_name_packet(dns.DNS_OPCODE_QUERY) questions = [] q = self.make_name_question(name1, dns.DNS_QTYPE_A, dns.DNS_QCLASS_IN) questions.append(q) self.finish_name_packet(p, questions) p.operation |= dns.DNS_FLAG_RECURSION_DESIRED send_packet = ndr.ndr_pack(p) ad.send(send_packet, 0) ad.settimeout(timeout) try: data = ad.recv(0xffff + 2, 0) data = ndr.ndr_unpack(dns.name_packet, data) # This should cause a loop in Windows # (which is restricted by a 20 CNAME limit) # # The reason it doesn't here is because forwarded CNAME have no # additional processing in the internal DNS server. self.assert_dns_rcode_equals(data, dns.DNS_RCODE_OK) self.assertEqual(name3, data.answers[-1].rdata) except socket.timeout: self.fail("DNS server is too slow (timeout %s)" % timeout)
def operation_80(self, op): if self.update_exists(op): return self.raise_if_not_fix(op) ace = "(OA;;CR;3e0f7e18-2c7a-4c10-ba82-4d926db99a3e;;%s-522)" % str( self.domain_sid) res = self.samdb.search( base=self.domain_dn, scope=ldb.SCOPE_BASE, attrs=["nTSecurityDescriptor"], controls=["search_options:1:2", "sd_flags:1:%d" % SECINFO_DACL]) msg = res[0] existing_sd = ndr_unpack(security.descriptor, msg["nTSecurityDescriptor"][0]) existing_sddl = existing_sd.as_sddl(self.domain_sid) self.insert_ace_into_dacl(msg.dn, existing_sddl, ace) if self.add_update_container: self.update_add(op)
def _find_dns_record(self, name, ip_address): name = name.rstrip('$') # computername records = self.samdb.search( base="DC=DomainDnsZones,{0}".format( self.samdb.get_default_basedn()), scope=ldb.SCOPE_SUBTREE, expression="(&(objectClass=dnsNode)(name={0}))".format(name), attrs=['dnsRecord', 'dNSTombstoned']) # unpack data and compare for record in records: if 'dNSTombstoned' in record and str( record['dNSTombstoned']) == 'TRUE': # if a record is dNSTombstoned, ignore it. continue for dns_record_bin in record['dnsRecord']: dns_record_obj = ndr_unpack(dnsp.DnssrvRpcRecord, dns_record_bin) ip = str(dns_record_obj.data) if str(ip) == str(ip_address): return True return False
def getntacl(lp, file, backend=None, eadbfile=None, direct_db_access=True, service=None, session_info=None): if direct_db_access: (backend_obj, dbname) = checkset_backend(lp, backend, eadbfile) if dbname is not None: try: attribute = backend_obj.wrap_getxattr(dbname, file, xattr.XATTR_NTACL_NAME) except Exception: # FIXME: Don't catch all exceptions, just those related to opening # xattrdb print("Fail to open %s" % dbname) attribute = samba.xattr_native.wrap_getxattr( file, xattr.XATTR_NTACL_NAME) else: attribute = samba.xattr_native.wrap_getxattr( file, xattr.XATTR_NTACL_NAME) ntacl = ndr_unpack(xattr.NTACL, attribute) if ntacl.version == 1: return ntacl.info elif ntacl.version == 2: return ntacl.info.sd elif ntacl.version == 3: return ntacl.info.sd elif ntacl.version == 4: return ntacl.info.sd else: return smbd.get_nt_acl(file, SECURITY_SECINFO_FLAGS, service=service, session_info=session_info)
def test_rootDSE_tokenGroups(self): """Testing rootDSE tokengroups against internal calculation""" if not url.startswith("ldap"): self.fail(msg="This test is only valid on ldap") res = self.ldb.search("", scope=ldb.SCOPE_BASE, attrs=["tokenGroups"]) self.assertEquals(len(res), 1) print("Getting tokenGroups from rootDSE") tokengroups = [] for sid in res[0]['tokenGroups']: tokengroups.append( str(ndr_unpack(samba.dcerpc.security.dom_sid, sid))) sidset1 = set(tokengroups) sidset2 = set(self.user_sids) if len(sidset1.difference(sidset2)): print("token sids don't match") print("tokengroups: %s" % tokengroups) print("calculated : %s" % self.user_sids) print("difference : %s" % sidset1.difference(sidset2)) self.fail( msg="calculated groups don't match against rootDSE tokenGroups" )
def test_samr_GetGroupsForUser_nomember(self): # Confirm that we get the correct results against SAMR also if not url.startswith("ldap://"): self.fail(msg="This test is only valid on ldap (so we an find the hostname and use SAMR)") host = url.split("://")[1] test_user = "******" self.admin_ldb.newuser(test_user, self.test_user_pass) res = self.admin_ldb.search(base="cn=%s,cn=users,%s" % (test_user, self.base_dn), attrs=["objectSid"], scope=ldb.SCOPE_BASE) user_sid = ndr_unpack(samba.dcerpc.security.dom_sid, res[0]["objectSid"][0]) (domain_sid, user_rid) = user_sid.split() samr_conn = samba.dcerpc.samr.samr("ncacn_ip_tcp:%s[seal]" % host, lp, creds) samr_handle = samr_conn.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED) samr_domain = samr_conn.OpenDomain(samr_handle, security.SEC_FLAG_MAXIMUM_ALLOWED, domain_sid) user_handle = samr_conn.OpenUser(samr_domain, security.SEC_FLAG_MAXIMUM_ALLOWED, user_rid) rids = samr_conn.GetGroupsForUser(user_handle) user_info = samr_conn.QueryUserInfo(user_handle, 1) delete_force(self.admin_ldb, "CN=%s,%s,%s" % (test_user, "cn=users", self.base_dn)) self.assertEqual(len(rids.rids), 1) self.assertEqual(rids.rids[0].rid, user_info.primary_gid)
def test_dn_tokenGroups(self): print("Getting tokenGroups from user DN") res = self.ldb.search(self.user_sid_dn, scope=ldb.SCOPE_BASE, attrs=["tokenGroups"]) self.assertEquals(len(res), 1) dn_tokengroups = [] for sid in res[0]['tokenGroups']: dn_tokengroups.append( str(ndr_unpack(samba.dcerpc.security.dom_sid, sid))) sidset1 = set(dn_tokengroups) sidset2 = set(self.user_sids) # The SIDs on the DN do not include the NTLM authentication SID sidset2.discard(samba.dcerpc.security.SID_NT_NTLM_AUTHENTICATION) if len(sidset1.difference(sidset2)): print("token sids don't match") print("difference : %s" % sidset1.difference(sidset2)) self.fail( msg="calculated groups don't match against user DN tokenGroups" )
def test_sort_behaviour_single_object(self): """Testing sorting behaviour on single objects""" user1_dn = "cn=test_user1,%s" % self.ou user2_dn = "cn=test_user2,%s" % self.ou user3_dn = "cn=test_user3,%s" % self.ou group_dn = "cn=test_group,%s" % self.ou self.ldb_dc1.add({"dn": user1_dn, "objectclass": "user"}) self.ldb_dc1.add({"dn": user2_dn, "objectclass": "user"}) self.ldb_dc1.add({"dn": user3_dn, "objectclass": "user"}) self.ldb_dc1.add({"dn": group_dn, "objectclass": "group"}) u1_guid = misc.GUID( self.ldb_dc1.search(base=user1_dn, attrs=["objectGUID"])[0]['objectGUID'][0]) u2_guid = misc.GUID( self.ldb_dc1.search(base=user2_dn, attrs=["objectGUID"])[0]['objectGUID'][0]) u3_guid = misc.GUID( self.ldb_dc1.search(base=user3_dn, attrs=["objectGUID"])[0]['objectGUID'][0]) g_guid = misc.GUID( self.ldb_dc1.search(base=group_dn, attrs=["objectGUID"])[0]['objectGUID'][0]) self.add_linked_attribute(group_dn, user1_dn, attr='member') self.add_linked_attribute(group_dn, user2_dn, attr='member') self.add_linked_attribute(group_dn, user3_dn, attr='member') self.add_linked_attribute(group_dn, user1_dn, attr='managedby') self.add_linked_attribute(group_dn, user2_dn, attr='nonSecurityMember') self.add_linked_attribute(group_dn, user3_dn, attr='nonSecurityMember') set_inactive = AbstractLink( drsuapi.DRSUAPI_ATTID_nonSecurityMember, drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, g_guid, u3_guid) expected_links = set([ set_inactive, AbstractLink(drsuapi.DRSUAPI_ATTID_member, drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, g_guid, u1_guid), AbstractLink(drsuapi.DRSUAPI_ATTID_member, drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, g_guid, u2_guid), AbstractLink(drsuapi.DRSUAPI_ATTID_member, drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, g_guid, u3_guid), AbstractLink(drsuapi.DRSUAPI_ATTID_managedBy, drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, g_guid, u1_guid), AbstractLink(drsuapi.DRSUAPI_ATTID_nonSecurityMember, drsuapi.DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE, g_guid, u2_guid), ]) dc_guid_1 = self.ldb_dc1.get_invocation_id() drs, drs_handle = self._ds_bind(self.dnsname_dc1) req8 = self._exop_req8(dest_dsa=None, invocation_id=dc_guid_1, nc_dn_str=group_dn, exop=drsuapi.DRSUAPI_EXOP_REPL_OBJ) (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) no_inactive = [] for link in ctr.linked_attributes: target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3, link.value.blob).guid no_inactive.append((link, target_guid)) self.assertTrue( AbstractLink(link.attid, link.flags, link.identifier.guid, target_guid) in expected_links) no_inactive.sort(cmp=_linked_attribute_compare) # assert the two arrays are the same self.assertEqual(len(expected_links), ctr.linked_attributes_count) self.assertEqual([x[0] for x in no_inactive], ctr.linked_attributes) self.remove_linked_attribute(group_dn, user3_dn, attr='nonSecurityMember') # Set the link inactive expected_links.remove(set_inactive) set_inactive.flags = 0 expected_links.add(set_inactive) has_inactive = [] (level, ctr) = drs.DsGetNCChanges(drs_handle, 8, req8) for link in ctr.linked_attributes: target_guid = ndr_unpack(drsuapi.DsReplicaObjectIdentifier3, link.value.blob).guid has_inactive.append((link, target_guid)) self.assertTrue( AbstractLink(link.attid, link.flags, link.identifier.guid, target_guid) in expected_links) has_inactive.sort(cmp=_linked_attribute_compare) # assert the two arrays are the same self.assertEqual(len(expected_links), ctr.linked_attributes_count) self.assertEqual([x[0] for x in has_inactive], ctr.linked_attributes)
def find_domain_sid(self): res = self.ldb.search(base=self.base_dn, expression="(objectClass=*)", scope=SCOPE_BASE) return ndr_unpack(security.dom_sid, res[0]["objectSid"][0])
def sid(msg): sid = ndr_unpack(security.dom_sid, msg["objectSID"][0]) (x, _) = sid.split() return x
def purge_computer_with_DC_objects(ucr, binddn, bindpw, computername): lp = LoadParm() lp.load('/etc/samba/smb.conf') samdb = SamDB(os.path.join(SAMBA_PRIVATE_DIR, "sam.ldb"), session_info=system_session(lp), lp=lp) backlink_attribute_list = [ "serverReferenceBL", "frsComputerReferenceBL", "msDFSR-ComputerReferenceBL" ] msgs = samdb.search( base=ucr["samba4/ldap/base"], scope=samba.ldb.SCOPE_SUBTREE, expression="(&(objectClass=computer)(sAMAccountName=%s$))" % computername, attrs=backlink_attribute_list) if not msgs: print "Samba 4 computer account '%s' not found." % (computername, ) sys.exit(1) answer = raw_input("Really remove %s from Samba 4? [y/N]: " % computername) if not answer.lower() in ('y', 'yes'): print "Ok, stopping as requested.\n" sys.exit(2) computer_obj = msgs[0] # Confirmation check answer = raw_input("If you are really sure type YES and hit enter: ") if not answer == 'YES': print "The answer was not 'YES', confirmation failed.\n" sys.exit(1) else: print "Ok, continuing as requested.\n" # Determine the NTDS_objectGUID NTDS_objectGUID = None if "serverReferenceBL" in computer_obj: msgs = samdb.search(base=computer_obj["serverReferenceBL"][0], scope=samba.ldb.SCOPE_SUBTREE, expression="(CN=NTDS Settings)", attrs=["objectGUID"]) if msgs and "objectGUID" in msgs[0]: NTDS_objectGUID = str( ndr_unpack(misc.GUID, msgs[0]["objectGUID"][0])) # Determine the Domain_GUID msgs = samdb.search(base=ucr["samba4/ldap/base"], scope=samba.ldb.SCOPE_BASE, attrs=["objectGUID"]) if not msgs: print "Samba 4 Domain_GUID for base dn '%s' not found." % ( ucr["samba4/ldap/base"], ) sys.exit(1) Domain_GUID = str(ndr_unpack(misc.GUID, msgs[0]["objectGUID"][0])) # Build current site list msgs = samdb.search(base="CN=Configuration,%s" % ucr["samba4/ldap/base"], scope=samba.ldb.SCOPE_SUBTREE, expression="(objectClass=site)", attrs=["cn"]) site_list = [obj["cn"][0] for obj in msgs] # Remove Samba 4 DNS records purge_s4_dns_records(ucr, binddn, bindpw, computername, NTDS_objectGUID, Domain_GUID, site_list) # remove objects from Samba 4 SAM database for backlink_attribute in backlink_attribute_list: if backlink_attribute in computer_obj: backlink_object = computer_obj[backlink_attribute][0] try: print "Removing %s from SAM database." % (backlink_object, ) samdb.delete(backlink_object, ["tree_delete:0"]) except: print >> sys.stderr, "Removal of Samba 4 %s objects %s from Samba 4 SAM database failed." % ( backlink_attribute, backlink_object, ) print traceback.format_exc() # Now delete the Samba 4 computer account and sub-objects # Cannot use tree_delete on isCriticalSystemObject, perform recursive delete like ldbdel code does it: msgs = samdb.search(base=computer_obj.dn, scope=samba.ldb.SCOPE_SUBTREE, attrs=["dn"]) obj_dn_list = [obj.dn for obj in msgs] obj_dn_list.sort(key=len) obj_dn_list.reverse() for obj_dn in obj_dn_list: try: print "Removing %s from SAM database." % (obj_dn, ) samdb.delete(obj_dn) except: print >> sys.stderr, "Removal of Samba 4 computer account object %s from Samba 4 SAM database failed." % ( obj_dn, ) print >> sys.stderr, traceback.format_exc() answer = raw_input("Really remove %s from UDM as well? [y/N]: " % computername) if not answer.lower() in ('y', 'yes'): print "Ok, stopping as requested.\n" sys.exit(2) # Finally, for consistency remove S4 computer object from UDM purge_udm_computer(ucr, binddn, bindpw, computername)
def rid(msg): sid = ndr_unpack(security.dom_sid, msg["objectSID"][0]) (_, rid) = sid.split() return rid