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
38 changes: 28 additions & 10 deletions dissect/target/plugins/apps/webserver/apache.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
from datetime import datetime
from functools import cached_property
from pathlib import Path
from typing import TYPE_CHECKING, NamedTuple
from typing import TYPE_CHECKING

from dissect.target.exceptions import FileNotFoundError, UnsupportedPluginError
from dissect.target.helpers.certificate import parse_x509
from dissect.target.helpers.fsutil import open_decompress
from dissect.target.plugin import OperatingSystem, export
from dissect.target.plugins.apps.webserver.webserver import (
LogFormat,
WebserverAccessLogRecord,
WebserverCertificateRecord,
WebserverErrorLogRecord,
Expand All @@ -26,11 +27,6 @@
from dissect.target.target import Target


class LogFormat(NamedTuple):
name: str
pattern: re.Pattern


# e.g. ServerRoot "/etc/httpd"
RE_CONFIG_ROOT = re.compile(
r"""
Expand Down Expand Up @@ -154,6 +150,24 @@ class LogFormat(NamedTuple):
(?P<message>.*) # The actual log message.
"""

RE_FIRST_LINE_OF_REQUEST = r"""
"
(
- # Malformed requests may result in the value "-"
|
(
(?P<method>.*?) # The HTTP Method used for the request.
\s
(?P<uri>.*?) # The HTTP URI of the request.
\s
?(?P<protocol>HTTP\/.*?)? # The request protocol.
)
|
(?P<request_line>.*?) # Malformed or invalid requests can contain raw bytes
)
"
"""

RE_ENV_VAR_IN_STRING = re.compile(r"\$\{(?P<env_var>[^\"\s$]+)\}", re.VERBOSE)

RE_VIRTUALHOST = re.compile(r"^\<VirtualHost (?P<addr>[^\s:]+)(?:\:(?P<port>\d+))?", re.IGNORECASE)
Expand Down Expand Up @@ -331,7 +345,7 @@ def _process_conf_file(self, path: Path, seen: set[Path] | None = None) -> None:

elif "include" in line_lower:
if not (match := RE_CONFIG_INCLUDE.match(line)):
self.target.log.warning("Unable to parse Apache 'Include' configuration in %s: %r", path, line)
self.target.log.debug("Unable to parse Apache 'Include' configuration in %s: %r", path, line)
continue

location = match.groupdict().get("location")
Expand Down Expand Up @@ -423,16 +437,20 @@ def access(self) -> Iterator[WebserverAccessLogRecord]:
if response_time := log.get("response_time"):
response_time = apache_response_time_to_ms(response_time)

ts = datetime.strptime(log["ts"], logformat.timestamp or "%d/%b/%Y:%H:%M:%S %z") # noqa: DTZ007
if logformat.timestamp and "%z" not in logformat.timestamp:
ts.replace(tzinfo=self.target.datetime.tzinfo)

yield WebserverAccessLogRecord(
ts=datetime.strptime(log["ts"], "%d/%b/%Y:%H:%M:%S %z"),
ts=ts,
webserver=self.__namespace__,
remote_user=clean_value(log["remote_user"]),
remote_user=clean_value(log.get("remote_user")),
remote_ip=log["remote_ip"],
local_ip=clean_value(log.get("local_ip")),
method=log["method"],
uri=log["uri"],
protocol=log["protocol"],
status_code=log["status_code"],
status_code=log.get("status_code"),
bytes_sent=clean_value(log["bytes_sent"]) or 0,
pid=log.get("pid"),
referer=clean_value(log.get("referer")),
Expand Down
Loading