Skip to content

Commit

Permalink
Migrated utmpx construct-based parser to use dtfabric log2timeline#1893
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimmetz authored and Onager committed Jun 24, 2018
1 parent 2d012b7 commit 49994c1
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 169 deletions.
7 changes: 5 additions & 2 deletions plaso/formatters/utmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@ class UtmpSessionFormatter(interface.ConditionalEventFormatter):

FORMAT_STRING_PIECES = [
'User: {username}',
'Computer Name: {computer_name}',
'Hostname: {hostname}',
'Terminal: {terminal}',
'PID: {pid}',
'Terminal identifier: {terminal_identifier}',
'Status: {status}',
'IP Address: {ip_address}',
'Exit status: {exit_status}']

FORMAT_STRING_SHORT_PIECES = ['User: {username}']
FORMAT_STRING_SHORT_PIECES = [
'User: {username}',
'PID: {pid}',
'Status: {status}']

SOURCE_LONG = 'UTMP session'
SOURCE_SHORT = 'LOG'
Expand Down
24 changes: 15 additions & 9 deletions plaso/formatters/utmpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ class UtmpxSessionFormatter(interface.ConditionalEventFormatter):
DATA_TYPE = 'mac:utmpx:event'

FORMAT_STRING_PIECES = [
'User: {user}',
'User: {username}',
'Status: {status}',
'Computer Name: {computer_name}',
'Terminal: {terminal}']
'Hostname: {hostname}',
'Terminal: {terminal}',
'PID: {pid}',
'Terminal identifier: {terminal_identifier}']

FORMAT_STRING_SHORT_PIECES = ['User: {user}']
FORMAT_STRING_SHORT_PIECES = [
'User: {username}',
'PID: {pid}',
'Status: {status}']

SOURCE_LONG = 'UTMPX session'
SOURCE_SHORT = 'LOG'
Expand Down Expand Up @@ -60,12 +65,13 @@ def GetMessages(self, unused_formatter_mediator, event):

event_values = event.CopyToDict()

status_type = event_values.get('status_type', None)
if status_type is not None:
event_values['status'] = self._STATUS_TYPES.get(
status_type, '{0:d}'.format(status_type))
login_type = event_values.get('type', None)
if login_type is None:
status = 'N/A'
else:
event_values['status'] = 'N/A'
status = self._STATUS_TYPES.get(login_type, 'UNKNOWN')

event_values['status'] = status

return self._ConditionalFormatMessages(event_values)

Expand Down
31 changes: 16 additions & 15 deletions plaso/parsers/utmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ class UtmpEventData(events.EventData):
"""utmp event data.
Attributes:
computer_name (str): name of the computer.
exit_status (int): exit status.
hostname (str): hostname or IP address.
ip_address (str): IP address from the connection.
pid (int): process identifier (PID).
terminal (str): type of terminal.
terminal_identifier (int): inittab identifier.
terminal (str): type of terminal.
type (int): type of login.
username (str): user name.
"""
Expand All @@ -32,21 +32,21 @@ class UtmpEventData(events.EventData):
def __init__(self):
"""Initializes event data."""
super(UtmpEventData, self).__init__(data_type=self.DATA_TYPE)
self.computer_name = None
self.exit_status = None
self.hostname = None
self.ip_address = None
self.pid = None
self.terminal = None
self.terminal_identifier = None
self.terminal = None
self.type = None
self.username = None


class UtmpParser(dtfabric_parser.DtFabricBaseParser):
"""Parser for Linux/Unix utmp files."""
"""Parser for Linux libc6 utmp files."""

NAME = 'utmp'
DESCRIPTION = 'Parser for Linux/Unix utmp files.'
DESCRIPTION = 'Parser for Linux libc6 utmp files.'

_DEFINITION_FILE = 'utmp.yaml'

Expand Down Expand Up @@ -74,7 +74,7 @@ def _ReadEntry(self, parser_mediator, file_object, file_offset):
Raises:
ParseError: if the entry cannot be parsed.
"""
entry_map = self._GetDataTypeMap('utmp_entry')
entry_map = self._GetDataTypeMap('linux_libc6_utmp_entry')

try:
entry, _ = self._ReadStructureFromFileObject(
Expand All @@ -91,14 +91,14 @@ def _ReadEntry(self, parser_mediator, file_object, file_offset):
encoding = parser_mediator.codepage or 'utf-8'

try:
username = entry.username.rstrip(b'\x00')
username = entry.username.split(b'\x00')[0]
username = username.decode(encoding)
except UnicodeDecodeError:
parser_mediator.ProduceExtractionError('unable to decode username string')
username = None

try:
terminal = entry.terminal.rstrip(b'\x00')
terminal = entry.terminal.split(b'\x00')[0]
terminal = terminal.decode(encoding)
except UnicodeDecodeError:
parser_mediator.ProduceExtractionError('unable to decode terminal string')
Expand All @@ -108,7 +108,7 @@ def _ReadEntry(self, parser_mediator, file_object, file_offset):
terminal = 'system boot'

try:
hostname = entry.hostname.rstrip(b'\x00')
hostname = entry.hostname.split(b'\x00')[0]
hostname = hostname.decode(encoding)
except UnicodeDecodeError:
parser_mediator.ProduceExtractionError('unable to decode hostname string')
Expand All @@ -123,11 +123,11 @@ def _ReadEntry(self, parser_mediator, file_object, file_offset):
ip_address = self._FormatPackedIPv6Address(entry.ip_address)

# TODO: add termination status.
# TODO: rename event data attributes to match data definition.
event_data = UtmpEventData()
event_data.computer_name = hostname
event_data.hostname = hostname
event_data.exit_status = entry.exit_status
event_data.ip_address = ip_address
event_data.offset = file_offset
event_data.pid = entry.pid
event_data.terminal = terminal
event_data.terminal_identifier = entry.terminal_identifier
Expand Down Expand Up @@ -156,15 +156,16 @@ def ParseFileObject(self, parser_mediator, file_object, **kwargs):
parser_mediator, file_object, file_offset)
except errors.ParseError as exception:
raise errors.UnableToParseFile(
'Unable to parse utmp header with error: {0!s}'.format(exception))
'Unable to parse first utmp entry with error: {0!s}'.format(
exception))

if not event_data.username:
raise errors.UnableToParseFile(
'Unable to parse utmp header with error: missing username')
'Unable to parse first utmp entry with error: missing username')

if not timestamp:
raise errors.UnableToParseFile(
'Unable to parse utmp header with error: missing timestamp')
'Unable to parse first utmp entry with error: missing timestamp')

date_time = dfdatetime_posix_time.PosixTimeInMicroseconds(
timestamp=timestamp)
Expand Down
36 changes: 35 additions & 1 deletion plaso/parsers/utmp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ attributes:
size: 4
units: bytes
---
name: utmp_entry
name: linux_libc6_utmp_entry
type: structure
attributes:
byte_order: little-endian
Expand Down Expand Up @@ -72,3 +72,37 @@ members:
type: stream
element_data_type: byte
number_of_elements: 20
---
name: macosx_utmpx_entry
type: structure
attributes:
byte_order: little-endian
members:
- name: username
type: stream
element_data_type: byte
number_of_elements: 256
- name: terminal_identifier
data_type: uint32
- name: terminal
type: stream
element_data_type: byte
number_of_elements: 32
- name: pid
data_type: uint32
- name: type
data_type: int16
- name: unknown1
data_type: int16
- name: timestamp
data_type: int32
- name: microseconds
data_type: int32
- name: hostname
type: stream
element_data_type: byte
number_of_elements: 256
- name: unknown2
type: stream
element_data_type: byte
number_of_elements: 64

0 comments on commit 49994c1

Please sign in to comment.