Skip to content

Commit

Permalink
Added CS type to replace FVDE log2timeline#83
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimmetz committed Jan 20, 2022
1 parent 4ca7bf0 commit c8ef67a
Show file tree
Hide file tree
Showing 37 changed files with 1,710 additions and 184 deletions.
2 changes: 1 addition & 1 deletion config/dpkg/control
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Homepage: https://github.com/log2timeline/dfvfs

Package: python3-dfvfs
Architecture: all
Depends: libbde-python3 (>= 20140531), libewf-python3 (>= 20131210), libfsapfs-python3 (>= 20201107), libfsext-python3 (>= 20220112), libfshfs-python3 (>= 20220115), libfsntfs-python3 (>= 20211229), libfsxfs-python3 (>= 20220113), libfvde-python3 (>= 20160719), libfwnt-python3 (>= 20210717), libluksde-python3 (>= 20200101), libmodi-python3 (>= 20210405), libphdi-python3 (>= 20220110), libqcow-python3 (>= 20201213), libsigscan-python3 (>= 20191221), libsmdev-python3 (>= 20140529), libsmraw-python3 (>= 20140612), libvhdi-python3 (>= 20201014), libvmdk-python3 (>= 20140421), libvsgpt-python3 (>= 20211115), libvshadow-python3 (>= 20160109), libvslvm-python3 (>= 20160109), python3-cffi-backend (>= 1.9.1), python3-cryptography (>= 2.0.2), python3-dfdatetime (>= 20211113), python3-dtfabric (>= 20170524), python3-idna (>= 2.5), python3-pytsk3 (>= 20210419), python3-pyxattr (>= 0.7.2), python3-yaml (>= 3.10), ${misc:Depends}
Depends: libbde-python3 (>= 20140531), libewf-python3 (>= 20131210), libfsapfs-python3 (>= 20201107), libfsext-python3 (>= 20220112), libfshfs-python3 (>= 20220115), libfsntfs-python3 (>= 20211229), libfsxfs-python3 (>= 20220113), libfvde-python3 (>= 20220120), libfwnt-python3 (>= 20210717), libluksde-python3 (>= 20200101), libmodi-python3 (>= 20210405), libphdi-python3 (>= 20220110), libqcow-python3 (>= 20201213), libsigscan-python3 (>= 20191221), libsmdev-python3 (>= 20140529), libsmraw-python3 (>= 20140612), libvhdi-python3 (>= 20201014), libvmdk-python3 (>= 20140421), libvsgpt-python3 (>= 20211115), libvshadow-python3 (>= 20160109), libvslvm-python3 (>= 20160109), python3-cffi-backend (>= 1.9.1), python3-cryptography (>= 2.0.2), python3-dfdatetime (>= 20211113), python3-dtfabric (>= 20170524), python3-idna (>= 2.5), python3-pytsk3 (>= 20210419), python3-pyxattr (>= 0.7.2), python3-yaml (>= 3.10), ${misc:Depends}
Description: Python 3 module of dfVFS
dfVFS, or Digital Forensics Virtual File System, provides read-only access to
file-system objects from various storage media types and file formats. The goal
Expand Down
2 changes: 1 addition & 1 deletion dependencies.ini
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ version_property: get_version()
[pyfvde]
dpkg_name: libfvde-python3
l2tbinaries_name: libfvde
minimum_version: 20160719
minimum_version: 20220120
pypi_name: libfvde-python
rpm_name: libfvde-python3
version_property: get_version()
Expand Down
1 change: 1 addition & 0 deletions dfvfs/credentials/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from dfvfs.credentials import apfs_credentials
from dfvfs.credentials import bde_credentials
from dfvfs.credentials import cs_credentials
from dfvfs.credentials import encrypted_stream_credentials
from dfvfs.credentials import fvde_credentials
from dfvfs.credentials import luksde_credentials
19 changes: 19 additions & 0 deletions dfvfs/credentials/cs_credentials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
"""The Core Storage (CS) credentials."""

from dfvfs.credentials import credentials
from dfvfs.credentials import manager
from dfvfs.lib import definitions


class CSCredentials(credentials.Credentials):
"""Core Storage (CS) credentials."""

# TODO: add support for key_data credential, needs pyfvde update.
CREDENTIALS = frozenset([
'encrypted_root_plist', 'password', 'recovery_password'])

TYPE_INDICATOR = definitions.TYPE_INDICATOR_CS


manager.CredentialsManager.RegisterCredentials(CSCredentials())
163 changes: 163 additions & 0 deletions dfvfs/file_io/cs_file_io.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# -*- coding: utf-8 -*-
"""The Core Storage (CS) file-like object."""

import os

from dfvfs.file_io import file_io
from dfvfs.lib import cs_helper
from dfvfs.lib import errors
from dfvfs.resolver import resolver


class CSFile(file_io.FileIO):
"""File input/output (IO) object using pyfvde."""

def __init__(self, resolver_context, path_spec):
"""Initializes a file input/output (IO) object.
Args:
resolver_context (Context): resolver context.
path_spec (PathSpec): a path specification.
"""
super(CSFile, self).__init__(resolver_context, path_spec)
self._file_system = None
self._fvde_logical_volume = None

def _Close(self):
"""Closes the file-like object."""
self._fvde_logical_volume = None

self._file_system = None

def _Open(self, mode='rb'):
"""Opens the file-like object 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 file-like object could not be opened.
OSError: if the file-like object could not be opened.
PathSpecError: if the path specification is incorrect.
"""
volume_index = cs_helper.CSPathSpecGetVolumeIndex(self._path_spec)
if volume_index is None:
raise errors.PathSpecError(
'Unable to retrieve volume index from path specification.')

self._file_system = resolver.Resolver.OpenFileSystem(
self._path_spec, resolver_context=self._resolver_context)
fvde_volume_group = self._file_system.GetFVDEVolumeGroup()

if (volume_index < 0 or
volume_index >= fvde_volume_group.number_of_logical_volumes):
raise errors.PathSpecError((
'Unable to retrieve logical volume index: {0:d} from path '
'specification.').format(volume_index))

self._fvde_logical_volume = fvde_volume_group.get_logical_volume(
volume_index)

self._UnlockFVDELogicalVolume(self._fvde_logical_volume, self._path_spec)

def _UnlockFVDELogicalVolume(self, fvde_logical_volume, path_spec):
"""Unlocks the Core Storage (CS) logical volume if necessary.
Args:
fvde_logical_volume (pyfvde.logical_volume): Core Storage (CS) logical
volume.
path_spec (PathSpec): path specification.
"""
if fvde_logical_volume.is_locked():
resolver.Resolver.key_chain.ExtractCredentialsFromPathSpec(path_spec)

password = resolver.Resolver.key_chain.GetCredential(
path_spec, 'password')
if password:
fvde_logical_volume.set_password(password)

recovery_password = resolver.Resolver.key_chain.GetCredential(
path_spec, 'recovery_password')
if recovery_password:
fvde_logical_volume.set_recovery_password(recovery_password)

fvde_logical_volume.unlock()

@property
def is_locked(self):
"""bool: True if the volume is locked."""
return self._fvde_logical_volume.is_locked()

# Note: that the following functions do not follow the style guide
# because they are part of the file-like object interface.
# pylint: disable=invalid-name

def read(self, size=None):
"""Reads a byte string from the file-like object at the current offset.
The function will read a byte string of the specified size or
all of the remaining data if no size was specified.
Args:
size (Optional[int]): number of bytes to read, where None is all
remaining data.
Returns:
bytes: data read.
Raises:
IOError: if the read failed.
OSError: if the read failed.
"""
if not self._is_open:
raise IOError('Not opened.')

return self._fvde_logical_volume.read(size)

def seek(self, offset, whence=os.SEEK_SET):
"""Seeks to an offset within the file-like object.
Args:
offset (int): offset to seek to.
whence (Optional(int)): value that indicates whether offset is an absolute
or relative position within the file.
Raises:
IOError: if the seek failed.
OSError: if the seek failed.
"""
if not self._is_open:
raise IOError('Not opened.')

self._fvde_logical_volume.seek(offset, whence)

def get_offset(self):
"""Retrieves the current offset into the file-like object.
Returns:
int: current offset into the file-like object.
Raises:
IOError: if the file-like object has not been opened.
OSError: if the file-like object has not been opened.
"""
if not self._is_open:
raise IOError('Not opened.')

return self._fvde_logical_volume.get_offset()

def get_size(self):
"""Retrieves the size of the file-like object.
Returns:
int: size of the file-like object data.
Raises:
IOError: if the file-like object has not been opened.
OSError: if the file-like object has not been opened.
"""
if not self._is_open:
raise IOError('Not opened.')

return self._fvde_logical_volume.size
6 changes: 3 additions & 3 deletions dfvfs/file_io/lvm_file_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from dfvfs.file_io import file_io
from dfvfs.lib import errors
from dfvfs.lib import lvm
from dfvfs.lib import lvm_helper
from dfvfs.resolver import resolver


Expand Down Expand Up @@ -41,7 +41,7 @@ def _Open(self, mode='rb'):
OSError: if the file-like object could not be opened.
PathSpecError: if the path specification is incorrect.
"""
volume_index = lvm.LVMPathSpecGetVolumeIndex(self._path_spec)
volume_index = lvm_helper.LVMPathSpecGetVolumeIndex(self._path_spec)
if volume_index is None:
raise errors.PathSpecError(
'Unable to retrieve volume index from path specification.')
Expand All @@ -53,7 +53,7 @@ def _Open(self, mode='rb'):
if (volume_index < 0 or
volume_index >= vslvm_volume_group.number_of_logical_volumes):
raise errors.PathSpecError((
'Unable to retrieve LVM logical volume index: {0:d} from path '
'Unable to retrieve logical volume index: {0:d} from path '
'specification.').format(volume_index))

self._vslvm_logical_volume = vslvm_volume_group.get_logical_volume(
Expand Down
8 changes: 6 additions & 2 deletions dfvfs/lib/apfs_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
"""Helper functions for Apple File System (APFS) support."""


_APFS_LOCATION_PREFIX = '/apfs'
_APFS_LOCATION_PREFIX_LENGTH = len(_APFS_LOCATION_PREFIX)


def APFSContainerPathSpecGetVolumeIndex(path_spec):
"""Retrieves the volume index from the path specification.
Expand All @@ -16,11 +20,11 @@ def APFSContainerPathSpecGetVolumeIndex(path_spec):
return volume_index

location = getattr(path_spec, 'location', None)
if location is None or not location.startswith('/apfs'):
if location is None or not location.startswith(_APFS_LOCATION_PREFIX):
return None

try:
volume_index = int(location[5:], 10) - 1
volume_index = int(location[_APFS_LOCATION_PREFIX_LENGTH:], 10) - 1
except (TypeError, ValueError):
volume_index = None

Expand Down
35 changes: 35 additions & 0 deletions dfvfs/lib/cs_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
"""Helper function for Core Storage (CS) support."""


_CS_LOCATION_PREFIX = '/cs'
_CS_LOCATION_PREFIX_LENGTH = len(_CS_LOCATION_PREFIX)


def CSPathSpecGetVolumeIndex(path_spec):
"""Retrieves the volume index from the path specification.
Args:
path_spec (PathSpec): path specification.
Returns:
int: volume index or None if not available.
"""
volume_index = getattr(path_spec, 'volume_index', None)

if volume_index is None:
location = getattr(path_spec, 'location', None)

if location is None or not location.startswith(_CS_LOCATION_PREFIX):
return None

volume_index = None
try:
volume_index = int(location[_CS_LOCATION_PREFIX_LENGTH:], 10) - 1
except ValueError:
pass

if volume_index is None or volume_index < 0:
return None

return volume_index
7 changes: 6 additions & 1 deletion dfvfs/lib/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@
TYPE_INDICATOR_BZIP2 = 'BZIP2'
TYPE_INDICATOR_COMPRESSED_STREAM = 'COMPRESSED_STREAM'
TYPE_INDICATOR_CPIO = 'CPIO'
TYPE_INDICATOR_CS = 'CS'
TYPE_INDICATOR_DATA_RANGE = 'DATA_RANGE'
TYPE_INDICATOR_ENCODED_STREAM = 'ENCODED_STREAM'
TYPE_INDICATOR_ENCRYPTED_STREAM = 'ENCRYPTED_STREAM'
TYPE_INDICATOR_EWF = 'EWF'
TYPE_INDICATOR_EXT = 'EXT'
TYPE_INDICATOR_FAKE = 'FAKE'
TYPE_INDICATOR_FVDE = 'FVDE'
TYPE_INDICATOR_GPT = 'GPT'
TYPE_INDICATOR_GZIP = 'GZIP'
TYPE_INDICATOR_HFS = 'HFS'
Expand All @@ -68,16 +68,21 @@
TYPE_INDICATOR_XZ = 'XZ'
TYPE_INDICATOR_ZIP = 'ZIP'

# Deprecated type indicator definitions.
TYPE_INDICATOR_FVDE = 'FVDE'

TYPE_INDICATORS_WITH_ENCRYPTION_SUPPORT = frozenset([
TYPE_INDICATOR_APFS_CONTAINER,
TYPE_INDICATOR_BDE,
TYPE_INDICATOR_CS,
TYPE_INDICATOR_FVDE,
TYPE_INDICATOR_LUKSDE])

# Deprecated in favor of TYPE_INDICATORS_WITH_ENCRYPTION_SUPPORT.
# Volume types that support encryption.
ENCRYPTED_VOLUME_TYPE_INDICATORS = frozenset([
TYPE_INDICATOR_BDE,
TYPE_INDICATOR_CS,
TYPE_INDICATOR_FVDE,
TYPE_INDICATOR_LUKSDE])

Expand Down
8 changes: 6 additions & 2 deletions dfvfs/lib/lvm.py → dfvfs/lib/lvm_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
"""Helper functions for Logical Volume Manager (LVM) support."""


_LVM_LOCATION_PREFIX = '/lvm'
_LVM_LOCATION_PREFIX_LENGTH = len(_LVM_LOCATION_PREFIX)


def LVMPathSpecGetVolumeIndex(path_spec):
"""Retrieves the volume index from the path specification.
Expand All @@ -16,12 +20,12 @@ def LVMPathSpecGetVolumeIndex(path_spec):
if volume_index is None:
location = getattr(path_spec, 'location', None)

if location is None or not location.startswith('/lvm'):
if location is None or not location.startswith(_LVM_LOCATION_PREFIX):
return None

volume_index = None
try:
volume_index = int(location[4:], 10) - 1
volume_index = int(location[_LVM_LOCATION_PREFIX_LENGTH:], 10) - 1
except ValueError:
pass

Expand Down
1 change: 1 addition & 0 deletions dfvfs/path/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from dfvfs.path import bde_path_spec
from dfvfs.path import compressed_stream_path_spec
from dfvfs.path import cpio_path_spec
from dfvfs.path import cs_path_spec
from dfvfs.path import data_range_path_spec
from dfvfs.path import encoded_stream_path_spec
from dfvfs.path import encrypted_stream_path_spec
Expand Down
Loading

0 comments on commit c8ef67a

Please sign in to comment.