--- /usr/lib/python2.7/dist-packages/samba/provision/__init__.py 2019-04-02 10:32:20.000000000 +0200 +++ __init__.py.patched 2019-04-15 14:29:01.750639642 +0200 @@ -1672,7 +1672,184 @@ return True return False -def check_dir_acl(path, acl, lp, domainsid, direct_db_access): +def check_dir_acl(path, acl_expected_for_gpo, lp, domainsid, direct_db_access): + try: + fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE) + fsacl_sddl = fsacl.as_sddl(domainsid) + except TypeError as error: + return + except NTSTATUSError as error: + if check_runtime_error(error, ntstatus.NT_STATUS_OBJECT_NAME_NOT_FOUND): + print "ERROR: File not found", path + return + else: + raise + + ## Sanitize "domainsid" to be a security.dom_sid + if isinstance(domainsid, str): + domainsid = security.dom_sid(domainsid) + + LA = security.dom_sid("%s-%d" % (domainsid, security.DOMAIN_RID_ADMINISTRATOR)) + DA = security.dom_sid("%s-%d" % (domainsid, security.DOMAIN_RID_ADMINS)) + CO = security.dom_sid(security.SID_CREATOR_OWNER) + + ## If LA in filesystem then treat it as DA for comparison (reversing what samba.ntacls.setntacl did) + if fsacl.owner_sid == LA: + fsacl.owner_sid = DA + + """ + MS doc about SE_DACL_AUTO_INHERITED of SECURITY_DESCRIPTOR_CONTROL: + For Windows 2000 ACLs that support auto-inheritance, this bit is always set. + """ + ## The default {31B2F340-016D-11D2-945F-00C04FB984F9} and {6AC1786C-016F-11D2-945F-00C04FB984F9} don't have this in Samba + ## so mask this difference here to avoid false positives + ## + ## sysvolreset removes the AI as well, so we always mask this: + fsacl.type |= security.SEC_DESC_DACL_AUTO_INHERITED + fix_P_to_AI = True ## also below in the subdir and file checks + + fsacl_sddl_mapped = fsacl.as_sddl(domainsid) + + ## at least in UCS, all base GPO directories have AI set, so expect that + sd = security.descriptor.from_sddl(acl_expected_for_gpo, domainsid) + sd.type |= security.SEC_DESC_DACL_AUTO_INHERITED + acl_expected_for_gpo = sd.as_sddl(domainsid) + + if fsacl_sddl_mapped != acl_expected_for_gpo: + raise ProvisioningError('%s NTACL of GPO directory %s does not match value expected from GPO object\nFSACL: %s\nDSACL: %s' % (acl_type(direct_db_access), path, fsacl_sddl_mapped, acl_expected_for_gpo)) + + ### After the base GPO directory has passed, now fix the FSACLs calculated by dsacl2fsacl to work for the subdirs and files + ## copy the security descriptor structure, we have to filter out a few ACEs + sd_masked = security.descriptor() + sd_masked.owner_sid = sd.owner_sid + sd_masked.group_sid = sd.group_sid + sd_masked.type = sd.type + sd_masked.revision = sd.revision + + ## the subdirs and files are AI and not P + sd_masked.type |= security.SEC_DESC_DACL_AUTO_INHERITED + sd_masked.type &= ~security.SEC_DESC_DACL_PROTECTED + + skip_other_da_aces = False + for i in range(0, len(sd.dacl.aces)): + if skip_other_da_aces and sd.dacl.aces[i].trustee in (DA, LA): + ## filter out additional ACEs for DA and LA, there is some duplication and ordering issue + continue + elif sd.dacl.aces[i].trustee in (DA, LA): + ## flag first occurrence of DA + skip_other_da_aces = True + if sd.dacl.aces[i].flags & security.SEC_ACE_FLAG_CONTAINER_INHERIT: + ## If GPO says CI then the subobjects must show the inherited flag + ## Note: dsacl2fsacl currently always fakes security.SEC_ACE_FLAG_CONTAINER_INHERIT + sd.dacl.aces[i].flags |= security.SEC_ACE_FLAG_INHERITED_ACE + + sd_masked.dacl_add(sd.dacl.aces[i]) + acl_expected_for_subdir = sd_masked.as_sddl(domainsid) + + ## Additionally for files MS AD and GPMC don't set the CI and OI flags + for i in range(0, len(sd_masked.dacl.aces)): + sd_masked.dacl.aces[i].flags &= ~ (security.SEC_ACE_FLAG_OBJECT_INHERIT | security.SEC_ACE_FLAG_CONTAINER_INHERIT) + try: + ## also filter out ACE for CO, not present for GPT.INI of new GPOs created via GPMC + sd_masked.dacl_del(CO) + except: + pass + acl_expected_for_file = sd_masked.as_sddl(domainsid) + sd_masked.type &= ~security.SEC_DESC_DACL_AUTO_INHERIT_REQ + acl_expected_for_registry_pol = sd_masked.as_sddl(domainsid) + + #print "ACL GPO: %s" % acl_expected_for_gpo + #print "ACL DIR: %s" % acl_expected_for_subdir + #print "ACL FILE: %s" % acl_expected_for_file + + for root, dirs, files in os.walk(path, topdown=False): + for name in files: + fsacl = getntacl(lp, os.path.join(root, name), + direct_db_access=direct_db_access, service=SYSVOL_SERVICE) + if fsacl is None: + raise ProvisioningError('%s ACL on GPO file %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name))) + + ## If LA in filesystem then treat it as DA for comparison (reversing what samba.ntacls.setntacl did) + if fsacl.owner_sid == LA: + fsacl.owner_sid = DA + + ## Mask current differences between Samba and MS AD & GPMC + fsacl_masked = security.descriptor() + fsacl_masked.owner_sid = fsacl.owner_sid + fsacl_masked.group_sid = fsacl.group_sid + fsacl_masked.type = fsacl.type + fsacl_masked.revision = fsacl.revision + + if fix_P_to_AI: + fsacl_masked.type &= ~security.SEC_DESC_DACL_PROTECTED + fsacl_masked.type |= security.SEC_DESC_DACL_AUTO_INHERITED + + skip_other_da_aces = False + for i in range(0, len(fsacl.dacl.aces)): + if skip_other_da_aces and fsacl.dacl.aces[i].trustee in (DA, LA): + ## filter out additional ACEs for DA and LA, there is some duplication and ordering issue + continue + elif fsacl.dacl.aces[i].trustee in (DA, LA): + ## flag first occurrence of DA + skip_other_da_aces = True + if str(fsacl.dacl.aces[i].trustee) == security.SID_CREATOR_OWNER: + ## filter out ACE for CO, not present for GPT.INI of new GPOs created via GPMC + continue + if fix_P_to_AI: + fsacl.dacl.aces[i].flags |= security.SEC_ACE_FLAG_INHERITED_ACE + ## The OI and CI flags don't make sense for files and are neither set by MS AD nor MS GPMC + fsacl.dacl.aces[i].flags &= ~ (security.SEC_ACE_FLAG_OBJECT_INHERIT | security.SEC_ACE_FLAG_CONTAINER_INHERIT) + fsacl_masked.dacl_add(fsacl.dacl.aces[i]) + + fsacl_sddl_mapped = fsacl_masked.as_sddl(domainsid) + + if name in ("GPT.INI", "GptTmpl.inf", "comment.cmtx"): + if fsacl_sddl_mapped != acl_expected_for_file: + raise ProvisioningError('%s NTACL of GPO file %s does not match value expected from GPO object\nFSACL: %s\nDSACL: %s' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl_mapped, acl_expected_for_file)) + else: + if fsacl_sddl_mapped != acl_expected_for_registry_pol: + raise ProvisioningError('%s NTACL of GPO file %s does not match value expected from GPO object\nFSACL: %s\nDSACL: %s' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl_mapped, acl_expected_for_registry_pol)) + + for name in dirs: + fsacl = getntacl(lp, os.path.join(root, name), + direct_db_access=direct_db_access, service=SYSVOL_SERVICE) + if fsacl is None: + raise ProvisioningError('%s ACL on GPO directory %s %s not found!' % (acl_type(direct_db_access), os.path.join(root, name))) + + ## If LA in filesystem then treat it as DA for comparison (reversing what samba.ntacls.setntacl did) + if fsacl.owner_sid == LA: + fsacl.owner_sid = DA + + ## Mask current differences between Samba and MS AD & GPMC + fsacl_masked = security.descriptor() + fsacl_masked.owner_sid = fsacl.owner_sid + fsacl_masked.group_sid = fsacl.group_sid + fsacl_masked.type = fsacl.type + fsacl_masked.revision = fsacl.revision + + if fix_P_to_AI: + fsacl_masked.type &= ~security.SEC_DESC_DACL_PROTECTED + fsacl_masked.type |= security.SEC_DESC_DACL_AUTO_INHERITED + + skip_other_da_aces = False + for i in range(0, len(fsacl.dacl.aces)): + if skip_other_da_aces and fsacl.dacl.aces[i].trustee in (DA, LA): + ## filter out additional ACEs for DA and LA, there is some duplication and ordering issue + continue + elif fsacl.dacl.aces[i].trustee == DA: + ## flag first occurrence of DA + skip_other_da_aces = True + if fix_P_to_AI: + fsacl.dacl.aces[i].flags |= security.SEC_ACE_FLAG_INHERITED_ACE + fsacl_masked.dacl_add(fsacl.dacl.aces[i]) + + fsacl_sddl_mapped = fsacl_masked.as_sddl(domainsid) + + if fsacl_sddl_mapped != acl_expected_for_subdir: + raise ProvisioningError('%s NTACL of GPO subdirectory %s does not match value expected from GPO object\nFSACL: %s\nDSACL: %s' % (acl_type(direct_db_access), os.path.join(root, name), fsacl_sddl_mapped, acl_expected_for_subdir)) + + +def check_dir_acl_orig(path, acl, lp, domainsid, direct_db_access): try: fsacl = getntacl(lp, path, direct_db_access=direct_db_access, service=SYSVOL_SERVICE) fsacl_sddl = fsacl.as_sddl(domainsid)