Source code for plaso.preprocessors.macos

"""MacOS preprocessor plugins."""

import abc
import plistlib

from plaso.containers import artifacts
from plaso.lib import errors
from plaso.lib import plist
from plaso.preprocessors import interface
from plaso.preprocessors import manager


[docs] class PlistFileArtifactPreprocessorPlugin( interface.FileArtifactPreprocessorPlugin): """Plist file artifact preprocessor plugin interface. Retrieves values from a plist file artifact using names of keys defined in _PLIST_KEYS. """ # The key that's value should be returned back. It is an ordered list # of preference. If the first value is found it will be returned and no # others will be searched. _PLIST_KEYS = [''] def _FindKeys(self, key, names, matches): """Searches the plist key hierarchy for keys with matching names. If a match is found a tuple of the key name and value is added to the matches list. Args: key (dict[str, object]): plist key. names (list[str]): names of the keys to match. matches (list[str]): keys with matching names. """ for name, subkey in key.items(): if name in names: matches.append((name, subkey)) if isinstance(subkey, dict): self._FindKeys(subkey, names, matches) def _ParseFileData(self, mediator, file_object): """Parses file content (data) for a preprocessing attribute. Args: mediator (PreprocessMediator): mediates interactions between preprocess plugins and other components, such as storage. file_object (dfvfs.FileIO): file-like object that contains the artifact value data. Raises: errors.PreProcessFail: if the preprocessing fails. """ plist_file = plist.PlistFile() try: plist_file.Read(file_object) except OSError as exception: raise errors.PreProcessFail(( f'Unable to read: {self.ARTIFACT_DEFINITION_NAME:s} with error: ' f'{exception!s}')) if not plist_file.root_key: raise errors.PreProcessFail(( f'Unable to read: {self.ARTIFACT_DEFINITION_NAME:s} with error: ' f'missing root key')) matches = [] self._FindKeys(plist_file.root_key, self._PLIST_KEYS, matches) if not matches: plist_keys_string = ', '.join(self._PLIST_KEYS) raise errors.PreProcessFail(( f'Unable to read: {self.ARTIFACT_DEFINITION_NAME:s} with error: no ' f'such keys: {plist_keys_string:s}.')) name = None value = None for name, value in matches: if value: break if value is None: plist_keys_string = ', '.join(self._PLIST_KEYS) raise errors.PreProcessFail(( f'Unable to read: {self.ARTIFACT_DEFINITION_NAME:s} with error: no ' f'values found for keys: {plist_keys_string:s}.')) self._ParsePlistKeyValue(mediator, name, value) @abc.abstractmethod def _ParsePlistKeyValue(self, mediator, name, value): """Parses a plist key value. Args: mediator (PreprocessMediator): mediates interactions between preprocess plugins and other components, such as storage. name (str): name of the plist key. value (str): value of the plist key. """
[docs] class MacOSHostnamePlugin(PlistFileArtifactPreprocessorPlugin): """MacOS hostname plugin.""" ARTIFACT_DEFINITION_NAME = 'MacOSSystemConfigurationPreferencesPlistFile' _PLIST_KEYS = ['ComputerName', 'LocalHostName'] def _ParsePlistKeyValue(self, mediator, name, value): """Parses a plist key value. Args: mediator (PreprocessMediator): mediates interactions between preprocess plugins and other components, such as storage. name (str): name of the plist key. value (str): value of the plist key. """ if name in self._PLIST_KEYS: hostname_artifact = artifacts.HostnameArtifact(name=value) mediator.AddHostname(hostname_artifact)
[docs] class MacOSKeyboardLayoutPlugin(PlistFileArtifactPreprocessorPlugin): """MacOS keyboard layout plugin.""" ARTIFACT_DEFINITION_NAME = 'MacOSKeyboardLayoutPlistFile' _PLIST_KEYS = ['AppleCurrentKeyboardLayoutInputSourceID'] def _ParsePlistKeyValue(self, mediator, name, value): """Parses a plist key value. Args: mediator (PreprocessMediator): mediates interactions between preprocess plugins and other components, such as storage. name (str): name of the plist key. value (str): value of the plist key. """ if name in self._PLIST_KEYS: if isinstance(value, (list, tuple)): value = value[0] _, _, keyboard_layout = value.rpartition('.') mediator.SetValue('keyboard_layout', keyboard_layout)
[docs] class MacOSSystemVersionPlugin(PlistFileArtifactPreprocessorPlugin): """MacOS system version information plugin.""" ARTIFACT_DEFINITION_NAME = 'MacOSSystemVersionPlistFile' _PLIST_KEYS = ['ProductUserVisibleVersion'] def _ParsePlistKeyValue(self, mediator, name, value): """Parses a plist key value. Args: mediator (PreprocessMediator): mediates interactions between preprocess plugins and other components, such as storage. name (str): name of the plist key. value (str): value of the plist key. """ if name in self._PLIST_KEYS: mediator.SetValue('operating_system_version', value)
[docs] class MacOSTimeZonePlugin(interface.FileEntryArtifactPreprocessorPlugin): """MacOS time zone plugin.""" ARTIFACT_DEFINITION_NAME = 'MacOSLocalTime' def _ParseFileEntry(self, mediator, file_entry): """Parses artifact file system data for a preprocessing attribute. Args: mediator (PreprocessMediator): mediates interactions between preprocess plugins and other components, such as storage. file_entry (dfvfs.FileEntry): file entry that contains the artifact value data. Raises: errors.PreProcessFail: if the preprocessing fails. """ if not file_entry or not file_entry.link: raise errors.PreProcessFail(( f'Unable to read: {self.ARTIFACT_DEFINITION_NAME:s} with error: not ' f'a symbolic link')) _, _, time_zone = file_entry.link.partition('zoneinfo/') if time_zone: try: mediator.SetTimeZone(time_zone) except ValueError: mediator.ProducePreprocessingWarning( self.ARTIFACT_DEFINITION_NAME, 'Unable to set time zone in knowledge base.')
[docs] class MacOSUserAccountsPlugin(interface.FileEntryArtifactPreprocessorPlugin): """MacOS user accounts plugin.""" ARTIFACT_DEFINITION_NAME = 'MacOSUserPasswordHashesPlistFiles' _KEYS = frozenset(['gid', 'home', 'name', 'realname', 'shell', 'uid']) def _GetTopLevelKeys(self, top_level, keys): """Retrieves top-level plist keys. Args: top_level (plistlib._InternalDict): top level plist object. keys (set[str]): names of the top-level keys that should be retrieved. Returns: dict[str, str]: values of the requested keys or an empty dictionary if no corresponding top-level keys were found. """ match = {} for key in set(keys): value = top_level.get(key) if value is not None: match[key] = value return match def _ParseFileEntry(self, mediator, file_entry): """Parses artifact file system data for a preprocessing attribute. Args: mediator (PreprocessMediator): mediates interactions between preprocess plugins and other components, such as storage. file_entry (dfvfs.FileEntry): file entry that contains the artifact value data. Raises: errors.PreProcessFail: if the preprocessing fails. """ file_object = file_entry.GetFileObject() try: plist_file = plist.PlistFile() plist_file.Read(file_object) match = self._GetTopLevelKeys(plist_file.root_key, self._KEYS) except (OSError, plistlib.InvalidFileException) as exception: mediator.ProducePreprocessingWarning( self.ARTIFACT_DEFINITION_NAME, f'Unable to read plist with error: {exception!s}') return name = match.get('name', [None])[0] uid = match.get('uid', [None])[0] if not name or not uid: mediator.ProducePreprocessingWarning( self.ARTIFACT_DEFINITION_NAME, 'Missing name or user identifier') return user_account = artifacts.UserAccountArtifact( identifier=uid, username=name) user_account.group_identifier = match.get('gid', [None])[0] user_account.full_name = match.get('realname', [None])[0] user_account.shell = match.get('shell', [None])[0] user_account.user_directory = match.get('home', [None])[0] try: mediator.AddUserAccount(user_account) except KeyError: mediator.ProducePreprocessingWarning( self.ARTIFACT_DEFINITION_NAME, f'Unable to add user account: {name:s} to knowledge base.')
manager.PreprocessPluginsManager.RegisterPlugins([ MacOSHostnamePlugin, MacOSKeyboardLayoutPlugin, MacOSSystemVersionPlugin, MacOSTimeZonePlugin, MacOSUserAccountsPlugin])