def testAPFSUnlockVolumeOnAPFS(self): """Tests the APFSUnlockVolume function on an APFS image.""" resolver_context = context.Context() test_path = self._GetTestFilePath(['apfs.dmg']) test_os_path_spec = path_spec_factory.Factory.NewPathSpec( definitions.TYPE_INDICATOR_OS, location=test_path) test_raw_path_spec = path_spec_factory.Factory.NewPathSpec( definitions.TYPE_INDICATOR_RAW, parent=test_os_path_spec) test_tsk_partition_path_spec = path_spec_factory.Factory.NewPathSpec( definitions.TYPE_INDICATOR_TSK_PARTITION, location='/p1', parent=test_raw_path_spec) test_apfs_container_path_spec = path_spec_factory.Factory.NewPathSpec( definitions.TYPE_INDICATOR_APFS_CONTAINER, location='/apfs1', parent=test_tsk_partition_path_spec) container_file_entry = resolver.Resolver.OpenFileEntry( test_apfs_container_path_spec, resolver_context=resolver_context) fsapfs_volume = container_file_entry.GetAPFSVolume() is_unlocked = apfs_helper.APFSUnlockVolume( fsapfs_volume, test_apfs_container_path_spec, resolver.Resolver.key_chain) self.assertTrue(is_unlocked)
def Unlock(self, scan_context, path_spec, credential_identifier, credential_data): """Unlocks a locked scan node e.g. the scan node of an encrypted volume. Args: scan_context (SourceScannerContext): source scanner context. path_spec (PathSpec): path specification of the locked scan node. credential_identifier (str): credential identifier used to unlock the scan node. credential_data (bytes): credential data used to unlock the scan node. Returns: bool: True if the scan node was successfully unlocked. Raises: BackEndError: if the scan node cannot be unlocked. KeyError: if the scan node does not exists or is not locked. """ if not scan_context.HasScanNode(path_spec): raise KeyError('Scan node does not exist.') if not scan_context.IsLockedScanNode(path_spec): raise KeyError('Scan node is not locked.') resolver.Resolver.key_chain.SetCredential(path_spec, credential_identifier, credential_data) if path_spec.type_indicator == definitions.TYPE_INDICATOR_APFS_CONTAINER: # TODO: consider changes this when upstream changes have been made. # Currently pyfsapfs does not support reading from a volume as a device. # Also see: https://github.com/log2timeline/dfvfs/issues/332 container_file_entry = resolver.Resolver.OpenFileEntry( path_spec, resolver_context=self._resolver_context) fsapfs_volume = container_file_entry.GetAPFSVolume() try: is_locked = not apfs_helper.APFSUnlockVolume( fsapfs_volume, path_spec, resolver.Resolver.key_chain) except IOError as exception: raise errors.BackEndError( 'Unable to unlock APFS volume with error: {0!s}'.format( exception)) else: file_object = resolver.Resolver.OpenFileObject( path_spec, resolver_context=self._resolver_context) is_locked = not file_object or file_object.is_locked file_object.close() if not is_locked: scan_context.UnlockScanNode(path_spec) return not is_locked
def testAPFSUnlockVolumeOnEncryptedAPFS(self): """Tests the APFSUnlockVolume function on an encrypted APFS image.""" resolver.Resolver.key_chain.Empty() resolver_context = context.Context() test_path = self._GetTestFilePath(['apfs_encrypted.dmg']) self._SkipIfPathNotExists(test_path) test_os_path_spec = path_spec_factory.Factory.NewPathSpec( definitions.TYPE_INDICATOR_OS, location=test_path) test_raw_path_spec = path_spec_factory.Factory.NewPathSpec( definitions.TYPE_INDICATOR_RAW, parent=test_os_path_spec) test_tsk_partition_path_spec = path_spec_factory.Factory.NewPathSpec( definitions.TYPE_INDICATOR_TSK_PARTITION, location='/p1', parent=test_raw_path_spec) test_apfs_container_path_spec = path_spec_factory.Factory.NewPathSpec( definitions.TYPE_INDICATOR_APFS_CONTAINER, location='/apfs1', parent=test_tsk_partition_path_spec) container_file_entry = resolver.Resolver.OpenFileEntry( test_apfs_container_path_spec, resolver_context=resolver_context) fsapfs_volume = container_file_entry.GetAPFSVolume() is_unlocked = apfs_helper.APFSUnlockVolume( fsapfs_volume, test_apfs_container_path_spec, resolver.Resolver.key_chain) self.assertFalse(is_unlocked) resolver.Resolver.key_chain.SetCredential( test_apfs_container_path_spec, 'password', self._APFS_PASSWORD) is_unlocked = apfs_helper.APFSUnlockVolume( fsapfs_volume, test_apfs_container_path_spec, resolver.Resolver.key_chain) self.assertTrue(is_unlocked)
def _ScanEncryptedVolumeNode(self, scan_context, scan_node): """Scans an encrypted volume node for supported formats. Args: scan_context (SourceScannerContext): source scanner context. scan_node (SourceScanNode): source scan node. Raises: BackEndError: if the scan node cannot be unlocked. ValueError: if the scan context or scan node is invalid. """ if scan_node.type_indicator == definitions.TYPE_INDICATOR_APFS_CONTAINER: # TODO: consider changes this when upstream changes have been made. # Currently pyfsapfs does not support reading from a volume as a device. # Also see: https://github.com/log2timeline/dfvfs/issues/332 container_file_entry = resolver.Resolver.OpenFileEntry( scan_node.path_spec, resolver_context=self._resolver_context) fsapfs_volume = container_file_entry.GetAPFSVolume() # TODO: unlocking the volume multiple times is inefficient cache volume # object in scan node and use is_locked = fsapfs_volume.is_locked() try: is_locked = not apfs_helper.APFSUnlockVolume( fsapfs_volume, scan_node.path_spec, resolver.Resolver.key_chain) except IOError as exception: raise errors.BackEndError( 'Unable to unlock APFS volume with error: {0!s}'.format( exception)) else: file_object = resolver.Resolver.OpenFileObject( scan_node.path_spec, resolver_context=self._resolver_context) is_locked = not file_object or file_object.is_locked file_object.close() if is_locked: scan_context.LockScanNode(scan_node.path_spec) # For BitLocker To Go add a scan node for the unencrypted part of # the volume. if scan_node.type_indicator == definitions.TYPE_INDICATOR_BDE: path_spec = self.ScanForFileSystem(scan_node.path_spec.parent) if path_spec: scan_context.AddScanNode(path_spec, scan_node.parent_node)
def _Open(self, mode='rb'): """Opens the file system defined by path specification. Args: mode (Optional[str]): file access mode. Raises: AccessError: if the access to open the file was denied. IOError: if the APFS volume could not be retrieved or unlocked. OSError: if the APFS volume could not be retrieved or unlocked. PathSpecError: if the path specification is incorrect. ValueError: if the path specification is invalid. """ if not self._path_spec.HasParent(): raise errors.PathSpecError( 'Unsupported path specification without parent.') if self._path_spec.parent.type_indicator != ( definitions.TYPE_INDICATOR_APFS_CONTAINER): raise errors.PathSpecError( 'Unsupported path specification not type APFS container.') apfs_container_file_system = resolver.Resolver.OpenFileSystem( self._path_spec.parent, resolver_context=self._resolver_context) fsapfs_volume = apfs_container_file_system.GetAPFSVolumeByPathSpec( self._path_spec.parent) if not fsapfs_volume: raise IOError('Unable to retrieve APFS volume') try: is_locked = not apfs_helper.APFSUnlockVolume( fsapfs_volume, self._path_spec.parent, resolver.Resolver.key_chain) except IOError as exception: raise IOError( 'Unable to unlock APFS volume with error: {0!s}'.format( exception)) if is_locked: raise IOError('Unable to unlock APFS volume.') self._fsapfs_volume = fsapfs_volume