From abcd4be454c143c83a3341cb7486aeee1535c376 Mon Sep 17 00:00:00 2001 From: pyrco <105293448+pyrco@users.noreply.github.com> Date: Thu, 16 Nov 2023 16:35:07 +0100 Subject: [PATCH] Put plugins in the proper unix or linux namespace (DIS-1996) --- dissect/target/plugins/os/unix/cronjobs.py | 20 ++++--------------- .../os/unix/{linux => }/esxi/__init__.py | 0 .../plugins/os/unix/{linux => }/esxi/_os.py | 4 ++-- dissect/target/plugins/os/unix/generic.py | 4 +++- dissect/target/plugins/os/unix/history.py | 2 +- .../plugins/os/unix/{ => linux}/services.py | 0 .../plugins/os/unix/linux/suse/zypper.py | 8 ++++---- dissect/target/plugins/os/unix/locale.py | 4 +++- .../target/plugins/os/unix/packagemanager.py | 6 +++--- dissect/target/plugins/os/unix/shadow.py | 2 +- .../os/unix/{ => linux}/test_services.py | 11 ++++++---- tests/test_plugin.py | 4 ++-- 12 files changed, 30 insertions(+), 35 deletions(-) rename dissect/target/plugins/os/unix/{linux => }/esxi/__init__.py (100%) rename dissect/target/plugins/os/unix/{linux => }/esxi/_os.py (99%) rename dissect/target/plugins/os/unix/{ => linux}/services.py (100%) rename tests/plugins/os/unix/{ => linux}/test_services.py (94%) diff --git a/dissect/target/plugins/os/unix/cronjobs.py b/dissect/target/plugins/os/unix/cronjobs.py index daa289490..1cf19620c 100644 --- a/dissect/target/plugins/os/unix/cronjobs.py +++ b/dissect/target/plugins/os/unix/cronjobs.py @@ -4,7 +4,7 @@ from dissect.target.plugin import Plugin, export CronjobRecord = TargetRecordDescriptor( - "linux/cronjob", + "unix/cronjob", [ ("string", "minute"), ("string", "hour"), @@ -12,13 +12,13 @@ ("string", "month"), ("string", "weekday"), ("string", "user"), - ("wstring", "command"), + ("string", "command"), ("path", "source"), ], ) EnvironmentVariableRecord = TargetRecordDescriptor( - "linux/environmentvariable", + "unix/environmentvariable", [ ("string", "key"), ("string", "value"), @@ -31,19 +31,6 @@ class CronjobPlugin(Plugin): def check_compatible(self) -> None: pass - def get_record(self, minute, hour, day, month, weekday, usr, cmd, path): - return CronjobRecord( - minute=minute, - hour=hour, - day=day, - month=month, - weekday=weekday, - user=usr, - command=cmd, - source=self.resolver.resolve(path), - _target=self.target, - ) - def parse_crontab(self, file_path): for line in file_path.open("rt"): line = line.strip() @@ -93,6 +80,7 @@ def cronjobs(self): "/var/spool/cron", "/var/spool/cron/crontabs", "/etc/cron.d", + "/usr/local/etc/cron.d", # FreeBSD ] for path in crontab_dirs: fspath = self.target.fs.path(path) diff --git a/dissect/target/plugins/os/unix/linux/esxi/__init__.py b/dissect/target/plugins/os/unix/esxi/__init__.py similarity index 100% rename from dissect/target/plugins/os/unix/linux/esxi/__init__.py rename to dissect/target/plugins/os/unix/esxi/__init__.py diff --git a/dissect/target/plugins/os/unix/linux/esxi/_os.py b/dissect/target/plugins/os/unix/esxi/_os.py similarity index 99% rename from dissect/target/plugins/os/unix/linux/esxi/_os.py rename to dissect/target/plugins/os/unix/esxi/_os.py index e487cd5a9..f1cb41637 100644 --- a/dissect/target/plugins/os/unix/linux/esxi/_os.py +++ b/dissect/target/plugins/os/unix/esxi/_os.py @@ -25,7 +25,7 @@ from dissect.target.filesystems import tar from dissect.target.helpers.record import TargetRecordDescriptor from dissect.target.plugin import OperatingSystem, arg, export, internal -from dissect.target.plugins.os.unix.linux._os import LinuxPlugin +from dissect.target.plugins.os.unix._os import UnixPlugin from dissect.target.target import Target VirtualMachineRecord = TargetRecordDescriptor( @@ -36,7 +36,7 @@ ) -class ESXiPlugin(LinuxPlugin): +class ESXiPlugin(UnixPlugin): """ESXi OS plugin ESXi partitioning varies between versions. Generally, specific partition numbers have special meaning. diff --git a/dissect/target/plugins/os/unix/generic.py b/dissect/target/plugins/os/unix/generic.py index 61a3d3649..5d7c06533 100644 --- a/dissect/target/plugins/os/unix/generic.py +++ b/dissect/target/plugins/os/unix/generic.py @@ -30,16 +30,18 @@ def activity(self) -> Optional[datetime]: def install_date(self) -> Optional[datetime]: """Return the likely install date of the operating system.""" + # Although this purports to be a generic function for Unix targets, + # these paths are Linux specific. files = [ # Debian "/var/log/installer/install-journal.txt", "/var/log/installer/syslog", + "/var/lib/dpkg/arch", # RedHat "/root/anaconda-ks.cfg", # Generic "/etc/hostname", "/etc/machine-id", - "/var/lib/dpkg/arch", ] dates = [] diff --git a/dissect/target/plugins/os/unix/history.py b/dissect/target/plugins/os/unix/history.py index 8463e8a68..edc1910ee 100644 --- a/dissect/target/plugins/os/unix/history.py +++ b/dissect/target/plugins/os/unix/history.py @@ -11,7 +11,7 @@ from dissect.target.plugin import Plugin, export, internal CommandHistoryRecord = create_extended_descriptor([UserRecordDescriptorExtension])( - "linux/history", + "unix/history", [ ("datetime", "ts"), ("string", "command"), diff --git a/dissect/target/plugins/os/unix/services.py b/dissect/target/plugins/os/unix/linux/services.py similarity index 100% rename from dissect/target/plugins/os/unix/services.py rename to dissect/target/plugins/os/unix/linux/services.py diff --git a/dissect/target/plugins/os/unix/linux/suse/zypper.py b/dissect/target/plugins/os/unix/linux/suse/zypper.py index 77a6a4c8f..fca0dce27 100644 --- a/dissect/target/plugins/os/unix/linux/suse/zypper.py +++ b/dissect/target/plugins/os/unix/linux/suse/zypper.py @@ -28,17 +28,17 @@ def logs(self) -> Iterator[PackageManagerLogRecord]: Example log format:: 2022-12-16 12:56:23|command|root@ec9fa6d67dda|'zypper' 'install' 'unzip'| - 2022-12-16 12:56:23|install|update-alternatives|1.21.8-1.4|x86_64||repo-oss|b4d6389437e306d6104559c82d09fce15c4486fbc7fd215cc33d265ff729aaf1| # noqa + 2022-12-16 12:56:23|install|update-alternatives|1.21.8-1.4|x86_64||repo-oss|b4d6389437e306d6104559c82d09fce15c4486fbc7fd215cc33d265ff729aaf1| # 2022-12-16 12:56:23 unzip-6.00-41.1.x86_64.rpm installed ok # Additional rpm output: # update-alternatives: using /usr/bin/unzip-plain to provide /usr/bin/unzip (unzip) in auto mode # - 2022-12-16 12:56:23|install|unzip|6.00-41.1|x86_64|root@ec9fa6d67dda|repo-oss|d7e42c9d83f97cf3b7eceb4d3fa64e445a33a7a33f387366734c444d5571cb3a| # noqa + 2022-12-16 12:56:23|install|unzip|6.00-41.1|x86_64|root@ec9fa6d67dda|repo-oss|d7e42c9d83f97cf3b7eceb4d3fa64e445a33a7a33f387366734c444d5571cb3a| 2022-12-16 12:57:50|command|root@ec9fa6d67dda|'zypper' 'remove' 'unzip'| # 2022-12-16 12:57:50 unzip-6.00-41.1.x86_64 removed ok # Additional rpm output: - # update-alternatives: warning: alternative /usr/bin/unzipsfx-plain (part of link group unzipsfx) doesn't exist; removing from list of alternatives # noqa - # update-alternatives: warning: alternative /usr/bin/zipgrep-plain (part of link group zipgrep) doesn't exist; removing from list of alternatives # noqa + # update-alternatives: warning: alternative /usr/bin/unzipsfx-plain (part of link group unzipsfx) doesn't exist; removing from list of alternatives + # update-alternatives: warning: alternative /usr/bin/zipgrep-plain (part of link group zipgrep) doesn't exist; removing from list of alternatives # 2022-12-16 12:57:50|remove |unzip|6.00-41.1|x86_64|root@ec9fa6d67dda| 2022-12-16 12:58:49|command|root@ec9fa6d67dda|'zypper' 'install' 'unzip'| diff --git a/dissect/target/plugins/os/unix/locale.py b/dissect/target/plugins/os/unix/locale.py index 18928a051..dd7e56540 100644 --- a/dissect/target/plugins/os/unix/locale.py +++ b/dissect/target/plugins/os/unix/locale.py @@ -5,7 +5,7 @@ from dissect.target.plugin import Plugin, export UnixKeyboardRecord = TargetRecordDescriptor( - "linux/keyboard", + "unix/keyboard", [ ("string", "layout"), ("string", "model"), @@ -64,6 +64,8 @@ def timezone(self): @export(property=True) def language(self): """Get the configured locale(s) of the system.""" + # Although this purports to be a generic function for Unix targets, + # these paths are Linux specific. locale_paths = ["/etc/default/locale", "/etc/locale.conf"] found_languages = [] diff --git a/dissect/target/plugins/os/unix/packagemanager.py b/dissect/target/plugins/os/unix/packagemanager.py index 74ab5f3db..3bdf34175 100644 --- a/dissect/target/plugins/os/unix/packagemanager.py +++ b/dissect/target/plugins/os/unix/packagemanager.py @@ -9,7 +9,7 @@ from dissect.target.plugin import Plugin, export PackageManagerLogRecord = TargetRecordDescriptor( - "linux/log/packagemanager", + "unix/log/packagemanager", [ ("datetime", "ts"), ("string", "package_manager"), @@ -61,7 +61,7 @@ def __init__(self, target: Target): for entry in self.TOOLS: try: self._plugins.append(getattr(self.target, entry)) - except Exception: # noqa + except Exception: target.log.exception(f"Failed to load tool plugin: {entry}") def check_compatible(self) -> None: @@ -77,5 +77,5 @@ def _func(self, f: str) -> Iterator[PackageManagerLogRecord]: @export(record=PackageManagerLogRecord) def logs(self) -> Iterator[PackageManagerLogRecord]: - """Returns logs from apt, yum and zypper package managers.""" + """Returns logs from all available Unix package managers.""" yield from self._func("logs") diff --git a/dissect/target/plugins/os/unix/shadow.py b/dissect/target/plugins/os/unix/shadow.py index 15cd18b4d..194348b39 100644 --- a/dissect/target/plugins/os/unix/shadow.py +++ b/dissect/target/plugins/os/unix/shadow.py @@ -5,7 +5,7 @@ from dissect.target.plugin import Plugin, export UnixShadowRecord = TargetRecordDescriptor( - "linux/shadow", + "unix/shadow", [ ("string", "name"), ("string", "crypt"), diff --git a/tests/plugins/os/unix/test_services.py b/tests/plugins/os/unix/linux/test_services.py similarity index 94% rename from tests/plugins/os/unix/test_services.py rename to tests/plugins/os/unix/linux/test_services.py index a6ff331db..4eda15075 100644 --- a/tests/plugins/os/unix/test_services.py +++ b/tests/plugins/os/unix/linux/test_services.py @@ -3,11 +3,14 @@ import pytest -from dissect.target.plugins.os.unix.services import ServicesPlugin, parse_systemd_config +from dissect.target.plugins.os.unix.linux.services import ( + ServicesPlugin, + parse_systemd_config, +) from tests._utils import absolute_path -def test_unix_services(target_unix_users, fs_unix): +def test_services(target_unix_users, fs_unix): systemd_service_1 = absolute_path("_data/plugins/os/unix/services/systemd.service") systemd_service_2 = absolute_path("_data/plugins/os/unix/services/systemd2.service") initd_service_1 = absolute_path("_data/plugins/os/unix/services/initd.sh") @@ -57,13 +60,13 @@ def test_unix_services(target_unix_users, fs_unix): ("[Unit]\ntest=hello\tme", 'Unit_test="hello\tme"'), ], ) -def test_unix_systemd_parser(assignment, expected_value): +def test_systemd(assignment, expected_value): data = parse_systemd_config(StringIO(assignment)) assert data == expected_value @pytest.mark.xfail -def test_unix_systemd_known_fails(): +def test_systemd_known_fails(): # While this should return `Hello world test help`, # the configparser attempts to append `help` as a value to the list of options # belonging to the key before it `test\\`. diff --git a/tests/test_plugin.py b/tests/test_plugin.py index f7981a7ce..000ad253d 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -131,7 +131,7 @@ def test_find_plugin_function_unix(target_unix: Target) -> None: assert len(found) == 1 assert found[0].name == "services" - assert found[0].path == "os.unix.services.services" + assert found[0].path == "os.unix.linux.services.services" TestRecord = create_extended_descriptor([UserRecordDescriptorExtension])( @@ -184,7 +184,7 @@ def test_find_plugin_function_default(target_default: Target) -> None: assert "services" in names assert "services" in names paths = [item.path for item in found] - assert "os.unix.services.services" in paths + assert "os.unix.linux.services.services" in paths assert "os.windows.services.services" in paths found, _ = find_plugin_functions(target_default, "mcafee.msc")