def test_admin_permissions(self): system = win32security.ConvertStringSidToSid(SYSTEM_SID) admins = win32security.ConvertStringSidToSid(ADMINS_SID) filesystem.chmod(self.probe_path, 0o400) dacl = _get_security_dacl(self.probe_path).GetSecurityDescriptorDacl() system_aces = [dacl.GetAce(index) for index in range(0, dacl.GetAceCount()) if dacl.GetAce(index)[2] == system] admin_aces = [dacl.GetAce(index) for index in range(0, dacl.GetAceCount()) if dacl.GetAce(index)[2] == admins] self.assertEqual(len(system_aces), 1) self.assertEqual(len(admin_aces), 1) self.assertEqual(system_aces[0][1], ntsecuritycon.FILE_ALL_ACCESS) self.assertEqual(admin_aces[0][1], ntsecuritycon.FILE_ALL_ACCESS)
def test_get_name_capability_sid(self): """ Test get_name with a compatibility SID. Should return `None` as we want to ignore these SIDs """ cap_sid = "S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681" sid_obj = win32security.ConvertStringSidToSid(cap_sid) self.assertIsNone(win_dacl.get_name(sid_obj))
def test_get_name_error(): """ Test get_name with an un mapped SID, should throw a CommandExecutionError """ test_sid = "S-1-2-3-4" sid_obj = win32security.ConvertStringSidToSid(test_sid) with pytest.raises(salt.exceptions.CommandExecutionError) as exc: salt.utils.win_dacl.get_name(sid_obj) assert "No mapping between account names" in exc.value.message
def getUsersGroup(): try: sid = win32security.ConvertStringSidToSid("S-1-5-32-555") name, _, _ = win32security.LookupAccountSid(None, sid) except Exception, err: Logger.error( "TS::getUsersGroup unable to found remote users group replacing by default name" ) return "Remote Desktop Users"
def str_sid2username(str_sid): ''' Convert a string sid to a string account name ''' '''sid = win32security.LookupAccountName(None, "Sekoia")[0] print sid''' try: sid = win32security.ConvertStringSidToSid(str_sid) return sid2username(sid) except: return ""
def _generate_dacl(user_sid: Any, mode: int, mask: Optional[int] = None) -> Any: if mask: mode = mode & (0o777 - mask) analysis = _analyze_mode(mode) # Get standard accounts from "well-known" sid # See the list here: # https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems system = win32security.ConvertStringSidToSid('S-1-5-18') admins = win32security.ConvertStringSidToSid('S-1-5-32-544') everyone = win32security.ConvertStringSidToSid('S-1-1-0') # New dacl, without inherited permissions dacl = win32security.ACL() # If user is already system or admins, any ACE defined here would be superseded by # the full control ACE that will be added after. if user_sid not in [system, admins]: # Handle user rights user_flags = _generate_windows_flags(analysis['user']) if user_flags: dacl.AddAccessAllowedAce(win32security.ACL_REVISION, user_flags, user_sid) # Handle everybody rights everybody_flags = _generate_windows_flags(analysis['all']) if everybody_flags: dacl.AddAccessAllowedAce(win32security.ACL_REVISION, everybody_flags, everyone) # Handle administrator rights full_permissions = _generate_windows_flags({ 'read': True, 'write': True, 'execute': True }) dacl.AddAccessAllowedAce(win32security.ACL_REVISION, full_permissions, system) dacl.AddAccessAllowedAce(win32security.ACL_REVISION, full_permissions, admins) return dacl
def test_check_owner_windows(self): self.assertTrue(filesystem.check_owner(self.probe_path)) system = win32security.ConvertStringSidToSid(SYSTEM_SID) security = win32security.SECURITY_ATTRIBUTES().SECURITY_DESCRIPTOR security.SetSecurityDescriptorOwner(system, False) with mock.patch('win32security.GetFileSecurity') as mock_get: mock_get.return_value = security self.assertFalse(filesystem.check_owner(self.probe_path))
def test_mkdir_correct_permissions(self): path = os.path.join(self.tempdir, 'dir') filesystem.mkdir(path, 0o700) everybody = win32security.ConvertStringSidToSid(EVERYBODY_SID) dacl = _get_security_dacl(path).GetSecurityDescriptorDacl() self.assertFalse([dacl.GetAce(index) for index in range(0, dacl.GetAceCount()) if dacl.GetAce(index)[2] == everybody])
def set_path_permissions(path): ''' Gives Administrators, System, and Owner full control over the specified directory Args: path (str): The path to the file or directory Returns: bool: True if successful, Otherwise CommandExecutionError ''' # TODO: Need to make this more generic, maybe a win_dacl utility admins = win32security.ConvertStringSidToSid('S-1-5-32-544') user = win32security.ConvertStringSidToSid('S-1-5-32-545') system = win32security.ConvertStringSidToSid('S-1-5-18') owner = win32security.ConvertStringSidToSid('S-1-3-4') dacl = win32security.ACL() revision = win32security.ACL_REVISION_DS inheritance = win32security.CONTAINER_INHERIT_ACE |\ win32security.OBJECT_INHERIT_ACE full_access = ntsecuritycon.GENERIC_ALL user_access = ntsecuritycon.GENERIC_READ | \ ntsecuritycon.GENERIC_EXECUTE dacl.AddAccessAllowedAceEx(revision, inheritance, full_access, admins) dacl.AddAccessAllowedAceEx(revision, inheritance, full_access, system) dacl.AddAccessAllowedAceEx(revision, inheritance, full_access, owner) if 'pki' not in path: dacl.AddAccessAllowedAceEx(revision, inheritance, user_access, user) try: win32security.SetNamedSecurityInfo( path, win32security.SE_FILE_OBJECT, win32security.DACL_SECURITY_INFORMATION | win32security.PROTECTED_DACL_SECURITY_INFORMATION, None, None, dacl, None) except pywintypes.error as exc: raise CommandExecutionError('Failed to set permissions: {0}'.format( exc[2])) return True
def test_new_file_correct_permissions(self): path = os.path.join(self.tempdir, 'file') desc = filesystem.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR, 0o700) os.close(desc) dacl = _get_security_dacl(path).GetSecurityDescriptorDacl() everybody = win32security.ConvertStringSidToSid(EVERYBODY_SID) self.assertFalse([dacl.GetAce(index) for index in range(0, dacl.GetAceCount()) if dacl.GetAce(index)[2] == everybody])
def test_get_name_error(self): """ Test get_name with an un mapped SID, should throw a CommandExecutionError """ test_sid = "S-1-2-3-4" sid_obj = win32security.ConvertStringSidToSid(test_sid) with TstSuiteLoggingHandler() as handler: self.assertRaises(CommandExecutionError, win_dacl.get_name, sid_obj) expected_message = 'ERROR:Error resolving "PySID:S-1-2-3-4"' self.assertIn(expected_message, handler.messages[0])
def grantEveryoneFilePermission(self, dirpath, filepath=""): """Grant full control to the group I{Everyone} in this folder and all sub-folders This function grants full control to the Windows group I{Everyone} for files in the C{dirpath} its sub-folders. If a C{filepath} is specified, only the permissions of a specific file are updated. @type dirpath: string @param dirpath: The full path of the folder for which these permissions are set @type filepath: string @param filepath: The full path to a specific file. """ # Execute command only on NTFS filesystem. Otherwise pass silently fullpath = os.path.abspath(dirpath) driveLetter = os.path.splitdrive(fullpath)[0] if not self.isNTFSVolume(driveLetter): pylabs.q.logger.log( "Skipped file permissions update - filesystem for [%s] is not NTFS" % dirpath, 6) return def _grantFile(fileName, securityDescriptor): '''Set security on a file''' pylabs.q.logger.log( "granting all access to everyone on %s" % fileName, 6) win32security.SetFileSecurity( fileName, win32security.DACL_SECURITY_INFORMATION, securityDescriptor) def _grantDir(dirpath, securityDescriptor): '''Set security on a folder''' for dir in pylabs.q.system.fs.listDirsInDir(dirpath): _grantDir(dir, securityDescriptor) for file in pylabs.q.system.fs.listFilesInDir(dirpath): _grantFile(file, securityDescriptor) win32security.SetFileSecurity( dirpath, win32security.DACL_SECURITY_INFORMATION, securityDescriptor) # create the security descriptor sd = win32security.SECURITY_DESCRIPTOR() # fill it: everyone = win32security.ConvertStringSidToSid('S-1-1-0') acl = win32security.ACL(128) acl.AddAccessAllowedAce(win32file.FILE_ALL_ACCESS, everyone) sd.SetSecurityDescriptorDacl(1, acl, 0) if filepath == "": # it's a dir _grantDir(dirpath, sd) else: _grantFile(os.path.join(dirpath, filepath), sd)
def test_read_unknown_sid(self): """ eventlogging - Read event with unknown SID """ # Fake SID that was made up. accountIdentifier = "S-1-5-21-3623811015-3361044348-30300820-1013" sid = win32security.ConvertStringSidToSid(accountIdentifier) msg = "Unknown SID " + accountIdentifier self.write_event_log(msg, sid=sid) evts = self.read_events() self.assertTrue(len(evts), 1) self.assert_common_fields(evts[0], msg=msg, sid=accountIdentifier)
def set_file_readable(filename): import win32api import win32security import ntsecuritycon as con users = win32security.ConvertStringSidToSid("S-1-5-32-545") admins = win32security.ConvertStringSidToSid("S-1-5-32-544") user, domain, type = win32security.LookupAccountName( "", win32api.GetUserName()) sd = win32security.GetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION) dacl = win32security.ACL() dacl.AddAccessAllowedAce(win32security.ACL_REVISION, con.FILE_ALL_ACCESS, users) dacl.AddAccessAllowedAce(win32security.ACL_REVISION, con.FILE_ALL_ACCESS, user) dacl.AddAccessAllowedAce(win32security.ACL_REVISION, con.FILE_ALL_ACCESS, admins) sd.SetSecurityDescriptorDacl(1, dacl, 0) win32security.SetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION, sd)
def test_world_permission(self): everybody = win32security.ConvertStringSidToSid(EVERYBODY_SID) filesystem.chmod(self.probe_path, 0o700) dacl = _get_security_dacl(self.probe_path).GetSecurityDescriptorDacl() self.assertFalse([dacl.GetAce(index) for index in range(0, dacl.GetAceCount()) if dacl.GetAce(index)[2] == everybody]) filesystem.chmod(self.probe_path, 0o704) dacl = _get_security_dacl(self.probe_path).GetSecurityDescriptorDacl() self.assertTrue([dacl.GetAce(index) for index in range(0, dacl.GetAceCount()) if dacl.GetAce(index)[2] == everybody])
def test_copy_ownership_and_apply_mode_windows(self): system = win32security.ConvertStringSidToSid(SYSTEM_SID) security = win32security.SECURITY_ATTRIBUTES().SECURITY_DESCRIPTOR security.SetSecurityDescriptorOwner(system, False) with mock.patch('win32security.GetFileSecurity') as mock_get: with mock.patch('win32security.SetFileSecurity') as mock_set: mock_get.return_value = security filesystem.copy_ownership_and_apply_mode( 'dummy', self.probe_path, 0o700, copy_user=True, copy_group=False) self.assertEqual(mock_set.call_count, 2) first_call = mock_set.call_args_list[0] security = first_call[0][2] self.assertEqual(system, security.GetSecurityDescriptorOwner()) second_call = mock_set.call_args_list[1] security = second_call[0][2] dacl = security.GetSecurityDescriptorDacl() everybody = win32security.ConvertStringSidToSid(EVERYBODY_SID) self.assertTrue(dacl.GetAceCount()) self.assertFalse([dacl.GetAce(index) for index in range(0, dacl.GetAceCount()) if dacl.GetAce(index)[2] == everybody])
def _test_flag(self, everyone_mode, windows_flag): # Note that flag is tested against `everyone`, not `user`, because practically these unit # tests are executed with admin privilege, so current user is effectively the admins group, # and so will always have all rights. filesystem.chmod(self.probe_path, 0o700 | everyone_mode) dacl = _get_security_dacl(self.probe_path).GetSecurityDescriptorDacl() everybody = win32security.ConvertStringSidToSid(EVERYBODY_SID) acls_everybody = [dacl.GetAce(index) for index in range(0, dacl.GetAceCount()) if dacl.GetAce(index)[2] == everybody] self.assertEqual(len(acls_everybody), 1) acls_everybody = acls_everybody[0] self.assertEqual(acls_everybody[1], windows_flag)
def get_name_from_sid(sid): ''' This gets the name from the specified SID. Opposite of get_sid_from_name Args: sid (str): The SID for which to find the name Returns: str: The name that corresponds to the passed SID ''' try: sid_obj = win32security.ConvertStringSidToSid(sid) name = win32security.LookupAccountSid(None, sid_obj)[0] except pywintypes.error as exc: raise CommandExecutionError('User {0} found: {1}'.format(sid, exc[2])) return name
def assert_world_no_permissions(file: str) -> None: """ Assert that the given file is not world-readable. :param str file: path of the file to check """ if POSIX_MODE: mode_file_all = os.stat(file).st_mode & 0o007 assert mode_file_all == 0 else: security = win32security.GetFileSecurity(file, win32security.DACL_SECURITY_INFORMATION) dacl = security.GetSecurityDescriptorDacl() mode = dacl.GetEffectiveRightsFromAcl({ 'TrusteeForm': win32security.TRUSTEE_IS_SID, 'TrusteeType': win32security.TRUSTEE_IS_USER, 'Identifier': win32security.ConvertStringSidToSid(EVERYBODY_SID), }) assert not mode
def has_world_permissions(path): # type: (str) -> bool """ Check if everybody/world has any right (read/write/execute) on a file given its path :param str path: path to test :return: True if everybody/world has any right to the file :rtype: bool """ if POSIX_MODE: return bool(stat.S_IMODE(os.stat(path).st_mode) & stat.S_IRWXO) security = win32security.GetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION) dacl = security.GetSecurityDescriptorDacl() return bool(dacl.GetEffectiveRightsFromAcl({ 'TrusteeForm': win32security.TRUSTEE_IS_SID, 'TrusteeType': win32security.TRUSTEE_IS_USER, 'Identifier': win32security.ConvertStringSidToSid('S-1-1-0'), }))
def test_renew_files_propagate_permissions( context: IntegrationTestsContext) -> None: """Test proper certificate renewal with custom permissions propagated on private key.""" certname = context.get_domain('renew') context.certbot(['-d', certname]) assert_cert_count_for_lineage(context.config_dir, certname, 1) privkey1 = join(context.config_dir, 'archive', certname, 'privkey1.pem') privkey2 = join(context.config_dir, 'archive', certname, 'privkey2.pem') if os.name != 'nt': os.chmod(privkey1, 0o444) else: import win32security # pylint: disable=import-error import ntsecuritycon # pylint: disable=import-error # Get the current DACL of the private key security = win32security.GetFileSecurity( privkey1, win32security.DACL_SECURITY_INFORMATION) dacl = security.GetSecurityDescriptorDacl() # Create a read permission for Everybody group everybody = win32security.ConvertStringSidToSid(EVERYBODY_SID) dacl.AddAccessAllowedAce(win32security.ACL_REVISION, ntsecuritycon.FILE_GENERIC_READ, everybody) # Apply the updated DACL to the private key security.SetSecurityDescriptorDacl(1, dacl, 0) win32security.SetFileSecurity(privkey1, win32security.DACL_SECURITY_INFORMATION, security) context.certbot(['renew']) assert_cert_count_for_lineage(context.config_dir, certname, 2) if os.name != 'nt': # On Linux, read world permissions + all group permissions # will be copied from the previous private key assert_world_read_permissions(privkey2) assert_equals_world_read_permissions(privkey1, privkey2) assert_equals_group_permissions(privkey1, privkey2) else: # On Windows, world will never have any permissions, and # group permission is irrelevant for this platform assert_world_no_permissions(privkey2)
def rchown(path, user): sid = None admins = None try: sid, d, type = win32security.LookupAccountName("", user) admins = win32security.ConvertStringSidToSid("S-1-5-32-544") except: return False dacl = win32security.ACL() dacl.AddAccessAllowedAce(win32security.ACL_REVISION, ntsecuritycon.FILE_ALL_ACCESS, sid) dacl.AddAccessAllowedAce(win32security.ACL_REVISION, ntsecuritycon.FILE_ALL_ACCESS, admins) try: System._rchown(path, sid, dacl) except Exception, e: return False
def assert_world_read_permissions(file): """ Assert that the given file is world-readable, but not world-writable or world-executable. :param file: path of the file to check """ if POSIX_MODE: mode_file_all = os.stat(file).st_mode & 0o007 assert mode_file_all == 4 else: security = win32security.GetFileSecurity(file, win32security.DACL_SECURITY_INFORMATION) dacl = security.GetSecurityDescriptorDacl() mode = dacl.GetEffectiveRightsFromAcl({ 'TrusteeForm': win32security.TRUSTEE_IS_SID, 'TrusteeType': win32security.TRUSTEE_IS_USER, 'Identifier': win32security.ConvertStringSidToSid(EVERYBODY_SID), }) assert not mode & ntsecuritycon.FILE_GENERIC_WRITE assert not mode & ntsecuritycon.FILE_GENERIC_EXECUTE assert mode & ntsecuritycon.FILE_GENERIC_READ == ntsecuritycon.FILE_GENERIC_READ
def assert_equals_world_read_permissions(file1: str, file2: str) -> None: """ Assert that two files have the same read permissions for everyone. :param str file1: first file path to compare :param str file2: second file path to compare """ if POSIX_MODE: mode_file1 = os.stat(file1).st_mode & 0o004 mode_file2 = os.stat(file2).st_mode & 0o004 else: everybody = win32security.ConvertStringSidToSid(EVERYBODY_SID) security1 = win32security.GetFileSecurity( file1, win32security.DACL_SECURITY_INFORMATION) dacl1 = security1.GetSecurityDescriptorDacl() mode_file1 = dacl1.GetEffectiveRightsFromAcl({ 'TrusteeForm': win32security.TRUSTEE_IS_SID, 'TrusteeType': win32security.TRUSTEE_IS_USER, 'Identifier': everybody, }) mode_file1 = mode_file1 & ntsecuritycon.FILE_GENERIC_READ security2 = win32security.GetFileSecurity( file2, win32security.DACL_SECURITY_INFORMATION) dacl2 = security2.GetSecurityDescriptorDacl() mode_file2 = dacl2.GetEffectiveRightsFromAcl({ 'TrusteeForm': win32security.TRUSTEE_IS_SID, 'TrusteeType': win32security.TRUSTEE_IS_USER, 'Identifier': everybody, }) mode_file2 = mode_file2 & ntsecuritycon.FILE_GENERIC_READ assert mode_file1 == mode_file2
def get_sid(principal): ''' Converts a username to a sid, or verifies a sid. Required for working with the DACL. Args: principal(str): The principal to lookup the sid. Can be a sid or a username. Returns: PySID Object: A sid Usage: .. code-block:: python # Get a user's sid salt.utils.win_dacl.get_sid('jsnuffy') # Verify that the sid is valid salt.utils.win_dacl.get_sid('S-1-5-32-544') ''' # Test if the user passed a sid or a name try: sid = salt.utils.win_functions.get_sid_from_name(principal) except CommandExecutionError: sid = principal # Test if the SID is valid try: sid = win32security.ConvertStringSidToSid(sid) except pywintypes.error: raise CommandExecutionError( 'Invalid user/group or sid: {0}'.format(principal)) except TypeError: raise CommandExecutionError return sid
def read_unknown_sid(self, api): # Fake SID that was made up. accountIdentifier = "S-1-5-21-3623811015-3361044348-30300820-1013" sid = win32security.ConvertStringSidToSid(accountIdentifier) msg = "Unknown SID of " + accountIdentifier eventID = 40 self.write_event_log(msg, eventID, sid) # Run Winlogbeat self.render_config_template(event_logs=[{ "name": self.providerName, "api": api }]) proc = self.start_beat() self.wait_until(lambda: self.output_has(1)) proc.kill() # Verify output events = self.read_output() assert len(events) == 1 evt = events[0] assert evt["type"] == api assert evt["event_id"] == eventID assert evt["level"] == "Information" assert evt["log_name"] == self.providerName assert evt["source_name"] == self.applicationName assert evt["computer_name"].lower() == win32api.GetComputerName( ).lower() assert evt["user.identifier"] == accountIdentifier assert "user.name" not in evt assert "user.type" not in evt assert "user.domain" not in evt assert evt["message"] == msg exit_code = proc.wait() assert exit_code == 0 return evt
def set_path_owner(path): ''' Sets the owner of a file or directory to be Administrator Args: path (str): The path to the file or directory Returns: bool: True if successful, Otherwise CommandExecutionError ''' # Must use the SID here to be locale agnostic admins = win32security.ConvertStringSidToSid('S-1-5-32-544') try: win32security.SetNamedSecurityInfo( path, win32security.SE_FILE_OBJECT, win32security.OWNER_SECURITY_INFORMATION | win32security.PROTECTED_DACL_SECURITY_INFORMATION, admins, None, None, None) except pywintypes.error as exc: raise CommandExecutionError('Failed to set owner: {0}'.format(exc[2])) return True
def get_name(principal): ''' Gets the name from the specified principal. Args: principal (str): Find the Normalized name based on this. Can be a PySID object, a SID string, or a user name in any capitalization. Returns: str: The name that corresponds to the passed principal Usage: .. code-block:: python salt.utils.win_dacl.get_name('S-1-5-32-544') salt.utils.win_dacl.get_name('adminisTrators') ''' # Assume PySID object sid_obj = principal # Name try: sid_obj = win32security.LookupAccountName(None, principal)[0] except (pywintypes.error, TypeError): pass # String SID try: sid_obj = win32security.ConvertStringSidToSid(principal) except (pywintypes.error, TypeError): pass try: return win32security.LookupAccountSid(None, sid_obj)[0] except TypeError: raise CommandExecutionError( 'Could not find User for {0}'.format(principal))
def read_unknown_sid(self, api): # Fake SID that was made up. accountIdentifier = "S-1-5-21-3623811015-3361044348-30300820-1013" sid = win32security.ConvertStringSidToSid(accountIdentifier) msg = "Unknown SID of " + accountIdentifier self.write_event_log(msg, sid=sid) # Run Winlogbeat self.render_config_template(event_logs=[{ "name": self.providerName, "api": api }]) proc = self.start_beat() self.wait_until(lambda: self.output_has(1)) proc.check_kill_and_wait() # Verify output events = self.read_output() assert len(events) == 1 evt = events[0] self.assert_common_fields(evt, api, msg, sid=accountIdentifier) return evt
def get_name(sid): ''' Gets the name from the specified SID. Opposite of get_sid Args: sid (str): The SID for which to find the name Returns: str: The name that corresponds to the passed SID Usage: .. code-block:: python salt.utils.win_dacl.get_name('S-1-5-32-544') ''' try: sid_obj = win32security.ConvertStringSidToSid(sid) name = win32security.LookupAccountSid(None, sid_obj)[0] except pywintypes.error as exc: raise CommandExecutionError('User {0} found: {1}'.format(sid, exc[2])) return name