def get_clean_sd(sd): """Get the SD without any inherited ACEs :param sd: SD to strip :return: An SD with inherited ACEs stripped """ sd_clean = security.descriptor() sd_clean.owner_sid = sd.owner_sid sd_clean.group_sid = sd.group_sid sd_clean.type = sd.type sd_clean.revision = sd.revision aces = [] if sd.sacl is not None: aces = sd.sacl.aces for i in range(0, len(aces)): ace = aces[i] if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE: sd_clean.sacl_add(ace) continue aces = [] if sd.dacl is not None: aces = sd.dacl.aces for i in range(0, len(aces)): ace = aces[i] if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE: sd_clean.dacl_add(ace) continue return sd_clean
def dsacl2fsacl(dssddl, sid, as_sddl=True): """ This function takes an the SDDL representation of a DS ACL and return the SDDL representation of this ACL adapted for files. It's used for Policy object provision """ ref = security.descriptor.from_sddl(dssddl, sid) fdescr = security.descriptor() fdescr.owner_sid = ref.owner_sid fdescr.group_sid = ref.group_sid fdescr.type = ref.type fdescr.revision = ref.revision aces = ref.dacl.aces for i in range(0, len(aces)): ace = aces[i] if not ace.type & security.SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT and str(ace.trustee) != security.SID_BUILTIN_PREW2K: # if fdescr.type & security.SEC_DESC_DACL_AUTO_INHERITED: ace.flags = ace.flags | security.SEC_ACE_FLAG_OBJECT_INHERIT | security.SEC_ACE_FLAG_CONTAINER_INHERIT if str(ace.trustee) == security.SID_CREATOR_OWNER: # For Creator/Owner the IO flag is set as this ACE has only a sense for child objects ace.flags = ace.flags | security.SEC_ACE_FLAG_INHERIT_ONLY ace.access_mask = ldapmask2filemask(ace.access_mask) fdescr.dacl_add(ace) if not as_sddl: return fdescr return fdescr.as_sddl(sid)
def dsacl2fsacl(dssddl,domsid): sid = security.dom_sid(domsid) ref = security.descriptor.from_sddl(dssddl,sid) fdescr = security.descriptor() fdescr.owner_sid = ref.owner_sid fdescr.group_sid = ref.group_sid fdescr.type = ref.type fdescr.revision = ref.revision fdescr.sacl = ref.sacl aces = ref.dacl.aces for i in range(0,len(aces)): ace = aces[i] if not ace.type & security.SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT and str(ace.trustee) != security.SID_BUILTIN_PREW2K: # if fdescr.type & security.SEC_DESC_DACL_AUTO_INHERITED: ace.flags = ace.flags | security.SEC_ACE_FLAG_OBJECT_INHERIT | security.SEC_ACE_FLAG_CONTAINER_INHERIT if str(ace.trustee) == security.SID_CREATOR_OWNER: # For Creator/Owner the IO flag is set as this ACE has only a sense for child objects ace.flags = ace.flags | security.SEC_ACE_FLAG_INHERIT_ONLY ace.access_mask = ldapmask2filemask(ace.access_mask) fdescr.dacl_add(ace) return fdescr.as_sddl(sid)
def setUp(self): self.descriptor = security.descriptor()
def run(self, computername, credopts=None, sambaopts=None, versionopts=None, H=None, computerou=None, description=None, prepare_oldjoin=False, ip_address_list=None, service_principal_name_list=None): if ip_address_list is None: ip_address_list = [] if service_principal_name_list is None: service_principal_name_list = [] # check each IP address if provided for ip_address in ip_address_list: if not _is_valid_ip(ip_address): raise CommandError('Invalid IP address {}'.format(ip_address)) lp = sambaopts.get_loadparm() creds = credopts.get_credentials(lp) try: samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp) samdb.newcomputer( computername, computerou=computerou, description=description, prepare_oldjoin=prepare_oldjoin, ip_address_list=ip_address_list, service_principal_name_list=service_principal_name_list, ) if ip_address_list: # if ip_address_list provided, then we need to create DNS # records for this computer. hostname = re.sub(r"\$$", "", computername) if hostname.count('$'): raise CommandError('Illegal computername "%s"' % computername) filters = '(&(sAMAccountName={}$)(objectclass=computer))'.format( ldb.binary_encode(hostname)) recs = samdb.search(base=samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression=filters, attrs=['primaryGroupID', 'objectSid']) group = recs[0]['primaryGroupID'][0] owner = ndr_unpack(security.dom_sid, recs[0]["objectSid"][0]) dns_conn = dnsserver.dnsserver( "ncacn_ip_tcp:{}[sign]".format(samdb.host_dns_name()), lp, creds) change_owner_sd = security.descriptor() change_owner_sd.owner_sid = owner change_owner_sd.group_sid = security.dom_sid( "{}-{}".format(samdb.get_domain_sid(), group), ) add_dns_records(samdb, hostname, dns_conn, change_owner_sd, samdb.host_dns_name(), ip_address_list, self.get_logger()) except Exception as e: raise CommandError( "Failed to create computer '%s': " % computername, e) self.outf.write("Computer '%s' created successfully\n" % computername)
def process_sd(self, dn, obj): sd_attr = "nTSecurityDescriptor" sd_val = obj[sd_attr] sd = ndr_unpack(security.descriptor, str(sd_val)) is_deleted = 'isDeleted' in obj and obj['isDeleted'][0].upper( ) == 'TRUE' if is_deleted: # we don't fix deleted objects return (sd, None) sd_clean = security.descriptor() sd_clean.owner_sid = sd.owner_sid sd_clean.group_sid = sd.group_sid sd_clean.type = sd.type sd_clean.revision = sd.revision broken = False last_inherited_type = None aces = [] if sd.sacl is not None: aces = sd.sacl.aces for i in range(0, len(aces)): ace = aces[i] if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE: sd_clean.sacl_add(ace) continue t = self.ace_get_effective_inherited_type(ace) if t is None: continue if last_inherited_type is not None: if t != last_inherited_type: # if it inherited from more than # one type it's very likely to be broken # # If not the recalculation will calculate # the same result. broken = True continue last_inherited_type = t aces = [] if sd.dacl is not None: aces = sd.dacl.aces for i in range(0, len(aces)): ace = aces[i] if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE: sd_clean.dacl_add(ace) continue t = self.ace_get_effective_inherited_type(ace) if t is None: continue if last_inherited_type is not None: if t != last_inherited_type: # if it inherited from more than # one type it's very likely to be broken # # If not the recalculation will calculate # the same result. broken = True continue last_inherited_type = t if broken: return (sd_clean, sd) if last_inherited_type is None: # ok return (sd, None) cls = None try: cls = obj["objectClass"][-1] except KeyError, e: pass
m.dn = ldb.Dn(self.samdb, "CN=Machine,%s" % str(gpo_dn)) m['a01'] = ldb.MessageElement("container", ldb.FLAG_MOD_ADD, "objectClass") m['a02'] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_ADD, "showInAdvancedViewOnly") self.samdb.add(m) # Copy GPO files over SMB create_directory_hier(conn, sharepath) copy_directory_local_to_remote(conn, gpodir, sharepath) # Get new security descriptor msg = get_gpo_info(self.samdb, gpo=gpo)[0] ds_sd_ndr = msg['ntSecurityDescriptor'][0] ds_sd = ndr_unpack(security.descriptor, ds_sd_ndr).as_sddl() # Create a file system security descriptor fs_sd = security.descriptor(dsacl2fsacl(ds_sd, self.samdb.get_domain_sid())) # Set ACL conn.set_acl(sharepath, fs_sd) except: self.samdb.transaction_cancel() raise else: self.samdb.transaction_commit() self.outf.write("GPO '%s' created as %s\n" % (displayname, gpo)) class cmd_gpo(SuperCommand): """Group Policy Object (GPO) management"""
def setUp(self): super(SecurityDescriptorTests, self).setUp() self.descriptor = security.descriptor()
def process_sd(self, dn, obj): sd_attr = "nTSecurityDescriptor" sd_val = obj[sd_attr] sd = ndr_unpack(security.descriptor, str(sd_val)) is_deleted = 'isDeleted' in obj and obj['isDeleted'][0].upper() == 'TRUE' if is_deleted: # we don't fix deleted objects return (sd, None) sd_clean = security.descriptor() sd_clean.owner_sid = sd.owner_sid sd_clean.group_sid = sd.group_sid sd_clean.type = sd.type sd_clean.revision = sd.revision broken = False last_inherited_type = None aces = [] if sd.sacl is not None: aces = sd.sacl.aces for i in range(0, len(aces)): ace = aces[i] if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE: sd_clean.sacl_add(ace) continue t = self.ace_get_effective_inherited_type(ace) if t is None: continue if last_inherited_type is not None: if t != last_inherited_type: # if it inherited from more than # one type it's very likely to be broken # # If not the recalculation will calculate # the same result. broken = True continue last_inherited_type = t aces = [] if sd.dacl is not None: aces = sd.dacl.aces for i in range(0, len(aces)): ace = aces[i] if not ace.flags & security.SEC_ACE_FLAG_INHERITED_ACE: sd_clean.dacl_add(ace) continue t = self.ace_get_effective_inherited_type(ace) if t is None: continue if last_inherited_type is not None: if t != last_inherited_type: # if it inherited from more than # one type it's very likely to be broken # # If not the recalculation will calculate # the same result. broken = True continue last_inherited_type = t if broken: return (sd_clean, sd) if last_inherited_type is None: # ok return (sd, None) cls = None try: cls = obj["objectClass"][-1] except KeyError, e: pass
def run(self, computername, credopts=None, sambaopts=None, versionopts=None, H=None, computerou=None, description=None, prepare_oldjoin=False, ip_address_list=None, service_principal_name_list=None): if ip_address_list is None: ip_address_list = [] if service_principal_name_list is None: service_principal_name_list = [] # check each IP address if provided for ip_address in ip_address_list: if not _is_valid_ip(ip_address): raise CommandError('Invalid IP address {}'.format(ip_address)) lp = sambaopts.get_loadparm() creds = credopts.get_credentials(lp) try: samdb = SamDB(url=H, session_info=system_session(), credentials=creds, lp=lp) samdb.newcomputer(computername, computerou=computerou, description=description, prepare_oldjoin=prepare_oldjoin, ip_address_list=ip_address_list, service_principal_name_list=service_principal_name_list, ) if ip_address_list: # if ip_address_list provided, then we need to create DNS # records for this computer. hostname = re.sub(r"\$$", "", computername) if hostname.count('$'): raise CommandError('Illegal computername "%s"' % computername) filters = '(&(sAMAccountName={}$)(objectclass=computer))'.format( ldb.binary_encode(hostname)) recs = samdb.search( base=samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression=filters, attrs=['primaryGroupID', 'objectSid']) group = recs[0]['primaryGroupID'][0] owner = ndr_unpack(security.dom_sid, recs[0]["objectSid"][0]) dns_conn = dnsserver.dnsserver( "ncacn_ip_tcp:{}[sign]".format(samdb.host_dns_name()), lp, creds) change_owner_sd = security.descriptor() change_owner_sd.owner_sid = owner change_owner_sd.group_sid = security.dom_sid( "{}-{}".format(samdb.get_domain_sid(), group), ) add_dns_records( samdb, hostname, dns_conn, change_owner_sd, samdb.host_dns_name(), ip_address_list, self.get_logger()) except Exception as e: raise CommandError("Failed to create computer '%s': " % computername, e) self.outf.write("Computer '%s' created successfully\n" % computername)
"objectClass") m['a02'] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_ADD, "showInAdvancedViewOnly") self.samdb.add(m) # Copy GPO files over SMB create_directory_hier(conn, sharepath) copy_directory_local_to_remote(conn, gpodir, sharepath) # Get new security descriptor msg = get_gpo_info(self.samdb, gpo=gpo)[0] ds_sd_ndr = msg['ntSecurityDescriptor'][0] ds_sd = ndr_unpack(security.descriptor, ds_sd_ndr).as_sddl() # Create a file system security descriptor fs_sd = security.descriptor( dsacl2fsacl(ds_sd, self.samdb.get_domain_sid())) # Set ACL conn.set_acl(sharepath, fs_sd) self.samdb.transaction_commit() except Exception, e: self.samdb.transaction_cancel() raise RuntimeError("Error adding GPO to AD", e) self.outf.write("GPO '%s' created as %s\n" % (displayname, gpo)) class cmd_gpo(SuperCommand): """Group Policy Object (GPO) management"""
def _test_notify_privileged_path(self, monitor_path=None, rel_prefix=None): self.connect_unpriv() domain_sid = security.dom_sid() # we just use S-0-0 smb_helper = ntacls.SMBHelper(self.smb_conn, domain_sid) private_name = "private" private_rel = self.make_path(rel_prefix, private_name) private_path = self.make_path(test_dir, private_name) # create a private test directory self.smb_conn.mkdir(private_path) # Get the security descriptor and replace it # with a one that only grants access to SYSTEM and the # owner. private_path_sd_old = smb_helper.get_acl(private_path) private_path_sd_new = security.descriptor() private_path_sd_new.type = private_path_sd_old.type private_path_sd_new.revision = private_path_sd_old.revision private_path_sd_new = security.descriptor.from_sddl("G:BAD:(A;;0x%x;;;%s)(A;;0x%x;;;%s)" % ( security.SEC_RIGHTS_DIR_ALL, security.SID_NT_SYSTEM, security.SEC_RIGHTS_DIR_ALL, str(private_path_sd_old.owner_sid)), domain_sid) private_path_sd_new.type |= security.SEC_DESC_SELF_RELATIVE private_path_sd_new.type |= security.SEC_DESC_DACL_PROTECTED set_secinfo = security.SECINFO_GROUP | security.SECINFO_DACL | security.SECINFO_PROTECTED_DACL smb_helper.set_acl(private_path, private_path_sd_new, sinfo=set_secinfo) # setup notification request as priviliged user monitor_priv_fnum = self.smb_conn.create(Name=monitor_path, ShareAccess=1) notify_priv = self.smb_conn.notify(fnum=monitor_priv_fnum, buffer_size=0xffff, completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, recursive=True) # setup notification request as unpriviliged user monitor_unpriv_fnum = self.smb_conn_unpriv.create(Name=monitor_path, ShareAccess=1) notify_unpriv = self.smb_conn_unpriv.notify(fnum=monitor_unpriv_fnum, buffer_size=0xffff, completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, recursive=True) # make sure we didn't receive any changes yet. self.smb_conn.echo() changes = notify_priv.get_changes(wait=False) self.assertIsNone(changes) self.smb_conn_unpriv.echo() changes = notify_unpriv.get_changes(wait=False) self.assertIsNone(changes) # trigger notification in the private dir new_name = 'test-new' new_rel = self.make_path(private_rel, new_name) new_path = self.make_path(private_path, new_name) self.smb_conn.mkdir(new_path) # check that only the privileged user received the changes changes = notify_priv.get_changes(wait=True) self.assertIsNotNone(changes) self.assertEqual(changes[0]['name'], new_rel) self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_ADDED) self.assertEqual(len(changes), 1) notify_priv = self.smb_conn.notify(fnum=monitor_priv_fnum, buffer_size=0xffff, completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, recursive=True) # check that the unprivileged user does not receives the changes self.smb_conn_unpriv.echo() changes = notify_unpriv.get_changes(wait=False) self.assertIsNone(changes) # and there's no additional change for the privileged user self.smb_conn.echo() changes = notify_priv.get_changes(wait=False) self.assertIsNone(changes) # trigger notification in the private dir self.smb_conn.rmdir(new_path) # check that only the privileged user received the changes changes = notify_priv.get_changes(wait=True) self.assertIsNotNone(changes) self.assertEqual(changes[0]['name'], new_rel) self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_REMOVED) self.assertEqual(len(changes), 1) notify_priv = self.smb_conn.notify(fnum=monitor_priv_fnum, buffer_size=0xffff, completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, recursive=True) # check that the unprivileged user does not receives the changes self.smb_conn_unpriv.echo() changes = notify_unpriv.get_changes(wait=False) self.assertIsNone(changes) # and there's no additional change for the privileged user self.smb_conn.echo() changes = notify_priv.get_changes(wait=False) self.assertIsNone(changes) # trigger notification for both self.smb_conn.rmdir(private_path) # check that both get thte notification changes = notify_unpriv.get_changes(wait=True) self.assertIsNotNone(changes) self.assertEqual(changes[0]['name'], private_rel) self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_REMOVED) self.assertEqual(len(changes), 1) notify_unpriv = self.smb_conn_unpriv.notify(fnum=monitor_unpriv_fnum, buffer_size=0xffff, completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, recursive=True) changes = notify_priv.get_changes(wait=True) self.assertIsNotNone(changes) self.assertEqual(changes[0]['name'], private_rel) self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_REMOVED) self.assertEqual(len(changes), 1) notify_priv = self.smb_conn.notify(fnum=monitor_priv_fnum, buffer_size=0xffff, completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, recursive=True) # check that the unprivileged user does not receives the changes self.smb_conn_unpriv.echo() changes = notify_unpriv.get_changes(wait=False) self.assertIsNone(changes) # and there's no additional change for the privileged user self.smb_conn.echo() changes = notify_priv.get_changes(wait=False) self.assertIsNone(changes) # closing the handle on will trigger a NOTIFY_CLEANUP self.smb_conn_unpriv.close(monitor_unpriv_fnum) try: changes = notify_unpriv.get_changes(wait=True) self.fail() except samba.NTSTATUSError as err: self.assertEqual(err.args[0], NT_STATUS_NOTIFY_CLEANUP) # there's no additional change for the privileged user self.smb_conn.echo() changes = notify_priv.get_changes(wait=False) self.assertIsNone(changes) # closing the handle on will trigger a NOTIFY_CLEANUP self.smb_conn.close(monitor_priv_fnum) try: changes = notify_priv.get_changes(wait=True) self.fail() except samba.NTSTATUSError as err: self.assertEqual(err.args[0], NT_STATUS_NOTIFY_CLEANUP)