Source code for plaso.preprocessors.macos

# -*- coding: utf-8 -*-
"""This file contains preprocessors for MacOS."""

from __future__ import unicode_literals

import abc

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, knowledge_base, file_object): """Parses file content (data) for a preprocessing attribute. Args: knowledge_base (KnowledgeBase): to fill with preprocessing information. 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 IOError as exception: raise errors.PreProcessFail( 'Unable to read: {0:s} with error: {1!s}'.format( self.ARTIFACT_DEFINITION_NAME, exception)) if not plist_file.root_key: raise errors.PreProcessFail(( 'Unable to read: {0:s} with error: missing root key').format( self.ARTIFACT_DEFINITION_NAME)) matches = [] self._FindKeys(plist_file.root_key, self._PLIST_KEYS, matches) if not matches: raise errors.PreProcessFail( 'Unable to read: {0:s} with error: no such keys: {1:s}.'.format( self.ARTIFACT_DEFINITION_NAME, ', '.join(self._PLIST_KEYS))) name = None value = None for name, value in matches: if value: break if value is None: raise errors.PreProcessFail(( 'Unable to read: {0:s} with error: no values found for keys: ' '{1:s}.').format( self.ARTIFACT_DEFINITION_NAME, ', '.join(self._PLIST_KEYS))) self._ParsePlistKeyValue(knowledge_base, name, value) @abc.abstractmethod def _ParsePlistKeyValue(self, knowledge_base, name, value): """Parses a plist key value. Args: knowledge_base (KnowledgeBase): to fill with preprocessing information. 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, knowledge_base, name, value): """Parses a plist key value. Args: knowledge_base (KnowledgeBase): to fill with preprocessing information. name (str): name of the plist key. value (str): value of the plist key. """ if not knowledge_base.GetHostname(): if name in self._PLIST_KEYS: hostname_artifact = artifacts.HostnameArtifact(name=value) knowledge_base.SetHostname(hostname_artifact)
[docs]class MacOSKeyboardLayoutPlugin(PlistFileArtifactPreprocessorPlugin): """MacOS keyboard layout plugin.""" ARTIFACT_DEFINITION_NAME = 'MacOSKeyboardLayoutPlistFile' _PLIST_KEYS = ['AppleCurrentKeyboardLayoutInputSourceID'] def _ParsePlistKeyValue(self, knowledge_base, name, value): """Parses a plist key value. Args: knowledge_base (KnowledgeBase): to fill with preprocessing information. name (str): name of the plist key. value (str): value of the plist key. """ if not knowledge_base.GetValue('keyboard_layout'): if name in self._PLIST_KEYS: if isinstance(value, (list, tuple)): value = value[0] _, _, keyboard_layout = value.rpartition('.') knowledge_base.SetValue('keyboard_layout', keyboard_layout)
[docs]class MacOSSystemVersionPlugin(PlistFileArtifactPreprocessorPlugin): """MacOS system version information plugin.""" ARTIFACT_DEFINITION_NAME = 'MacOSSystemVersionPlistFile' _PLIST_KEYS = ['ProductUserVisibleVersion'] def _ParsePlistKeyValue(self, knowledge_base, name, value): """Parses a plist key value. Args: knowledge_base (KnowledgeBase): to fill with preprocessing information. name (str): name of the plist key. value (str): value of the plist key. """ if not knowledge_base.GetValue('operating_system_version'): if name in self._PLIST_KEYS: knowledge_base.SetValue('operating_system_version', value)
[docs]class MacOSTimeZonePlugin(interface.FileEntryArtifactPreprocessorPlugin): """MacOS time zone plugin.""" ARTIFACT_DEFINITION_NAME = 'MacOSLocalTime' def _ParseFileEntry(self, knowledge_base, file_entry): """Parses artifact file system data for a preprocessing attribute. Args: knowledge_base (KnowledgeBase): to fill with preprocessing information. 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( 'Unable to read: {0:s} with error: not a symbolic link'.format( self.ARTIFACT_DEFINITION_NAME)) _, _, time_zone = file_entry.link.partition('zoneinfo/') # TODO: check if time zone is set in knowledge base. if time_zone: try: knowledge_base.SetTimeZone(time_zone) except ValueError: # TODO: add and store preprocessing errors. pass
[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, None) if value is not None: match[key] = value return match def _GetPlistRootKey(self, file_entry): """Retrieves the root key of a plist file. Args: file_entry (dfvfs.FileEntry): file entry of the plist. Returns: dict[str, object]: plist root key. Raises: errors.PreProcessFail: if the preprocessing fails. """ file_object = file_entry.GetFileObject() try: plist_file = plist.PlistFile() plist_file.Read(file_object) except IOError as exception: location = getattr(file_entry.path_spec, 'location', '') raise errors.PreProcessFail( 'Unable to read plist file: {0:s} with error: {1!s}'.format( location, exception)) finally: file_object.close() return plist_file.root_key def _ParseFileEntry(self, knowledge_base, file_entry): """Parses artifact file system data for a preprocessing attribute. Args: knowledge_base (KnowledgeBase): to fill with preprocessing information. file_entry (dfvfs.FileEntry): file entry that contains the artifact value data. Raises: errors.PreProcessFail: if the preprocessing fails. """ root_key = self._GetPlistRootKey(file_entry) if not root_key: location = getattr(file_entry.path_spec, 'location', '') raise errors.PreProcessFail(( 'Unable to read: {0:s} plist: {1:s} with error: missing root ' 'key.').format(self.ARTIFACT_DEFINITION_NAME, location)) try: match = self._GetTopLevelKeys(root_key, self._KEYS) except KeyError as exception: location = getattr(file_entry.path_spec, 'location', '') raise errors.PreProcessFail( 'Unable to read: {0:s} plist: {1:s} with error: {2!s}'.format( self.ARTIFACT_DEFINITION_NAME, location, exception)) name = match.get('name', [None])[0] uid = match.get('uid', [None])[0] if not name or not uid: # TODO: add and store preprocessing errors. 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: knowledge_base.AddUserAccount(user_account) except KeyError: # TODO: add and store preprocessing errors. pass
manager.PreprocessPluginsManager.RegisterPlugins([ MacOSHostnamePlugin, MacOSKeyboardLayoutPlugin, MacOSSystemVersionPlugin, MacOSTimeZonePlugin, MacOSUserAccountsPlugin])