From ebcdc4a36be9b79325b11ec0c44a43db93e29519 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 23 Aug 2012 10:37:46 +1000 Subject: s4-samba-tool: Add samba-tool ntacl sysvolcheck command This command verifies that the current on-disk ACLs match the directory and the defaults from provision. Unlike sysvolreset, this does not change any of the permissions. Andrew Bartlett --- source4/scripting/python/samba/netcmd/ntacl.py | 34 +++++++ .../scripting/python/samba/provision/__init__.py | 110 ++++++++++++++++++++- 2 files changed, 143 insertions(+), 1 deletion(-) (limited to 'source4') diff --git a/source4/scripting/python/samba/netcmd/ntacl.py b/source4/scripting/python/samba/netcmd/ntacl.py index 81217b76d6..4a0c91c289 100644 --- a/source4/scripting/python/samba/netcmd/ntacl.py +++ b/source4/scripting/python/samba/netcmd/ntacl.py @@ -179,6 +179,39 @@ class cmd_ntacl_sysvolreset(Command): lp.get("realm").lower(), samdb.domain_dn(), lp, use_ntvfs=use_ntvfs) +class cmd_ntacl_sysvolcheck(Command): + """Check sysvol ACLs match defaults (including correct ACLs on GPOs)""" + synopsis = "%prog [options]" + + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "credopts": options.CredentialsOptions, + "versionopts": options.VersionOptions, + } + + def run(self, + credopts=None, sambaopts=None, versionopts=None): + lp = sambaopts.get_loadparm() + path = lp.private_path("secrets.ldb") + creds = credopts.get_credentials(lp) + creds.set_kerberos_state(DONT_USE_KERBEROS) + logger = self.get_logger() + + netlogon = lp.get("path", "netlogon") + sysvol = lp.get("path", "sysvol") + try: + samdb = SamDB(session_info=system_session(), + lp=lp) + except Exception, e: + raise CommandError("Unable to open samdb:", e) + + domain_sid = security.dom_sid(samdb.domain_sid) + + provision.checksysvolacl(samdb, netlogon, sysvol, + domain_sid, + lp.get("realm").lower(), samdb.domain_dn(), + lp) + class cmd_ntacl(SuperCommand): """NT ACLs manipulation""" @@ -187,4 +220,5 @@ class cmd_ntacl(SuperCommand): subcommands["set"] = cmd_ntacl_set() subcommands["get"] = cmd_ntacl_get() subcommands["sysvolreset"] = cmd_ntacl_sysvolreset() + subcommands["sysvolcheck"] = cmd_ntacl_sysvolcheck() diff --git a/source4/scripting/python/samba/provision/__init__.py b/source4/scripting/python/samba/provision/__init__.py index 4762e5b162..ed46b4040d 100644 --- a/source4/scripting/python/samba/provision/__init__.py +++ b/source4/scripting/python/samba/provision/__init__.py @@ -69,7 +69,7 @@ from samba.dsdb import ( ) from samba.idmap import IDmapDB from samba.ms_display_specifiers import read_ms_ldif -from samba.ntacls import setntacl, dsacl2fsacl +from samba.ntacls import setntacl, getntacl, dsacl2fsacl from samba.ndr import ndr_pack, ndr_unpack from samba.provision.backend import ( ExistingBackend, @@ -1455,6 +1455,114 @@ def setsysvolacl(samdb, netlogon, sysvol, uid, gid, domainsid, dnsdomain, domain # Set acls on Policy folder and policies folders set_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, use_ntvfs) +def acl_type(direct_db_access): + if direct_db_access: + return "DB" + else: + return "VFS" + +def check_dir_acl(path, acl, lp, domainsid, direct_db_access): + fsacl = getntacl(lp, path, direct_db_access=direct_db_access) + fsacl_sddl = fsacl.as_sddl(domainsid) + if fsacl_sddl != acl: + raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), path, fsacl_sddl, acl)) + + 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) + 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))) + fsacl_sddl = fsacl.as_sddl(domainsid) + if fsacl_sddl != acl: + raise ProvisioningError('%s ACL on GPO file %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl, acl)) + + for name in files: + fsacl = getntacl(lp, os.path.join(root, name), direct_db_access=direct_db_access) + 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))) + fsacl_sddl = fsacl.as_sddl(domainsid) + if fsacl_sddl != acl: + raise ProvisioningError('%s ACL on GPO directory %s %s does not match expected value %s from GPO object' % (acl_type(direct_db_access), os.path.join(root, name), fsacl, acl)) + + +def check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, direct_db_access): + """Set ACL on the sysvol//Policies folder and the policy + folders beneath. + + :param sysvol: Physical path for the sysvol folder + :param dnsdomain: The DNS name of the domain + :param domainsid: The SID of the domain + :param domaindn: The DN of the domain (ie. DC=...) + :param samdb: An LDB object on the SAM db + :param lp: an LP object + """ + + # Set ACL for GPO root folder + root_policy_path = os.path.join(sysvol, dnsdomain, "Policies") + fsacl = getntacl(lp, root_policy_path, direct_db_access=direct_db_access) + if fsacl is None: + raise ProvisioningError('DB ACL on policy root %s %s not found!' % (acl_type(direct_db_access), root_policy_path)) + fsacl_sddl = fsacl.as_sddl(domainsid) + if fsacl_sddl != POLICIES_ACL: + raise ProvisioningError('%s ACL on policy root %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), policy_root, fsacl_sddl, acl)) + res = samdb.search(base="CN=Policies,CN=System,%s"%(domaindn), + attrs=["cn", "nTSecurityDescriptor"], + expression="", scope=ldb.SCOPE_ONELEVEL) + + for policy in res: + acl = ndr_unpack(security.descriptor, + str(policy["nTSecurityDescriptor"])).as_sddl() + policy_path = getpolicypath(sysvol, dnsdomain, str(policy["cn"])) + check_dir_acl(policy_path, dsacl2fsacl(acl, str(domainsid)), lp, + domainsid, direct_db_access) + + +def checksysvolacl(samdb, netlogon, sysvol, domainsid, dnsdomain, domaindn, + lp): + """Set the ACL for the sysvol share and the subfolders + + :param samdb: An LDB object on the SAM db + :param netlogon: Physical path for the netlogon folder + :param sysvol: Physical path for the sysvol folder + :param uid: The UID of the "Administrator" user + :param gid: The GID of the "Domain adminstrators" group + :param domainsid: The SID of the domain + :param dnsdomain: The DNS name of the domain + :param domaindn: The DN of the domain (ie. DC=...) + """ + + # This will ensure that the smbd code we are running when setting ACLs is initialised with the smb.conf + s3conf = s3param.get_context() + s3conf.load(lp.configfile) + # ensure we are using the right samba4 passdb backend, no matter what + s3conf.set("passdb backend", "samba4:%s" % samdb.url) + # ensure that we init the samba4 backend, so the domain sid is marked in secrets.tdb + s4_passdb = passdb.PDB(s3conf.get("passdb backend")) + + # now ensure everything matches correctly, to avoid wierd issues + if passdb.get_global_sam_sid() != domainsid: + raise ProvisioningError('SID as seen by smbd [%s] does not match SID as seen by the provision script [%s]!' % (passdb.get_global_sam_sid(), domainsid)) + + domain_info = s4_passdb.domain_info() + if domain_info["dom_sid"] != domainsid: + raise ProvisioningError('SID as seen by pdb_samba4 [%s] does not match SID as seen by the provision script [%s]!' % (domain_info["dom_sid"], domainsid)) + + if domain_info["dns_domain"].upper() != dnsdomain.upper(): + raise ProvisioningError('Realm as seen by pdb_samba4 [%s] does not match Realm as seen by the provision script [%s]!' % (domain_info["dns_domain"].upper(), dnsdomain.upper())) + + # Set the SYSVOL_ACL on the sysvol folder and subfolder (first level) + for direct_db_access in [True, False]: + for dir_path in [os.path.join(sysvol, dnsdomain), netlogon]: + fsacl = getntacl(lp, dir_path, direct_db_access=direct_db_access) + if fsacl is None: + raise ProvisioningError('%s ACL on sysvol directory %s not found!' % (acl_type(direct_db_access), dir_path)) + fsacl_sddl = fsacl.as_sddl(domainsid) + if fsacl_sddl != SYSVOL_ACL: + raise ProvisioningError('%s ACL on sysvol directory %s %s does not match expected value %s from provision' % (acl_type(direct_db_access), dir_path, fsacl_sddl, SYSVOL_ACL)) + + # Check acls on Policy folder and policies folders + check_gpos_acl(sysvol, dnsdomain, domainsid, domaindn, samdb, lp, direct_db_access) + def interface_ips_v4(lp): '''return only IPv4 IPs''' -- cgit