dissect.database 1.2.dev8__py3-none-any.whl → 1.2.dev9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. dissect/database/ese/ntds/database.py +6 -6
  2. dissect/database/ese/ntds/ntds.py +12 -3
  3. dissect/database/ese/ntds/objects/attributeschema.py +15 -0
  4. dissect/database/ese/ntds/objects/certificationauthority.py +5 -0
  5. dissect/database/ese/ntds/objects/computer.py +15 -0
  6. dissect/database/ese/ntds/objects/configuration.py +5 -0
  7. dissect/database/ese/ntds/objects/crossref.py +14 -0
  8. dissect/database/ese/ntds/objects/crossrefcontainer.py +5 -0
  9. dissect/database/ese/ntds/objects/domaindns.py +5 -0
  10. dissect/database/ese/ntds/objects/group.py +10 -0
  11. dissect/database/ese/ntds/objects/grouppolicycontainer.py +5 -0
  12. dissect/database/ese/ntds/objects/ntdsdsa.py +5 -0
  13. dissect/database/ese/ntds/objects/object.py +36 -10
  14. dissect/database/ese/ntds/objects/organizationalperson.py +16 -1
  15. dissect/database/ese/ntds/objects/organizationalunit.py +26 -1
  16. dissect/database/ese/ntds/objects/person.py +10 -0
  17. dissect/database/ese/ntds/objects/pkienrollmentservice.py +5 -0
  18. dissect/database/ese/ntds/objects/server.py +5 -0
  19. dissect/database/ese/ntds/objects/site.py +16 -1
  20. dissect/database/ese/ntds/objects/top.py +5 -0
  21. dissect/database/ese/ntds/objects/trusteddomain.py +36 -0
  22. dissect/database/ese/ntds/objects/user.py +71 -4
  23. dissect/database/ese/ntds/schema.py +3 -3
  24. dissect/database/ese/ntds/util.py +103 -33
  25. {dissect_database-1.2.dev8.dist-info → dissect_database-1.2.dev9.dist-info}/METADATA +1 -1
  26. {dissect_database-1.2.dev8.dist-info → dissect_database-1.2.dev9.dist-info}/RECORD +31 -31
  27. {dissect_database-1.2.dev8.dist-info → dissect_database-1.2.dev9.dist-info}/WHEEL +0 -0
  28. {dissect_database-1.2.dev8.dist-info → dissect_database-1.2.dev9.dist-info}/entry_points.txt +0 -0
  29. {dissect_database-1.2.dev8.dist-info → dissect_database-1.2.dev9.dist-info}/licenses/COPYRIGHT +0 -0
  30. {dissect_database-1.2.dev8.dist-info → dissect_database-1.2.dev9.dist-info}/licenses/LICENSE +0 -0
  31. {dissect_database-1.2.dev8.dist-info → dissect_database-1.2.dev9.dist-info}/top_level.txt +0 -0
@@ -11,7 +11,7 @@ from dissect.database.ese.ntds.pek import PEK
11
11
  from dissect.database.ese.ntds.query import Query
12
12
  from dissect.database.ese.ntds.schema import Schema
13
13
  from dissect.database.ese.ntds.sd import SecurityDescriptor
14
- from dissect.database.ese.ntds.util import DN, DatabaseFlags, SearchFlags, encode_value
14
+ from dissect.database.ese.ntds.util import DN, DatabaseFlag, SearchFlag, encode_value
15
15
 
16
16
  if TYPE_CHECKING:
17
17
  from collections.abc import Iterator
@@ -44,14 +44,14 @@ class Database:
44
44
  self.data._make_dn.cache_clear()
45
45
 
46
46
  @cached_property
47
- def flags(self) -> DatabaseFlags | None:
47
+ def flags(self) -> DatabaseFlag | None:
48
48
  """Return the database flags."""
49
49
  if self.hiddeninfo is None:
50
50
  return None
51
51
 
52
- result = DatabaseFlags(0)
52
+ result = DatabaseFlag(0)
53
53
  flags = self.hiddeninfo.get("flags_col")
54
- for idx, member in enumerate(DatabaseFlags.__members__.values()):
54
+ for idx, member in enumerate(DatabaseFlag.__members__.values()):
55
55
  if flags[idx] == ord(b"1"):
56
56
  result = member if result is None else result | member
57
57
 
@@ -256,9 +256,9 @@ class DataTable:
256
256
  if schema.search_flags is None:
257
257
  raise ValueError(f"Attribute is not indexed: {attribute!r}")
258
258
 
259
- if SearchFlags.Indexed in schema.search_flags:
259
+ if SearchFlag.Indexed in schema.search_flags:
260
260
  name = f"INDEX_{schema.id:08x}"
261
- elif SearchFlags.TupleIndexed in schema.search_flags:
261
+ elif SearchFlag.TupleIndexed in schema.search_flags:
262
262
  name = f"INDEX_T_{schema.id:08x}"
263
263
  else:
264
264
  # TODO add ContainerIndexed
@@ -20,6 +20,7 @@ if TYPE_CHECKING:
20
20
  TrustedDomain,
21
21
  User,
22
22
  )
23
+ from dissect.database.ese.ntds.objects.organizationalunit import OrganizationalUnit
23
24
  from dissect.database.ese.ntds.pek import PEK
24
25
 
25
26
 
@@ -83,11 +84,11 @@ class NTDS:
83
84
 
84
85
  def groups(self) -> Iterator[Group]:
85
86
  """Get all group objects from the database."""
86
- yield from self.search(objectCategory="group")
87
+ yield from self.search(objectClass="group")
87
88
 
88
89
  def servers(self) -> Iterator[Server]:
89
90
  """Get all server objects from the database."""
90
- yield from self.search(objectCategory="server")
91
+ yield from self.search(objectClass="server")
91
92
 
92
93
  def users(self) -> Iterator[User]:
93
94
  """Get all user objects from the database."""
@@ -95,7 +96,11 @@ class NTDS:
95
96
 
96
97
  def computers(self) -> Iterator[Computer]:
97
98
  """Get all computer objects from the database."""
98
- yield from self.search(objectCategory="computer")
99
+ yield from self.search(objectClass="computer")
100
+
101
+ def domains(self) -> Iterator[DomainDNS]:
102
+ """Get all domain objects from the database."""
103
+ yield from self.search(objectClass="domainDNS")
99
104
 
100
105
  def trusts(self) -> Iterator[TrustedDomain]:
101
106
  """Get all trust objects from the database."""
@@ -105,6 +110,10 @@ class NTDS:
105
110
  """Get all group policy objects (GPO) objects from the database."""
106
111
  yield from self.search(objectClass="groupPolicyContainer")
107
112
 
113
+ def organizational_units(self) -> Iterator[OrganizationalUnit]:
114
+ """Get all organizational unit (OU) objects from the database."""
115
+ yield from self.search(objectClass="organizationalUnit")
116
+
108
117
  def secrets(self) -> Iterator[Secret]:
109
118
  """Get all secret objects from the database."""
110
119
  yield from self.search(objectClass="secret")
@@ -1,6 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import TYPE_CHECKING, ClassVar
4
+
3
5
  from dissect.database.ese.ntds.objects.top import Top
6
+ from dissect.database.ese.ntds.util import SearchFlag, SystemFlagAttribute
7
+
8
+ if TYPE_CHECKING:
9
+ from dissect.database.ese.ntds.objects.object import DecoderMap
4
10
 
5
11
 
6
12
  class AttributeSchema(Top):
@@ -11,3 +17,12 @@ class AttributeSchema(Top):
11
17
  """
12
18
 
13
19
  __object_class__ = "attributeSchema"
20
+ __decoders__: ClassVar[DecoderMap] = {
21
+ "searchFlags": lambda db, value: SearchFlag(value),
22
+ "systemFlags": lambda db, value: SystemFlagAttribute(value),
23
+ }
24
+
25
+ @property
26
+ def search_flags(self) -> SearchFlag | None:
27
+ """Return the searchFlags of this attribute schema."""
28
+ return self.get("searchFlags")
@@ -11,3 +11,8 @@ class CertificationAuthority(Top):
11
11
  """
12
12
 
13
13
  __object_class__ = "certificationAuthority"
14
+
15
+ @property
16
+ def dns_host_name(self) -> str | None:
17
+ """Return the dNSHostName of this Certification Authority."""
18
+ return self.get("dNSHostName")
@@ -23,6 +23,21 @@ class Computer(User):
23
23
  def __repr_body__(self) -> str:
24
24
  return f"name={self.name!r}"
25
25
 
26
+ @property
27
+ def dns_host_name(self) -> str | None:
28
+ """Return the dNSHostName of this computer."""
29
+ return self.get("dNSHostName")
30
+
31
+ @property
32
+ def operating_system(self) -> str | None:
33
+ """Return the operatingSystem of this computer."""
34
+ return self.get("operatingSystem")
35
+
36
+ @property
37
+ def operating_system_version(self) -> str | None:
38
+ """Return the operatingSystemVersion of this computer."""
39
+ return self.get("operatingSystemVersion")
40
+
26
41
  def fve_recovery_information(self) -> Iterator[MSFVERecoveryInformation]:
27
42
  """Return the BitLocker recovery information objects associated with this computer."""
28
43
  for child in self.children():
@@ -11,3 +11,8 @@ class Configuration(Top):
11
11
  """
12
12
 
13
13
  __object_class__ = "configuration"
14
+
15
+ @property
16
+ def gp_link(self) -> str | None:
17
+ """Return the group policy link of the configuration."""
18
+ return self.get("gPLink")
@@ -1,6 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import TYPE_CHECKING, ClassVar
4
+
3
5
  from dissect.database.ese.ntds.objects.top import Top
6
+ from dissect.database.ese.ntds.util import SystemFlagCrossRef
7
+
8
+ if TYPE_CHECKING:
9
+ from dissect.database.ese.ntds.objects.object import DecoderMap
4
10
 
5
11
 
6
12
  class CrossRef(Top):
@@ -11,3 +17,11 @@ class CrossRef(Top):
11
17
  """
12
18
 
13
19
  __object_class__ = "crossRef"
20
+ __decoders__: ClassVar[DecoderMap] = {
21
+ "systemFlags": lambda db, value: SystemFlagCrossRef(value),
22
+ }
23
+
24
+ @property
25
+ def behavior_version(self) -> int | None:
26
+ """Return the msDS-Behavior-Version of this cross-reference."""
27
+ return self.get("msDS-Behavior-Version")
@@ -11,3 +11,8 @@ class CrossRefContainer(Top):
11
11
  """
12
12
 
13
13
  __object_class__ = "crossRefContainer"
14
+
15
+ @property
16
+ def behavior_version(self) -> int | None:
17
+ """Return the msDS-Behavior-Version of this cross-reference container."""
18
+ return self.get("msDS-Behavior-Version")
@@ -19,3 +19,8 @@ class DomainDNS(Domain):
19
19
  if (pek := self.get("pekList")) is not None:
20
20
  return PEK(pek)
21
21
  return None
22
+
23
+ @property
24
+ def behavior_version(self) -> int | None:
25
+ """Return the msDS-Behavior-Version of this domain DNS object."""
26
+ return self.get("msDS-Behavior-Version")
@@ -24,6 +24,16 @@ class Group(Top):
24
24
  """Return the group's sAMAccountName."""
25
25
  return self.get("sAMAccountName")
26
26
 
27
+ @property
28
+ def mail(self) -> str | None:
29
+ """Return the mail address of this group."""
30
+ return self.get("mail")
31
+
32
+ @property
33
+ def admin_count(self) -> int | None:
34
+ """Return the group's adminCount."""
35
+ return self.get("adminCount")
36
+
27
37
  def managed_by(self) -> Iterator[Object]:
28
38
  """Return the objects that manage this group."""
29
39
  self._assert_local()
@@ -11,3 +11,8 @@ class GroupPolicyContainer(Container):
11
11
  """
12
12
 
13
13
  __object_class__ = "groupPolicyContainer"
14
+
15
+ @property
16
+ def file_path(self) -> str | None:
17
+ """Return the gPCFileSysPath of this group policy container."""
18
+ return self.get("gPCFileSysPath")
@@ -19,6 +19,11 @@ class NTDSDSA(ApplicationSettings):
19
19
 
20
20
  __object_class__ = "nTDSDSA"
21
21
 
22
+ @property
23
+ def behavior_version(self) -> int | None:
24
+ """Return the msDS-Behavior-Version of this NTDS DSA object."""
25
+ return self.get("msDS-Behavior-Version")
26
+
22
27
  def domain(self) -> DomainDNS | None:
23
28
  """Return the domain object associated with this NTDS DSA object, if any."""
24
29
  self._assert_local()
@@ -1,19 +1,23 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import struct
3
4
  from functools import cached_property
4
- from typing import TYPE_CHECKING, Any, ClassVar
5
+ from typing import TYPE_CHECKING, Any, ClassVar, TypeAlias
6
+ from uuid import UUID
5
7
 
6
- from dissect.database.ese.ntds.util import InstanceType, decode_value
8
+ from dissect.database.ese.ntds.util import InstanceType, SystemFlag, decode_value
7
9
 
8
10
  if TYPE_CHECKING:
9
- from collections.abc import Iterator
11
+ from collections.abc import Callable, Iterator
10
12
  from datetime import datetime
11
13
 
12
14
  from dissect.database.ese.ntds.database import Database
13
15
  from dissect.database.ese.ntds.sd import SecurityDescriptor
14
- from dissect.database.ese.ntds.util import DN, SystemFlags
16
+ from dissect.database.ese.ntds.util import DN
15
17
  from dissect.database.ese.record import Record
16
18
 
19
+ DecoderMap: TypeAlias = dict[str, Callable[[Database, Any], Any]]
20
+
17
21
 
18
22
  class Object:
19
23
  """Base class for all objects in the NTDS database.
@@ -29,7 +33,19 @@ class Object:
29
33
  - https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adsc/041c6068-c710-4c74-968f-3040e4208701
30
34
  """
31
35
 
36
+ # Subclasses must override this to specify their object class
32
37
  __object_class__: str
38
+ """The objectClass value for this object."""
39
+
40
+ # Decoders for specific attributes to this object
41
+ __decoders__: ClassVar[DecoderMap] = {
42
+ "Ancestors": lambda db, value: [v[0] for v in struct.iter_unpack("<I", value)],
43
+ "instanceType": lambda db, value: InstanceType(value),
44
+ "systemFlags": lambda db, value: SystemFlag(value),
45
+ "objectGUID": lambda db, value: UUID(bytes_le=value),
46
+ }
47
+
48
+ # All known object classes
33
49
  __known_classes__: ClassVar[dict[str, type[Object]]] = {}
34
50
 
35
51
  def __init__(self, db: Database, record: Record):
@@ -39,6 +55,12 @@ class Object:
39
55
  def __init_subclass__(cls):
40
56
  cls.__known_classes__[cls.__object_class__] = cls
41
57
 
58
+ # Merge parent decoders with any new ones defined on the new class
59
+ decoders = {}
60
+ for parent in reversed(cls.__mro__[1:]):
61
+ decoders |= getattr(parent, "__decoders__", {})
62
+ cls.__decoders__ = decoders | cls.__decoders__
63
+
42
64
  def __repr__(self) -> str:
43
65
  suffix = self.__repr_suffix__()
44
66
  return f"<{self.__class__.__name__} {self.__repr_body__()}{' ' + suffix if suffix else ''}>"
@@ -89,7 +111,13 @@ class Object:
89
111
  name: The attribute name to retrieve.
90
112
  raw: Whether to return the raw value without decoding.
91
113
  """
92
- return _get_attribute(self.db, self.record, name, raw=raw)
114
+ value = _get_attribute(self.db, self.record, name, raw=raw)
115
+
116
+ # Allow custom decoders to override the default decoding logic for specific attributes
117
+ # This is convenient for things like enums or timestamps that we want to represent as more meaningful types
118
+ if value is not None and name in self.__decoders__:
119
+ value = self.__decoders__[name](self.db, value)
120
+ return value
93
121
 
94
122
  def as_dict(self) -> dict[str, Any]:
95
123
  """Return the object's attributes as a dictionary."""
@@ -97,7 +125,7 @@ class Object:
97
125
  for key in self.record.as_dict():
98
126
  if (schema := self.db.data.schema.lookup_attribute(column=key)) is not None:
99
127
  key = schema.name
100
- result[key] = _get_attribute(self.db, self.record, key)
128
+ result[key] = self.get(key)
101
129
  return result
102
130
 
103
131
  def parent(self) -> Object | None:
@@ -221,7 +249,7 @@ class Object:
221
249
  return self.get("instanceType")
222
250
 
223
251
  @property
224
- def system_flags(self) -> SystemFlags | None:
252
+ def system_flags(self) -> SystemFlag | None:
225
253
  """Return the object's system flags."""
226
254
  return self.get("systemFlags")
227
255
 
@@ -240,9 +268,7 @@ class Object:
240
268
  @cached_property
241
269
  def sd(self) -> SecurityDescriptor | None:
242
270
  """Return the Security Descriptor for this object."""
243
- if (sd_id := self.get("nTSecurityDescriptor")) is not None:
244
- return self.db.sd.sd(sd_id)
245
- return None
271
+ return self.get("nTSecurityDescriptor")
246
272
 
247
273
  @cached_property
248
274
  def well_known_objects(self) -> list[Object]:
@@ -13,6 +13,21 @@ class OrganizationalPerson(Person):
13
13
  __object_class__ = "organizationalPerson"
14
14
 
15
15
  @property
16
- def city(self) -> str:
16
+ def city(self) -> str | None:
17
17
  """Return the city (l) of this organizational person."""
18
18
  return self.get("l") # "l" (localityName) represents the city/locality.
19
+
20
+ @property
21
+ def mail(self) -> str | None:
22
+ """Return the mail address of this organizational person."""
23
+ return self.get("mail")
24
+
25
+ @property
26
+ def title(self) -> str | None:
27
+ """Return the title of this organizational person."""
28
+ return self.get("title")
29
+
30
+ @property
31
+ def allowed_to_act_on_behalf_of_other_identity(self) -> str | None:
32
+ """Return the msDS-AllowedToActOnBehalfOfOtherIdentity attribute of this organizational person."""
33
+ return self.get("msDS-AllowedToActOnBehalfOfOtherIdentity")
@@ -1,13 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
3
+ from typing import TYPE_CHECKING, ClassVar
4
4
 
5
5
  from dissect.database.ese.ntds.objects.top import Top
6
+ from dissect.database.ese.ntds.util import GroupPolicyOption
6
7
 
7
8
  if TYPE_CHECKING:
8
9
  from collections.abc import Iterator
9
10
 
10
11
  from dissect.database.ese.ntds.objects import Object
12
+ from dissect.database.ese.ntds.objects.object import DecoderMap
11
13
 
12
14
 
13
15
  class OrganizationalUnit(Top):
@@ -18,6 +20,29 @@ class OrganizationalUnit(Top):
18
20
  """
19
21
 
20
22
  __object_class__ = "organizationalUnit"
23
+ __decoders__: ClassVar[DecoderMap] = {
24
+ "gPOptions": lambda db, value: GroupPolicyOption(value),
25
+ }
26
+
27
+ @property
28
+ def gp_link(self) -> str | None:
29
+ """Return the group policy link of the organizational unit."""
30
+ return self.get("gPLink")
31
+
32
+ @property
33
+ def gp_options(self) -> int | None:
34
+ """Return the group policy options of the organizational unit."""
35
+ return self.get("gPOptions")
36
+
37
+ @property
38
+ def telephone_number(self) -> str | None:
39
+ """Return the telephone number of this organizational unit."""
40
+ return self.get("telephoneNumber")
41
+
42
+ @property
43
+ def user_password(self) -> str | None:
44
+ """Return the userPassword of this organizational unit."""
45
+ return self.get("userPassword")
21
46
 
22
47
  def managed_by(self) -> Iterator[Object]:
23
48
  """Return the objects that manage this organizational unit."""
@@ -11,3 +11,13 @@ class Person(Top):
11
11
  """
12
12
 
13
13
  __object_class__ = "person"
14
+
15
+ @property
16
+ def telephone_number(self) -> str | None:
17
+ """Return the telephone number of this person."""
18
+ return self.get("telephoneNumber")
19
+
20
+ @property
21
+ def user_password(self) -> str | None:
22
+ """Return the userPassword attribute of this person."""
23
+ return self.get("userPassword")
@@ -11,3 +11,8 @@ class PKIEnrollmentService(Top):
11
11
  """
12
12
 
13
13
  __object_class__ = "pKIEnrollmentService"
14
+
15
+ @property
16
+ def dns_host_name(self) -> str | None:
17
+ """Return the dNSHostName of this PKI Enrollment Service."""
18
+ return self.get("dNSHostName")
@@ -19,6 +19,11 @@ class Server(Top):
19
19
 
20
20
  __object_class__ = "server"
21
21
 
22
+ @property
23
+ def dns_host_name(self) -> str | None:
24
+ """Return the dNSHostName of this server."""
25
+ return self.get("dNSHostName")
26
+
22
27
  def computer(self) -> Computer | None:
23
28
  """Return the computer object associated with this server, if any."""
24
29
  self._assert_local()
@@ -1,13 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
3
+ from typing import TYPE_CHECKING, ClassVar
4
4
 
5
5
  from dissect.database.ese.ntds.objects.top import Top
6
+ from dissect.database.ese.ntds.util import GroupPolicyOption
6
7
 
7
8
  if TYPE_CHECKING:
8
9
  from collections.abc import Iterator
9
10
 
10
11
  from dissect.database.ese.ntds.objects import Object
12
+ from dissect.database.ese.ntds.objects.object import DecoderMap
11
13
 
12
14
 
13
15
  class Site(Top):
@@ -18,6 +20,19 @@ class Site(Top):
18
20
  """
19
21
 
20
22
  __object_class__ = "site"
23
+ __decoders__: ClassVar[DecoderMap] = {
24
+ "gPOptions": lambda db, value: GroupPolicyOption(value),
25
+ }
26
+
27
+ @property
28
+ def gp_link(self) -> str:
29
+ """Return the group policy link of the site."""
30
+ return self.get("gPLink")
31
+
32
+ @property
33
+ def gp_options(self) -> int:
34
+ """Return the group policy options of the site."""
35
+ return self.get("gPOptions", 0)
21
36
 
22
37
  def managed_by(self) -> Iterator[Object]:
23
38
  """Return the objects that manage this site."""
@@ -19,3 +19,8 @@ class Top(Object):
19
19
  def display_name(self) -> str | None:
20
20
  """Return the displayName for this object."""
21
21
  return self.get("displayName")
22
+
23
+ @property
24
+ def description(self) -> str | None:
25
+ """Return the description for this object."""
26
+ return self.get("description")
@@ -1,6 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import TYPE_CHECKING, ClassVar
4
+
3
5
  from dissect.database.ese.ntds.objects.leaf import Leaf
6
+ from dissect.database.ese.ntds.util import TrustAttribute, TrustDirection, TrustType
7
+
8
+ if TYPE_CHECKING:
9
+ from dissect.database.ese.ntds.objects.object import DecoderMap
4
10
 
5
11
 
6
12
  class TrustedDomain(Leaf):
@@ -11,3 +17,33 @@ class TrustedDomain(Leaf):
11
17
  """
12
18
 
13
19
  __object_class__ = "trustedDomain"
20
+ __decoders__: ClassVar[DecoderMap] = {
21
+ "trustType": lambda db, value: TrustType(value),
22
+ "trustDirection": lambda db, value: TrustDirection(value),
23
+ "trustAttributes": lambda db, value: TrustAttribute(value),
24
+ }
25
+
26
+ @property
27
+ def trust_type(self) -> TrustType | None:
28
+ """Return the trustType of this trusted domain."""
29
+ return self.get("trustType")
30
+
31
+ @property
32
+ def trust_direction(self) -> TrustDirection | None:
33
+ """Return the trustDirection of this trusted domain."""
34
+ return self.get("trustDirection")
35
+
36
+ @property
37
+ def trust_attributes(self) -> TrustAttribute | None:
38
+ """Return the trustAttributes of this trusted domain."""
39
+ return self.get("trustAttributes")
40
+
41
+ @property
42
+ def trust_partner(self) -> str | None:
43
+ """Return the trustPartner of this trusted domain."""
44
+ return self.get("trustPartner")
45
+
46
+ @property
47
+ def security_identifier(self) -> str | None:
48
+ """Return the securityIdentifier of this trusted domain."""
49
+ return self.get("securityIdentifier")
@@ -1,16 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
3
+ from typing import TYPE_CHECKING, ClassVar
4
+
5
+ from dissect.util.ts import wintimestamp
4
6
 
5
7
  from dissect.database.ese.ntds.objects.organizationalperson import OrganizationalPerson
6
- from dissect.database.ese.ntds.util import UserAccountControl
8
+ from dissect.database.ese.ntds.util import SAMAccountType, UserAccountControl
7
9
 
8
10
  if TYPE_CHECKING:
9
11
  from collections.abc import Iterator
12
+ from datetime import datetime
10
13
 
11
14
  from dissect.database.ese.ntds.objects.group import Group
12
- from dissect.database.ese.ntds.objects.object import Object
13
- from dissect.database.ese.ntds.util import SAMAccountType
15
+ from dissect.database.ese.ntds.objects.object import DecoderMap, Object
14
16
 
15
17
 
16
18
  class User(OrganizationalPerson):
@@ -21,6 +23,16 @@ class User(OrganizationalPerson):
21
23
  """
22
24
 
23
25
  __object_class__ = "user"
26
+ __decoders__: ClassVar[DecoderMap] = {
27
+ "sAMAccountType": lambda db, value: SAMAccountType(value),
28
+ "userAccountControl": lambda db, value: UserAccountControl(value),
29
+ "badPasswordTime": lambda db, value: None if value == 0 else wintimestamp(value),
30
+ "lastLogonTimestamp": lambda db, value: None if value == 0 else wintimestamp(value),
31
+ "lastLogon": lambda db, value: None if value == 0 else wintimestamp(value),
32
+ "lastLogoff": lambda db, value: None if value == 0 else wintimestamp(value),
33
+ "pwdLastSet": lambda db, value: None if value == 0 else wintimestamp(value),
34
+ "accountExpires": lambda db, value: None if value in (0, ((1 << 63) - 1)) else wintimestamp(value),
35
+ }
24
36
 
25
37
  def __repr_body__(self) -> str:
26
38
  return f"name={self.name!r} sam_account_name={self.sam_account_name!r} is_machine_account={self.is_machine_account()}" # noqa: E501
@@ -45,6 +57,61 @@ class User(OrganizationalPerson):
45
57
  """Return the user's userAccountControl flags."""
46
58
  return self.get("userAccountControl")
47
59
 
60
+ @property
61
+ def user_principal_name(self) -> str | None:
62
+ """Return the user's userPrincipalName."""
63
+ return self.get("userPrincipalName")
64
+
65
+ @property
66
+ def service_principal_name(self) -> list[str]:
67
+ """Return the user's servicePrincipalName."""
68
+ return self.get("servicePrincipalName") or []
69
+
70
+ @property
71
+ def home_directory(self) -> str | None:
72
+ """Return the user's home directory."""
73
+ return self.get("homeDirectory")
74
+
75
+ @property
76
+ def home_drive(self) -> str | None:
77
+ """Return the user's home drive."""
78
+ return self.get("homeDrive")
79
+
80
+ @property
81
+ def script_path(self) -> str | None:
82
+ """Return the user's script path."""
83
+ return self.get("scriptPath")
84
+
85
+ @property
86
+ def password_last_set(self) -> datetime | None:
87
+ """Return the last time the user's password was set."""
88
+ return self.get("pwdLastSet")
89
+
90
+ @property
91
+ def logon_last_failed(self) -> datetime | None:
92
+ """Return the last time the user had a failed logon attempt."""
93
+ return self.get("badPasswordTime")
94
+
95
+ @property
96
+ def logon_last_local(self) -> datetime | None:
97
+ """Return the last time the user had a successful local logon."""
98
+ return self.get("lastLogon")
99
+
100
+ @property
101
+ def logon_last_replicated(self) -> datetime | None:
102
+ """Return the last time the user had a successful logon replicated across domain controllers."""
103
+ return self.get("lastLogonTimestamp")
104
+
105
+ @property
106
+ def account_expires(self) -> datetime | None:
107
+ """Return the time the user's account expires."""
108
+ return self.get("accountExpires")
109
+
110
+ @property
111
+ def admin_count(self) -> int | None:
112
+ """Return the user's adminCount."""
113
+ return self.get("adminCount")
114
+
48
115
  def is_machine_account(self) -> bool:
49
116
  """Return whether this user is a machine account."""
50
117
  return UserAccountControl.WORKSTATION_TRUST_ACCOUNT in self.user_account_control
@@ -7,7 +7,7 @@ from dissect.database.ese.ntds.c_ds import c_ds
7
7
 
8
8
  if TYPE_CHECKING:
9
9
  from dissect.database.ese.ntds.database import Database
10
- from dissect.database.ese.ntds.util import SearchFlags
10
+ from dissect.database.ese.ntds.util import SearchFlag
11
11
 
12
12
  # These are fixed columns in the NTDS database
13
13
  # They do not exist in the schema, but are required for basic operation
@@ -121,7 +121,7 @@ class AttributeEntry(NamedTuple):
121
121
  om_object_class: bytes | None
122
122
  is_single_valued: bool
123
123
  link_id: int | None
124
- search_flags: SearchFlags | None
124
+ search_flags: SearchFlag | None
125
125
 
126
126
 
127
127
  class Schema:
@@ -255,7 +255,7 @@ class Schema:
255
255
  om_object_class: bytes | None,
256
256
  is_single_valued: bool,
257
257
  link_id: int | None,
258
- search_flags: SearchFlags | None,
258
+ search_flags: SearchFlag | None,
259
259
  ) -> None:
260
260
  entry = AttributeEntry(
261
261
  dnt=dnt,
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import struct
4
4
  from enum import Flag, IntEnum, IntFlag, auto
5
5
  from typing import TYPE_CHECKING, Any
6
- from uuid import UUID
7
6
 
8
7
  from dissect.util.sid import read_sid, write_sid
9
8
  from dissect.util.ts import wintimestamp
@@ -18,7 +17,7 @@ if TYPE_CHECKING:
18
17
  from dissect.database.ese.ntds.schema import AttributeEntry
19
18
 
20
19
 
21
- class DatabaseFlags(Flag):
20
+ class DatabaseFlag(Flag):
22
21
  """Database flags that are stored in the hiddentable.
23
22
 
24
23
  The flags are weirdly stored as ``1``, ``0`` or ``\x00`` in a byte array.
@@ -45,19 +44,71 @@ class InstanceType(IntFlag):
45
44
  NamingContextDeleting = 0x00000020
46
45
 
47
46
 
48
- # https://learn.microsoft.com/en-us/windows/win32/adschema/a-useraccountcontrol
49
- class SystemFlags(IntFlag):
50
- NotReplicated = 0x00000001
51
- ReplicatedToGlobalCatalog = 0x00000002
52
- Constructed = 0x00000004
53
- BaseSchema = 0x00000010
54
- DeletedImmediately = 0x02000000
55
- CannotBeMoved = 0x04000000
56
- CannotBeRenamed = 0x08000000
57
- ConfigurationCanBeMovedWithRestrictions = 0x10000000
58
- ConfigurationCanBeMoved = 0x20000000
59
- ConfigurationCanBeRenamedWithRestrictions = 0x40000000
60
- CannotBeDeleted = 0x80000000
47
+ # https://learn.microsoft.com/en-us/windows/win32/adschema/a-systemflags
48
+ # https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/1e38247d-8234-4273-9de3-bbf313548631
49
+ class SystemFlag(IntFlag):
50
+ # The first 3 flags have an overlap whether it's set on an attributeSchema or crossRef object
51
+ # We don't specify them here, see SystemFlagAttribute and SystemFlagCrossRef
52
+
53
+ # The following flags are also specific to certain objects, but have no overlap
54
+ ATTR_IS_OPERATIONAL = 0x00000008
55
+ SCHEMA_BASE_OBJECT = 0x00000010
56
+ ATTR_IS_RDN = 0x00000020
57
+ DISALLOW_MOVE_ON_DELETE = 0x02000000
58
+ DOMAIN_DISALLOW_MOVE = 0x04000000
59
+ DOMAIN_DISALLOW_RENAME = 0x08000000
60
+ CONFIG_ALLOW_LIMITED_MOVE = 0x10000000
61
+ CONFIG_ALLOW_MOVE = 0x20000000
62
+ CONFIG_ALLOW_RENAME = 0x40000000
63
+ DISALLOW_DELETE = 0x80000000
64
+
65
+
66
+ # System flags that overlap with other flags and are specific to attributeSchema objects
67
+ class SystemFlagAttribute(IntFlag):
68
+ ATTR_NOT_REPLICATED = 0x00000001
69
+ ATTR_REQ_PARTIAL_SET_MEMBER = 0x00000002
70
+ ATTR_IS_CONSTRUCTED = 0x00000004
71
+
72
+ # TODO: When we drop Python 3.10 support, we can subclass SystemFlag
73
+ # For now, just duplicate the flags here
74
+ ATTR_IS_OPERATIONAL = 0x00000008
75
+ SCHEMA_BASE_OBJECT = 0x00000010
76
+ ATTR_IS_RDN = 0x00000020
77
+ DISALLOW_MOVE_ON_DELETE = 0x02000000
78
+ DOMAIN_DISALLOW_MOVE = 0x04000000
79
+ DOMAIN_DISALLOW_RENAME = 0x08000000
80
+ CONFIG_ALLOW_LIMITED_MOVE = 0x10000000
81
+ CONFIG_ALLOW_MOVE = 0x20000000
82
+ CONFIG_ALLOW_RENAME = 0x40000000
83
+ DISALLOW_DELETE = 0x80000000
84
+
85
+
86
+ # For better readability when printing attributeSchema objects, we reset the name
87
+ SystemFlagAttribute.__name__ = "SystemFlag"
88
+
89
+
90
+ # System flags that overlap with other flags and are specific to crossRef objects
91
+ class SystemFlagCrossRef(IntFlag):
92
+ CR_NTDS_NC = 0x00000001
93
+ CR_NTDS_DOMAIN = 0x00000002
94
+ CR_NTDS_NOT_GC_REPLICATED = 0x00000004
95
+
96
+ # TODO: When we drop Python 3.10 support, we can subclass SystemFlag
97
+ # For now, just duplicate the flags here
98
+ ATTR_IS_OPERATIONAL = 0x00000008
99
+ SCHEMA_BASE_OBJECT = 0x00000010
100
+ ATTR_IS_RDN = 0x00000020
101
+ DISALLOW_MOVE_ON_DELETE = 0x02000000
102
+ DOMAIN_DISALLOW_MOVE = 0x04000000
103
+ DOMAIN_DISALLOW_RENAME = 0x08000000
104
+ CONFIG_ALLOW_LIMITED_MOVE = 0x10000000
105
+ CONFIG_ALLOW_MOVE = 0x20000000
106
+ CONFIG_ALLOW_RENAME = 0x40000000
107
+ DISALLOW_DELETE = 0x80000000
108
+
109
+
110
+ # For better readability when printing crossRef objects, we reset the name
111
+ SystemFlagCrossRef.__name__ = "SystemFlag"
61
112
 
62
113
 
63
114
  # https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/dd302fd1-0aa7-406b-ad91-2a6b35738557
@@ -98,7 +149,7 @@ class SAMAccountType(IntEnum):
98
149
  SAM_APP_QUERY_GROUP = 0x40000001
99
150
 
100
151
 
101
- class SearchFlags(IntFlag):
152
+ class SearchFlag(IntFlag):
102
153
  Indexed = 0x00000001
103
154
  ContainerIndexed = 0x00000002
104
155
  Anr = 0x00000004
@@ -109,6 +160,41 @@ class SearchFlags(IntFlag):
109
160
  Confidential = 0x00000080
110
161
 
111
162
 
163
+ class TrustType(IntEnum):
164
+ DOWNLEVEL = 0x00000001
165
+ UPLEVEL = 0x00000002
166
+ MIT = 0x00000003
167
+ DCE = 0x00000004
168
+ AAD = 0x00000005
169
+
170
+
171
+ class TrustDirection(IntEnum):
172
+ DISABLED = 0
173
+ INBOUND = 1
174
+ OUTBOUND = 2
175
+ BIDIRECTIONAL = 3
176
+
177
+
178
+ class TrustAttribute(IntFlag):
179
+ NON_TRANSITIVE = 0x00000001
180
+ UPLEVEL_ONLY = 0x00000002
181
+ FILTER_SIDS = 0x00000004
182
+ FOREST_TRANSITIVE = 0x00000008
183
+ CROSS_ORGANIZATION = 0x00000010
184
+ WITHIN_FOREST = 0x00000020
185
+ TREAT_AS_EXTERNAL = 0x00000040
186
+ TRUST_USES_RC4_ENCRYPTION = 0x00000080
187
+ TRUST_USES_AES_KEYS = 0x00000100
188
+ CROSS_ORGANIZATION_NO_TGT_DELEGATION = 0x00000200
189
+ PIM_TRUST = 0x00000400
190
+ TREE_PARENT = 0x00400000
191
+ TREE_ROOT = 0x00800000
192
+
193
+
194
+ class GroupPolicyOption(IntFlag):
195
+ BLOCK_POLICY = 0x00000001
196
+
197
+
112
198
  def _pek_decrypt(db: Database, value: bytes) -> bytes:
113
199
  """Decrypt a PEK-encrypted blob using the database's PEK, if it's unlocked.
114
200
 
@@ -255,22 +341,6 @@ def _decode_pwd_history(db: Database, value: list[bytes]) -> list[bytes]:
255
341
  ATTRIBUTE_ENCODE_DECODE_MAP: dict[
256
342
  str, tuple[Callable[[Database, Any], Any] | None, Callable[[Database, Any], Any] | None]
257
343
  ] = {
258
- "Ancestors": (None, lambda db, value: [v[0] for v in struct.iter_unpack("<I", value)]),
259
- "instanceType": (lambda db, value: int(value), lambda db, value: InstanceType(int(value))),
260
- "systemFlags": (lambda db, value: int(value), lambda db, value: SystemFlags(int(value))),
261
- "searchFlags": (lambda db, value: int(value), lambda db, value: SearchFlags(int(value))),
262
- "sAMAccountType": (lambda db, value: int(value), lambda db, value: SAMAccountType(int(value))),
263
- "userAccountControl": (lambda db, value: int(value), lambda db, value: UserAccountControl(int(value))),
264
- "objectGUID": (lambda db, value: value.bytes_le, lambda db, value: UUID(bytes_le=value)),
265
- "badPasswordTime": (None, lambda db, value: wintimestamp(int(value))),
266
- "lastLogonTimestamp": (None, lambda db, value: wintimestamp(int(value))),
267
- "lastLogon": (None, lambda db, value: wintimestamp(int(value))),
268
- "lastLogoff": (None, lambda db, value: wintimestamp(int(value))),
269
- "pwdLastSet": (None, lambda db, value: wintimestamp(int(value))),
270
- "accountExpires": (
271
- None,
272
- lambda db, value: float("inf") if int(value) == ((1 << 63) - 1) else wintimestamp(int(value)),
273
- ),
274
344
  # Protected attributes
275
345
  "unicodePwd": (None, _pek_decrypt),
276
346
  "dBCSPwd": (None, _pek_decrypt),
@@ -432,7 +502,7 @@ SYNTAX_ENCODE_DECODE_MAP: dict[
432
502
  # TODO: Object(DN-String); A DN-String plus a Unicode string
433
503
  14: (None, None),
434
504
  # NTSecurityDescriptor; A security descriptor
435
- 15: (None, lambda db, value: int.from_bytes(value, byteorder="little")),
505
+ 15: (None, lambda db, value: db.sd.sd(int.from_bytes(value, byteorder="little"))),
436
506
  # LargeInteger; A 64-bit number
437
507
  16: (None, lambda db, value: int(value)),
438
508
  # String(Sid); Security identifier (SID)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.database
3
- Version: 1.2.dev8
3
+ Version: 1.2.dev9
4
4
  Summary: A Dissect module implementing parsers for various database formats, including Berkeley DB, Microsofts Extensible Storage Engine (ESE) and SQLite3
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License-Expression: Apache-2.0
@@ -29,40 +29,40 @@ dissect/database/ese/ntds/c_pek.py,sha256=YU_rRpOEf8uB8OE6bGb25KIGm9e-yrn0tTItRC
29
29
  dissect/database/ese/ntds/c_pek.pyi,sha256=lHfhvHWABT1vLmknu66phOXsylgwzGIWVdbqDIZU5Qw,4683
30
30
  dissect/database/ese/ntds/c_sd.py,sha256=DYICZsWCBzj0OtvhO6vhzzVjK9YP6tzBCsgKgr0pc-k,4294
31
31
  dissect/database/ese/ntds/c_sd.pyi,sha256=7717Y0EBVu37Liu26rqsDWkLIdSCqWn9KK9svtniLqY,5279
32
- dissect/database/ese/ntds/database.py,sha256=6T9JGxB0BD9JF9LP4DjWfozm7mPb5eiXP56Q6bGqKnk,16783
33
- dissect/database/ese/ntds/ntds.py,sha256=ffE_yOvmwDUXwuQSjCDcGagKXx3nUajAkxDr1HpJbHQ,5060
32
+ dissect/database/ese/ntds/database.py,sha256=fPMgTzHm1rsPZ3Hvt7SQqtjdXQNNLa51wGEK3i5_afw,16776
33
+ dissect/database/ese/ntds/ntds.py,sha256=Uws58HSUKUUIs3qEc3zlrQfwNK-cKwyzkAt3320aHs8,5506
34
34
  dissect/database/ese/ntds/pek.py,sha256=BEmxO175T8QkGVvFQLN9MI9uDCcK4jztuZetbwbbYqU,4154
35
35
  dissect/database/ese/ntds/query.py,sha256=pDLLCVdrCRNq3ripvMDiyxXoFWsVJLwwsRMplRf7C9s,8063
36
- dissect/database/ese/ntds/schema.py,sha256=xxVL--RRBM8-IP12L86hrz-EEGLqXKAXpQZyOT_Vhn0,16644
36
+ dissect/database/ese/ntds/schema.py,sha256=qAC9STfmJqUMp7633ZcOZOQgWI-UymwX3h4PUUjUV1U,16641
37
37
  dissect/database/ese/ntds/sd.py,sha256=Y-oYnJPcLMDB_4X8TLEGtt-n_nC4HLA0WqIS8qYAwAs,5995
38
- dissect/database/ese/ntds/util.py,sha256=nuY7NRSlr5V5Jz2NIOEhe3hbzPjFLVQnprXa3swSl_M,18856
38
+ dissect/database/ese/ntds/util.py,sha256=3aIvxX3B2-LXQgmHA6rrFiN0obIZHdfD1-wKfecqaI8,20451
39
39
  dissect/database/ese/ntds/objects/__init__.py,sha256=LpZ9nHGxmuhVPQkD-qwh-OwFM1T4OzjcklAcEF2Zmrs,10868
40
40
  dissect/database/ese/ntds/objects/applicationsettings.py,sha256=j7UzmF8yxm3LR2lLnmGb7vFvUhYokCUOdhJXnk9xxzE,358
41
- dissect/database/ese/ntds/objects/attributeschema.py,sha256=0e_s5N5mnybxuufOEjGIVWlyuJkSgplz9LuIsaS2S38,342
41
+ dissect/database/ese/ntds/objects/attributeschema.py,sha256=E8JioZAc_OgnK60nbrxHFxgFBvR1HKAz0tCdj4xt6DI,892
42
42
  dissect/database/ese/ntds/objects/builtindomain.py,sha256=KEo-YQl-r748Hnkod21qej2qsLGj7jM4vt6nbufMPWQ,334
43
- dissect/database/ese/ntds/objects/certificationauthority.py,sha256=oqmPYlz5SYUv7RfTNCKZ3F233v3K7lBtxDDz_g_R1GU,365
43
+ dissect/database/ese/ntds/objects/certificationauthority.py,sha256=XWGzSImJ7Qo1TEMTUznAslp-810lWe8Xpk-Cjo99nIc,532
44
44
  dissect/database/ese/ntds/objects/classschema.py,sha256=Hrpfgl_mR6ciHL7qya63mvzsrouFIy4BJVHU5wJm_Hg,1544
45
45
  dissect/database/ese/ntds/objects/classstore.py,sha256=VqWGm4ZqonQQGnFE67AyGGUYuTLdBmPQzZ34X-fRG5M,321
46
- dissect/database/ese/ntds/objects/computer.py,sha256=QstBJ59mxsepGBLW_EJBwOe4gCGHDz5ZJoSknD6TQlY,1136
47
- dissect/database/ese/ntds/objects/configuration.py,sha256=AMjJBr4yCDITulk3LhVMhZBEOUYf6ApzibwxiLO79sU,332
46
+ dissect/database/ese/ntds/objects/computer.py,sha256=F6twYcQNvfiY1Ba5BmYwhgMEwpqU53wKTPxBaQ0BnO0,1636
47
+ dissect/database/ese/ntds/objects/configuration.py,sha256=jPef8omT1QinAQ51ST_YaBcGGEzOzn-pZ9-Bg5uYMUY,483
48
48
  dissect/database/ese/ntds/objects/container.py,sha256=fYtFfWHpcr_4wMN0_vfN9pRx2UXt26Fw31c4FOH9uEQ,316
49
49
  dissect/database/ese/ntds/objects/controlaccessright.py,sha256=Xh0hYp7rOQ2oZEA2hOALY_uGZNAxkiuhzhrgK1pLM2U,354
50
50
  dissect/database/ese/ntds/objects/crldistributionpoint.py,sha256=f9G1uDI3Pq9hQ6ldfvv6eEuAWpFrnwxgbKTELYlQlrY,358
51
- dissect/database/ese/ntds/objects/crossref.py,sha256=-i0jHqp-0qbntW3-3r-IazgnqVj6fSytY6AziNYNXzE,319
52
- dissect/database/ese/ntds/objects/crossrefcontainer.py,sha256=hUQMyrt4bsq6nNwIwKfyJPBtZctt3imYNKQdCPp9oWw,356
51
+ dissect/database/ese/ntds/objects/crossref.py,sha256=G6WGZsXwny0nX77bJ5rMf5LiE2c9p7ENxjPofxfhCAg,811
52
+ dissect/database/ese/ntds/objects/crossrefcontainer.py,sha256=YeEnVCVYpt6p1jQ8grctPz3r9DWZj55DwDWc-zJubmo,548
53
53
  dissect/database/ese/ntds/objects/dfsconfiguration.py,sha256=Ofgj8Nnfw4_kGTjGnqaSDB6opshqQMNAJFFk_AuTbbo,345
54
54
  dissect/database/ese/ntds/objects/displayspecifier.py,sha256=Kuc7usI-zLK0TadGAm_fnuu6DurHNYZD6-hQMlQ3F6g,345
55
55
  dissect/database/ese/ntds/objects/dmd.py,sha256=SnJLgFO5rMmsVjgYN0z0klE5tQkZxnmAaqsWefitV0U,324
56
56
  dissect/database/ese/ntds/objects/dnsnode.py,sha256=os48qV-nvqa80PiyDLXVSST31NI_KfFC32jlkm30FzY,309
57
57
  dissect/database/ese/ntds/objects/dnszone.py,sha256=rJ_3zdsxrR3FvzXU1qbbGqnh67FR64CRClO3dF7wBXU,659
58
58
  dissect/database/ese/ntds/objects/domain.py,sha256=aWQN-5P1MMh2aPXJoyPORxhyE_5dIt7aIfBB7Bxp8mM,304
59
- dissect/database/ese/ntds/objects/domaindns.py,sha256=dnGFmIQAOZib-lGwSvNHanWMuLcN0meox1mI4BcCW64,596
59
+ dissect/database/ese/ntds/objects/domaindns.py,sha256=bHJQukHnhmBJwgGMTiwWHyF5PXQYhv6sdYrJP2QH2CY,780
60
60
  dissect/database/ese/ntds/objects/domainpolicy.py,sha256=SjvVx1Tx0_ckkf4_rIGlA5YMBHQ3POoMn5vQJl2yzwA,687
61
61
  dissect/database/ese/ntds/objects/dsuisettings.py,sha256=hklM6JRYVQ-28nL_yv8S53yOOLc-ph93eEwJr2rM5CA,330
62
62
  dissect/database/ese/ntds/objects/filelinktracking.py,sha256=UmsPb_pKSjCIy6K7pbcdNlZVLhF3FaehbbqYM8SpTRY,346
63
63
  dissect/database/ese/ntds/objects/foreignsecurityprincipal.py,sha256=w87K7yXSwGBrka6IFntVPtOhihyN5Dy5pM_ZNp8qYAM,378
64
- dissect/database/ese/ntds/objects/group.py,sha256=XWtzDy51nZGv234mfonJt4h7sBZqfG2NR4avlxueThw,1389
65
- dissect/database/ese/ntds/objects/grouppolicycontainer.py,sha256=F55DGq5XZ3rTRjkvX1tUfSGNMz6AKOIQPhHGh4BSDpw,380
64
+ dissect/database/ese/ntds/objects/group.py,sha256=ONPHhZo27rFOxhRvVAFEQ9LFSyAGVkzQ63s-67Uq0hQ,1662
65
+ dissect/database/ese/ntds/objects/grouppolicycontainer.py,sha256=dglJ-M85cGV2ltPL6eYD6FhYYQ-ZX2cg9Lo5-Fu9Lpg,548
66
66
  dissect/database/ese/ntds/objects/infrastructureupdate.py,sha256=g5qvuqaFJRMUTfREd9B2NkQoF_Oavg8XsMn3_WPYRtQ,362
67
67
  dissect/database/ese/ntds/objects/intersitetransport.py,sha256=1-4Hh1jFFM4EyRQigHBiPnCfWRON_irZp4LTKowVVpQ,355
68
68
  dissect/database/ese/ntds/objects/intersitetransportcontainer.py,sha256=UuVsYRk4PHtE8SGXNnmo0x5NYWhTUtWDI7bawVFZYpI,386
@@ -112,17 +112,17 @@ dissect/database/ese/ntds/objects/mspki_privatekeyrecoveryagent.py,sha256=BA7NKZ
112
112
  dissect/database/ese/ntds/objects/msspp_activationobjectscontainer.py,sha256=l_OpFUHcRZ7LYvXAdfrDXSClX9f78Ca5UJPgNszo3ZE,405
113
113
  dissect/database/ese/ntds/objects/mstpm_informationobjectscontainer.py,sha256=84wqeKMwcShtr6uzgHoG6rMpk_3r22xe6BTvpt24pSg,404
114
114
  dissect/database/ese/ntds/objects/ntdsconnection.py,sha256=mWgzUTQ0o6Gsc6CsyBoakyXV5UejzTlsVp8j3vm10bw,341
115
- dissect/database/ese/ntds/objects/ntdsdsa.py,sha256=pES4m9UCqxDI6sOQUjyp3Uc0alHN3pNGpr84L46L5ys,1226
115
+ dissect/database/ese/ntds/objects/ntdsdsa.py,sha256=g9YtoARzN6IWk0ai0dcwWYQf1B4afjc6OV2ec1twTe8,1408
116
116
  dissect/database/ese/ntds/objects/ntdsservice.py,sha256=mTdpd2tzm8OOiGm6rJN35_dyeWtvzPYI_rVDBZz0gNc,326
117
117
  dissect/database/ese/ntds/objects/ntdssitesettings.py,sha256=VNlke-Vgxjh0fzd5o2X-4fwCBrkEI1coR4oto5OenPo,709
118
118
  dissect/database/ese/ntds/objects/ntfrssettings.py,sha256=PwIKT6t_zRxfdacWuvzMfq_mIhj_wyIhHzORNcF2eOU,745
119
- dissect/database/ese/ntds/objects/object.py,sha256=gYxjaivNCr0WfjrAAIqS1SmSR2zF2_ROR0g2Cip-3gg,10162
120
- dissect/database/ese/ntds/objects/organizationalperson.py,sha256=X4QHT9AeGU7mDtIPlZbkhs1v2ZCPEarehFyn5EFszdY,559
121
- dissect/database/ese/ntds/objects/organizationalunit.py,sha256=H_VHrUgN92cXgGoV3ngiV4HjSaDndSUkQSjcNmb7WFs,715
122
- dissect/database/ese/ntds/objects/person.py,sha256=s67VHtsyN5t8icU9adPTLGcrIMH14yrEY1VqUqSw3Do,304
119
+ dissect/database/ese/ntds/objects/object.py,sha256=cEBfgi6vLplql2fZbDeRJHDcHVHk9pLIxhMX90SfGLY,11320
120
+ dissect/database/ese/ntds/objects/organizationalperson.py,sha256=KUfpx76IObfdf8feLTVjJj3tAONS7np3st9xVPtPmCQ,1123
121
+ dissect/database/ese/ntds/objects/organizationalunit.py,sha256=3PrtKri5Unmcg_poLWVFDmF2m0BdiibKgSwq4c6kG-w,1631
122
+ dissect/database/ese/ntds/objects/person.py,sha256=pDnOHNFWD3phbipS0gCQLrFzXpqroI8O_5pUuAQk8Ko,628
123
123
  dissect/database/ese/ntds/objects/physicallocation.py,sha256=Wp-lGM8TVEaPa0syzHe07vHRL98nXwP_iCtlHv0650s,719
124
124
  dissect/database/ese/ntds/objects/pkicertificatetemplate.py,sha256=zLNujYL62arZy0w9UzVUgMr8htPSygeygG5ZDROS0F0,370
125
- dissect/database/ese/ntds/objects/pkienrollmentservice.py,sha256=Ii9XcEpHKqgrk10g5kb4gComemjc0KmCeXm237IlTFM,358
125
+ dissect/database/ese/ntds/objects/pkienrollmentservice.py,sha256=jaS-OmAOu-fpg8wXuq_lcacACeY2gUA_54-PzIIZKCU,524
126
126
  dissect/database/ese/ntds/objects/querypolicy.py,sha256=W6O1qevlVEn6Y1yRi1Ki8FErrNCE5leYE4J_y_eNhrc,325
127
127
  dissect/database/ese/ntds/objects/ridmanager.py,sha256=magfnp-sk3Nb6BapzUbcphM-UVxOMBAVQgiaFGYCJpU,319
128
128
  dissect/database/ese/ntds/objects/ridset.py,sha256=xpmfNsqKwSIL4D9bNvaOokbz-95bcxhbeFxgRtf-muY,303
@@ -131,16 +131,16 @@ dissect/database/ese/ntds/objects/rrasadministrationdictionary.py,sha256=dzS3xag
131
131
  dissect/database/ese/ntds/objects/samserver.py,sha256=q32pI-Pjd1TOQle_LuR53rjZlDjCY2U2aAYCWjLEaDg,348
132
132
  dissect/database/ese/ntds/objects/secret.py,sha256=0278o0wpy3n5JmhYKFpNDy1MgNcHBbjX0aUYXxeCUlQ,3392
133
133
  dissect/database/ese/ntds/objects/securityobject.py,sha256=hR0a0wjyrLFtSMtRZOwPCYXpfuOCtNMhozK4bkzly-A,330
134
- dissect/database/ese/ntds/objects/server.py,sha256=9swxb2s6vNTAPsL_mjQBjZNGft-pUvA2-zds1gKfYJ8,889
134
+ dissect/database/ese/ntds/objects/server.py,sha256=k1iqvIUHttxXz3aQLJPJkN2FH-pAz4SE3Z2tdzJS2-U,1039
135
135
  dissect/database/ese/ntds/objects/serverscontainer.py,sha256=NnJ0M23RO-hsKd2vWcvViOMRV8psGJkwiaRC-ghmrk0,345
136
- dissect/database/ese/ntds/objects/site.py,sha256=Yj5lLgivDumOaS8QaTdsS0XZfQEkYr7loN9aUwxj_Y4,640
136
+ dissect/database/ese/ntds/objects/site.py,sha256=Ozc_TNvXKhIbHJShLpBuHMWkS82IpxSoYErziJ9B9dE,1175
137
137
  dissect/database/ese/ntds/objects/sitelink.py,sha256=cNHis9jsxFPtxfiCfxd4klP495XfjcwrF507wduyADk,313
138
138
  dissect/database/ese/ntds/objects/sitescontainer.py,sha256=C09THsbE-tBz2fFsYj5UkdbNuzIBqTo65F71a0oEY3w,337
139
139
  dissect/database/ese/ntds/objects/subnetcontainer.py,sha256=IEwSBFxoPXBIKuH-V7rK5yueAEDaMIuVXbtTEmGUlCg,341
140
140
  dissect/database/ese/ntds/objects/subschema.py,sha256=-vE76P_Yhj-Ny6ZAYnTTPMS0dtLNFZRUgyh9IXIm5Po,317
141
- dissect/database/ese/ntds/objects/top.py,sha256=1oPtivWGki1lfElWMs57zZ05g6dbWSa77A77ytf7hWs,527
142
- dissect/database/ese/ntds/objects/trusteddomain.py,sha256=J-besYk7tF3vxbJC3PogDk3iduBp_86A2SHA0LJQFb8,336
143
- dissect/database/ese/ntds/objects/user.py,sha256=wuCViCevxboNXdkojQuKzaqWYnEz7k7Y017LisdAFOQ,2615
141
+ dissect/database/ese/ntds/objects/top.py,sha256=jXH9u3g3EhPAA4Tiw9g201BaZPJ1-4FeLOnKL5ma3Vg,676
142
+ dissect/database/ese/ntds/objects/trusteddomain.py,sha256=PhioFfl5gkGFQIS0_D70rIH0x0g3Hp9ZhaNMxCh6afs,1645
143
+ dissect/database/ese/ntds/objects/user.py,sha256=mMZ9CE6YL5sNm6KxAJWk50lxdB-P-pYeIKvEwsWSSL4,5213
144
144
  dissect/database/ese/ntds/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
145
145
  dissect/database/ese/ntds/tools/ntds.py,sha256=6hgpoSr-QKgbz8ABkYsM-MZf6kkH_nlJbz_BCqbrGzE,861
146
146
  dissect/database/ese/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -159,10 +159,10 @@ dissect/database/sqlite3/encryption/__init__.py,sha256=kJdFWXD9Z_O_QipC-_A9dlVfR
159
159
  dissect/database/sqlite3/encryption/sqlcipher/__init__.py,sha256=kJdFWXD9Z_O_QipC-_A9dlVfR6AOPSOoT8WBhpFbSsE,238
160
160
  dissect/database/sqlite3/encryption/sqlcipher/exception.py,sha256=GKNtzcnAKlWkvjLluruA8LfzCwjRRWubibbH8WM9l2o,121
161
161
  dissect/database/sqlite3/encryption/sqlcipher/sqlcipher.py,sha256=y_oJRKZqoJBeOQaBbniesZKm1sVTFvXZ466rJYZj2xE,11217
162
- dissect_database-1.2.dev8.dist-info/licenses/COPYRIGHT,sha256=pFH-OBYz6Xj23UB0Odz5IhoTR8nsTbJQNlCRV_wMaiE,317
163
- dissect_database-1.2.dev8.dist-info/licenses/LICENSE,sha256=PhUqiw6jAh2KbBdVRPBq_hfAvfcTBin7nZ3CK7NQbTM,11341
164
- dissect_database-1.2.dev8.dist-info/METADATA,sha256=hGEpclu4s8U1QSHYtlzFE6-use8kBW1cwjqvDDjnDkY,5540
165
- dissect_database-1.2.dev8.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
166
- dissect_database-1.2.dev8.dist-info/entry_points.txt,sha256=ZVVKj3Nzjkgm1kBXGWyGNVUJzTbmVgivv9lgFcuLkpk,343
167
- dissect_database-1.2.dev8.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
168
- dissect_database-1.2.dev8.dist-info/RECORD,,
162
+ dissect_database-1.2.dev9.dist-info/licenses/COPYRIGHT,sha256=pFH-OBYz6Xj23UB0Odz5IhoTR8nsTbJQNlCRV_wMaiE,317
163
+ dissect_database-1.2.dev9.dist-info/licenses/LICENSE,sha256=PhUqiw6jAh2KbBdVRPBq_hfAvfcTBin7nZ3CK7NQbTM,11341
164
+ dissect_database-1.2.dev9.dist-info/METADATA,sha256=lpFACmhxqvnFHoHk3_cI8ydgt--hK4JpVhbHVksYfVU,5540
165
+ dissect_database-1.2.dev9.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
166
+ dissect_database-1.2.dev9.dist-info/entry_points.txt,sha256=ZVVKj3Nzjkgm1kBXGWyGNVUJzTbmVgivv9lgFcuLkpk,343
167
+ dissect_database-1.2.dev9.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
168
+ dissect_database-1.2.dev9.dist-info/RECORD,,