dissect.target 3.20.dev47__py3-none-any.whl → 3.20.dev48__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,17 @@
1
- import gzip
1
+ from __future__ import annotations
2
+
2
3
  import ipaddress
3
4
  import struct
4
5
  from collections import namedtuple
5
6
  from typing import Iterator
6
7
 
7
8
  from dissect.cstruct import cstruct
8
- from dissect.util.stream import BufferedStream
9
9
  from dissect.util.ts import from_unix
10
10
 
11
11
  from dissect.target.exceptions import UnsupportedPluginError
12
- from dissect.target.helpers.fsutil import TargetPath
12
+ from dissect.target.helpers.fsutil import TargetPath, open_decompress
13
13
  from dissect.target.helpers.record import TargetRecordDescriptor
14
- from dissect.target.plugin import OperatingSystem, Plugin, export
14
+ from dissect.target.plugin import Plugin, alias, export
15
15
  from dissect.target.target import Target
16
16
 
17
17
  UTMP_FIELDS = [
@@ -27,16 +27,12 @@ UTMP_FIELDS = [
27
27
 
28
28
  BtmpRecord = TargetRecordDescriptor(
29
29
  "linux/log/btmp",
30
- [
31
- *UTMP_FIELDS,
32
- ],
30
+ UTMP_FIELDS,
33
31
  )
34
32
 
35
33
  WtmpRecord = TargetRecordDescriptor(
36
34
  "linux/log/wtmp",
37
- [
38
- *UTMP_FIELDS,
39
- ],
35
+ UTMP_FIELDS,
40
36
  )
41
37
 
42
38
  utmp_def = """
@@ -104,24 +100,13 @@ UTMP_ENTRY = namedtuple(
104
100
  class UtmpFile:
105
101
  """utmp maintains a full accounting of the current status of the system"""
106
102
 
107
- def __init__(self, target: Target, path: TargetPath):
108
- self.fh = target.fs.path(path).open()
109
-
110
- if "gz" in path:
111
- self.compressed = True
112
- else:
113
- self.compressed = False
103
+ def __init__(self, path: TargetPath):
104
+ self.fh = open_decompress(path, "rb")
114
105
 
115
106
  def __iter__(self):
116
- if self.compressed:
117
- gzip_entry = BufferedStream(gzip.open(self.fh, mode="rb"))
118
- byte_stream = gzip_entry
119
- else:
120
- byte_stream = self.fh
121
-
122
107
  while True:
123
108
  try:
124
- entry = c_utmp.entry(byte_stream)
109
+ entry = c_utmp.entry(self.fh)
125
110
 
126
111
  r_type = ""
127
112
  if entry.ut_type in c_utmp.Type:
@@ -151,7 +136,7 @@ class UtmpFile:
151
136
  # ut_addr_v6 is parsed as IPv4 address. This could not lead to incorrect results.
152
137
  ut_addr = ipaddress.ip_address(struct.pack("<i", entry.ut_addr_v6[0]))
153
138
 
154
- utmp_entry = UTMP_ENTRY(
139
+ yield UTMP_ENTRY(
155
140
  ts=from_unix(entry.ut_tv.tv_sec),
156
141
  ut_type=r_type,
157
142
  ut_pid=entry.ut_pid,
@@ -162,7 +147,6 @@ class UtmpFile:
162
147
  ut_addr=ut_addr,
163
148
  )
164
149
 
165
- yield utmp_entry
166
150
  except EOFError:
167
151
  break
168
152
 
@@ -170,17 +154,28 @@ class UtmpFile:
170
154
  class UtmpPlugin(Plugin):
171
155
  """Unix utmp log plugin."""
172
156
 
173
- WTMP_GLOB = "/var/log/wtmp*"
174
- BTMP_GLOB = "/var/log/btmp*"
157
+ def __init__(self, target: Target):
158
+ super().__init__(target)
159
+ self.btmp_paths = list(self.target.fs.path("/").glob("var/log/btmp*"))
160
+ self.wtmp_paths = list(self.target.fs.path("/").glob("var/log/wtmp*"))
161
+ self.utmp_paths = list(self.target.fs.path("/").glob("var/run/utmp*"))
175
162
 
176
163
  def check_compatible(self) -> None:
177
- if not self.target.os == OperatingSystem.LINUX and not any(
178
- [
179
- list(self.target.fs.glob(self.BTMP_GLOB)),
180
- list(self.target.fs.glob(self.WTMP_GLOB)),
181
- ]
182
- ):
183
- raise UnsupportedPluginError("No WTMP or BTMP log files found")
164
+ if not any(self.btmp_paths + self.wtmp_paths + self.utmp_paths):
165
+ raise UnsupportedPluginError("No wtmp and/or btmp log files found")
166
+
167
+ def _build_record(self, record: TargetRecordDescriptor, entry: UTMP_ENTRY) -> Iterator[BtmpRecord | WtmpRecord]:
168
+ return record(
169
+ ts=entry.ts,
170
+ ut_type=entry.ut_type,
171
+ ut_pid=entry.ut_pid,
172
+ ut_user=entry.ut_user,
173
+ ut_line=entry.ut_line,
174
+ ut_id=entry.ut_id,
175
+ ut_host=entry.ut_host,
176
+ ut_addr=entry.ut_addr,
177
+ _target=self.target,
178
+ )
184
179
 
185
180
  @export(record=BtmpRecord)
186
181
  def btmp(self) -> Iterator[BtmpRecord]:
@@ -192,26 +187,18 @@ class UtmpPlugin(Plugin):
192
187
  - https://en.wikipedia.org/wiki/Utmp
193
188
  - https://www.thegeekdiary.com/what-is-the-purpose-of-utmp-wtmp-and-btmp-files-in-linux/
194
189
  """
195
- btmp_paths = self.target.fs.glob(self.BTMP_GLOB)
196
- for btmp_path in btmp_paths:
197
- btmp = UtmpFile(self.target, btmp_path)
198
-
199
- for entry in btmp:
200
- yield BtmpRecord(
201
- ts=entry.ts,
202
- ut_type=entry.ut_type,
203
- ut_pid=entry.ut_pid,
204
- ut_user=entry.ut_user,
205
- ut_line=entry.ut_line,
206
- ut_id=entry.ut_id,
207
- ut_host=entry.ut_host,
208
- ut_addr=entry.ut_addr,
209
- _target=self.target,
210
- )
190
+ for path in self.btmp_paths:
191
+ if not path.is_file():
192
+ self.target.log.warning("Unable to parse btmp file: %s is not a file", path)
193
+ continue
211
194
 
195
+ for entry in UtmpFile(path):
196
+ yield self._build_record(BtmpRecord, entry)
197
+
198
+ @alias("utmp")
212
199
  @export(record=WtmpRecord)
213
200
  def wtmp(self) -> Iterator[WtmpRecord]:
214
- """Return the content of the wtmp log files.
201
+ """Yield contents of wtmp log files.
215
202
 
216
203
  The wtmp file contains the historical data of the utmp file. The utmp file contains information about users
217
204
  logins at which terminals, logouts, system events and current status of the system, system boot time
@@ -220,19 +207,11 @@ class UtmpPlugin(Plugin):
220
207
  References:
221
208
  - https://www.thegeekdiary.com/what-is-the-purpose-of-utmp-wtmp-and-btmp-files-in-linux/
222
209
  """
223
- wtmp_paths = self.target.fs.glob(self.WTMP_GLOB)
224
- for wtmp_path in wtmp_paths:
225
- wtmp = UtmpFile(self.target, wtmp_path)
226
-
227
- for entry in wtmp:
228
- yield WtmpRecord(
229
- ts=entry.ts,
230
- ut_type=entry.ut_type,
231
- ut_pid=entry.ut_pid,
232
- ut_user=entry.ut_user,
233
- ut_line=entry.ut_line,
234
- ut_id=entry.ut_id,
235
- ut_host=entry.ut_host,
236
- ut_addr=entry.ut_addr,
237
- _target=self.target,
238
- )
210
+
211
+ for path in self.wtmp_paths + self.utmp_paths:
212
+ if not path.is_file():
213
+ self.target.log.warning("Unable to parse wtmp file: %s is not a file", path)
214
+ continue
215
+
216
+ for entry in UtmpFile(path):
217
+ yield self._build_record(WtmpRecord, entry)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dissect.target
3
- Version: 3.20.dev47
3
+ Version: 3.20.dev48
4
4
  Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -272,7 +272,7 @@ dissect/target/plugins/os/unix/log/auth.py,sha256=9NJvlo7Vbsp_ENJFpKd04PH_sUuOy6
272
272
  dissect/target/plugins/os/unix/log/journal.py,sha256=xe8p8MM_95uYjFNzNSP5IsoIthJtxwFEDicYR42RYAI,17681
273
273
  dissect/target/plugins/os/unix/log/lastlog.py,sha256=Wr3-2n1-GwckN9mSx-yM55N6_L0PQyx6TGHoEvuc6c0,2515
274
274
  dissect/target/plugins/os/unix/log/messages.py,sha256=XtjZ0a2budgQm_K5JT3fMf7JcjuD0AelcD3zOFN2xpI,5732
275
- dissect/target/plugins/os/unix/log/utmp.py,sha256=n22dcjhclky3koF4MyRz1cBOmEeWwen6jstLsvfnMw8,7784
275
+ dissect/target/plugins/os/unix/log/utmp.py,sha256=k2A69s2qUT2JunJrH8GO6nQ0zMDotXMTaj8OzQ7ljj8,7336
276
276
  dissect/target/plugins/os/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
277
277
  dissect/target/plugins/os/windows/_os.py,sha256=-Bsp9696JqU7luh_AbqojzG9BxVdYIFl5Ma-LiFBQBo,12505
278
278
  dissect/target/plugins/os/windows/activitiescache.py,sha256=BbGD-vETHm1IRMoazVer_vqSJIoQxxhWcJ_xlBeOMds,6899
@@ -378,10 +378,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
378
378
  dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
379
379
  dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
380
380
  dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
381
- dissect.target-3.20.dev47.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
382
- dissect.target-3.20.dev47.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
383
- dissect.target-3.20.dev47.dist-info/METADATA,sha256=28qUe6AIKvwvWI58yI9VPiy1U86ihuVGMhzR-avNrtc,12897
384
- dissect.target-3.20.dev47.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
385
- dissect.target-3.20.dev47.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
386
- dissect.target-3.20.dev47.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
387
- dissect.target-3.20.dev47.dist-info/RECORD,,
381
+ dissect.target-3.20.dev48.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
382
+ dissect.target-3.20.dev48.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
383
+ dissect.target-3.20.dev48.dist-info/METADATA,sha256=coXjiskalhbUYyedrn0Fe49ZmJvIbeAaMYawTB3Q9QQ,12897
384
+ dissect.target-3.20.dev48.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
385
+ dissect.target-3.20.dev48.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
386
+ dissect.target-3.20.dev48.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
387
+ dissect.target-3.20.dev48.dist-info/RECORD,,