Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dissect/target/helpers/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def DynamicDescriptor(types: Sequence[str]) -> RecordDescriptor:
("varint", "gid"),
("string", "gecos"),
("path", "home"),
("string", "shell"),
("path", "shell"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps the source field should also be a path?

("string", "source"),
]

Expand Down
4 changes: 2 additions & 2 deletions dissect/target/plugins/os/unix/_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def users(self, sessions: bool = False) -> Iterator[UnixUserRecord]:
gid=pwent.get(3) or None,
gecos=pwent.get(4),
home=posix_path(pwent.get(5)),
shell=pwent.get(6),
shell=posix_path(pwent.get(6)),
source=passwd_file,
_target=self.target,
)
Expand Down Expand Up @@ -156,7 +156,7 @@ def users(self, sessions: bool = False) -> Iterator[UnixUserRecord]:
yield UnixUserRecord(
name=user["name"],
home=posix_path(user["home"]),
shell=user["shell"],
shell=posix_path(user["shell"]),
source="/var/log/syslog",
_target=self.target,
)
Expand Down
28 changes: 21 additions & 7 deletions dissect/target/plugins/os/unix/bsd/citrix/_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from io import BytesIO
from typing import TYPE_CHECKING

from flow.record.fieldtypes import posix_path
from dissect.util.stream import RangeStream

from dissect.target.filesystems.ffs import FfsFilesystem
Expand Down Expand Up @@ -195,9 +196,17 @@ def version(self) -> str | None:
@export(property=True)
def ips(self) -> list[str]:
return self._ips

@staticmethod
def _as_hashable_path_or_str(value: object | None) -> str | None:
if value is None:
return None
if hasattr(value, "as_posix"):
return value.as_posix()
return str(value)

@export(record=UnixUserRecord)
def users(self) -> Iterator[UnixUserRecord]:
def users(self) -> Iterator:
nstmp_users = set()
seen = set()
nstmp_path = self.target.fs.path("/var/nstmp/")
Expand Down Expand Up @@ -226,23 +235,28 @@ def users(self) -> Iterator[UnixUserRecord]:
# for the root user in /var/nstmp.
user_home = self.target.fs.path("/root")

seen.add((username, user_home.as_posix() if user_home else None, None))
yield UnixUserRecord(name=username, home=user_home)
seen.add((username, self._as_hashable_path_or_str(user_home), None))
yield UnixUserRecord(name=username, home=posix_path(user_home.as_posix()) if user_home else None)

# Yield all users in nstmp that were not observed in the config
for username in nstmp_users:
# The nsmonitor user has a home directory of /var/nstmp/monitors rather than /var/nstmp/nsmonitor
home = nstmp_path.joinpath(username) if username != "nsmonitor" else nstmp_path.joinpath("monitors")
seen.add((username, home.as_posix(), None))
yield UnixUserRecord(name=username, home=home)
seen.add((username, self._as_hashable_path_or_str(home), None))
yield UnixUserRecord(name=username, home=posix_path(home.as_posix()))

# Yield users from /etc/passwd if we have not seem them in previous loops
for user in super().users():
if (user.name, user.home.as_posix(), user.shell) in seen:
if (
user.name,
self._as_hashable_path_or_str(user.home),
self._as_hashable_path_or_str(user.shell),
) in seen:
continue

# To prevent bogus command history for all users without a home whenever a history is located at the root
# of the filesystem, we set the user home to None if their home is equivalent to '/'
user.home = user.home if user.home != "/" else None
user.home = user.home if user.home and str(user.home) != "/" else None
yield user

@export(property=True)
Expand Down
2 changes: 1 addition & 1 deletion dissect/target/plugins/os/unix/bsd/darwin/macos/_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def users(self) -> Iterator[MacOSUserRecord]:
gid=user.get("gid", [None])[0],
gecos=user.get("realname", [None])[0],
home=posix_path(home_dir) if home_dir else None,
shell=user.get("shell", [None])[0],
shell=posix_path(shell) if (shell := user.get("shell", [None])[0]) else None,
source=path,
)
except FileNotFoundError:
Expand Down
5 changes: 3 additions & 2 deletions dissect/target/plugins/os/unix/linux/fortios/_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from tarfile import ReadError
from typing import TYPE_CHECKING, BinaryIO, TextIO

from flow.record.fieldtypes import posix_path
from dissect.util import cpio
from dissect.util.compression import xz

Expand Down Expand Up @@ -282,7 +283,7 @@ def users(self) -> Iterator[FortiOSUserRecord | UnixUserRecord]:
name=username,
password=":".join(entry.get("password", [])),
groups=list(entry.get("accprofile", [])),
home="/root",
home=posix_path("/root"),
_target=self.target,
)
except KeyError as e:
Expand All @@ -297,7 +298,7 @@ def users(self) -> Iterator[FortiOSUserRecord | UnixUserRecord]:
name=username,
password=":".join(entry.get("password", [])),
groups=list(entry.get("profileid", [])),
home="/root",
home=posix_path("/root"),
_target=self.target,
)
except KeyError as e:
Expand Down
6 changes: 5 additions & 1 deletion dissect/target/plugins/os/windows/_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from typing import TYPE_CHECKING, Any
from uuid import UUID

from flow.record.fieldtypes import windows_path

from dissect.target.exceptions import RegistryError, RegistryValueNotFoundError
from dissect.target.helpers.record import WindowsUserRecord
from dissect.target.plugin import OperatingSystem, OSPlugin, export, internal
Expand Down Expand Up @@ -335,10 +337,12 @@ def users(self) -> Iterator[WindowsUserRecord]:
# Use SAM username if available
name = self._sam_by_sid[sid].username if sid in self._sam_by_sid else home.split("\\")[-1]

resolved_home = self.target.resolve(home) if home else None

yield WindowsUserRecord(
sid=subkey.name,
name=name,
home=self.target.resolve(home),
home=windows_path(str(resolved_home)) if resolved_home else None,
_target=self.target,
)

Expand Down
2 changes: 1 addition & 1 deletion tests/plugins/os/unix/bsd/citrix/test__os.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,4 @@ def test_citrix_os(target_citrix: Target, fs_bsd: VirtualFilesystem) -> None:

assert users[7].name == "root" # User entry for /root, from /etc/passwd
assert users[7].home == posix_path("/root")
assert users[7].shell == "/usr/bin/bash"
assert users[7].shell == posix_path("/usr/bin/bash")
4 changes: 1 addition & 3 deletions tests/plugins/os/unix/bsd/darwin/macos/test__os.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
from typing import TYPE_CHECKING
from unittest.mock import Mock

from flow.record.fieldtypes import posix_path

from dissect.target.filesystem import VirtualFilesystem
from dissect.target.plugin import OperatingSystem
from dissect.target.plugins.os.unix.bsd.darwin.macos._os import MacOSPlugin
Expand Down Expand Up @@ -39,7 +37,7 @@ def test_macos_os(target_macos_users: Target, fs_macos: VirtualFilesystem) -> No
assert dissect_user._desc.name == "macos/user"
assert dissect_user.name == "_dissect"
assert dissect_user.passwd == "*"
assert dissect_user.home == posix_path("/Users/dissect")
assert str(dissect_user.home) == "/Users/dissect"
assert dissect_user.shell == "/usr/bin/false"
assert dissect_user.source == "/var/db/dslocal/nodes/Default/users/_dissect.plist"

Expand Down
4 changes: 3 additions & 1 deletion tests/plugins/os/unix/linux/fortios/test__os.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from io import BytesIO
from typing import TYPE_CHECKING

from flow.record.fieldtypes import posix_path

from dissect.target.plugins.os.unix.linux.fortios._os import FortiOSPlugin
from dissect.target.plugins.os.unix.linux.fortios.generic import GenericPlugin
from dissect.target.plugins.os.unix.linux.fortios.locale import FortiOSLocalePlugin
Expand Down Expand Up @@ -93,7 +95,7 @@ def test_fortigate_os(target_unix: Target, fs_unix: VirtualFilesystem) -> None:
assert users[0].name == "admin"
assert users[0].groups == ["super_admin"]
assert users[0].password == "ENC:SH22zS4+QvU399DXuDApIVHu5fGh3wQCwO1aGeqlbA08G9tB/DvJsqLdG9HA18="
assert users[0].home == "/root"
assert users[0].home == posix_path("/root")

assert users[1].hostname == "FortiGate-VM64"
assert users[1].name == "guest"
Expand Down
4 changes: 2 additions & 2 deletions tests/plugins/os/unix/test__os.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,13 @@ def test_users(target_unix_users: Target) -> None:
assert users[0].uid == 0
assert users[0].gid == 0
assert users[0].home == posix_path("/root")
assert users[0].shell == "/bin/bash"
assert users[0].shell == posix_path("/bin/bash")

assert users[1].name == "user"
assert users[1].uid == 1000
assert users[1].gid == 1000
assert users[1].home == posix_path("/home/user")
assert users[1].shell == "/bin/bash"
assert users[1].shell == posix_path("/bin/bash")

assert users[2].name == "+@ngtest"
assert users[2].uid is None
Expand Down