Skip to content

Commit

Permalink
Merge pull request #5848 from freedomofpress/1039-paxtest-checks-in-t…
Browse files Browse the repository at this point in the history
…estinfra

Re-enables paxtest in testinfra
  • Loading branch information
zenmonkeykstop authored Mar 31, 2021
2 parents e6fd7fe + 14ea71e commit 577c278
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 31 deletions.
18 changes: 18 additions & 0 deletions molecule/testinfra/common/paxtest_results.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Executable anonymous mapping : Killed
Executable bss : Killed
Executable data : Killed
Executable heap : Killed
Executable stack : Killed
Executable shared library bss : Killed
Executable shared library data : Killed
Executable anonymous mapping (mprotect) : Killed
Executable bss (mprotect) : Killed
Executable data (mprotect) : Killed
Executable heap (mprotect) : Killed
Executable stack (mprotect) : Killed
Executable shared library bss (mprotect) : Killed
Executable shared library data (mprotect): Killed
Return to function (strcpy) : paxtest: return address contains a NULL byte.
Return to function (memcpy) : {{ memcpy_result }}
Return to function (strcpy, PIE) : paxtest: return address contains a NULL byte.
Return to function (memcpy, PIE) : {{ memcpy_result }}
74 changes: 43 additions & 31 deletions molecule/testinfra/common/test_grsecurity.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import pytest
import re
import warnings
import io
import difflib
import os
from jinja2 import Template

import testutils

Expand Down Expand Up @@ -99,39 +103,47 @@ def test_grsecurity_sysctl_options(host, sysctl_opt):
assert host.sysctl(sysctl_opt[0]) == sysctl_opt[1]


@pytest.mark.skip_in_prod
@pytest.mark.parametrize('paxtest_check', [
"Executable anonymous mapping",
"Executable bss",
"Executable data",
"Executable heap",
"Executable stack",
"Executable shared library bss",
"Executable shared library data",
"Executable anonymous mapping (mprotect)",
"Executable bss (mprotect)",
"Executable data (mprotect)",
"Executable heap (mprotect)",
"Executable stack (mprotect)",
"Executable shared library bss (mprotect)",
"Executable shared library data (mprotect)",
"Writable text segments",
"Return to function (memcpy)",
"Return to function (memcpy, PIE)",
])
def test_grsecurity_paxtest(host, paxtest_check):
def test_grsecurity_paxtest(host):
"""
Check that paxtest does not report anything vulnerable
Requires the package paxtest to be installed.
The paxtest package is currently being installed in the app-test role.
Check that paxtest reports the expected mitigations. These are
"Killed" for most of the checks, with the notable exception of the
memcpy ones. Only newer versions of paxtest will fail the latter,
regardless of kernel.
"""
if host.exists("/usr/bin/paxtest"):
if not host.exists("/usr/bin/paxtest"):
warnings.warn("Installing paxtest to run kernel tests")
with host.sudo():
host.run("apt-get update && apt-get install -y paxtest")
try:
with host.sudo():
# Log to /tmp to avoid cluttering up /root.
paxtest_cmd = "paxtest blackhat /tmp/paxtest.log"
# Select only predictably formatted lines; omit
# the guesses, since the number of bits can vary
paxtest_cmd += " | grep -P '^(Executable|Return)'"
paxtest_results = host.check_output(paxtest_cmd)

paxtest_template_path = "{}/paxtest_results.j2".format(
os.path.dirname(os.path.abspath(__file__)))

memcpy_result = "Killed"
# Versions of paxtest newer than 0.9.12 or so will report
# "Vulnerable" on memcpy tests, see details in
# https://github.com/freedomofpress/securedrop/issues/1039
if host.system_info.codename == "focal":
memcpy_result = "Vulnerable"
with io.open(paxtest_template_path, 'r') as f:
paxtest_template = Template(f.read().rstrip())
paxtest_expected = paxtest_template.render(memcpy_result=memcpy_result)

# The stdout prints here will only be displayed if the test fails
for paxtest_diff in difflib.context_diff(paxtest_expected.split('\n'),
paxtest_results.split('\n')):
print(paxtest_diff)
assert paxtest_results == paxtest_expected
finally:
with host.sudo():
c = host.run("paxtest blackhat")
assert c.rc == 0
assert "Vulnerable" not in c.stdout
regex = r"^{}\s*:\sKilled$".format(re.escape(paxtest_check))
assert re.search(regex, c.stdout)
host.run("apt-get remove -y paxtest")


@pytest.mark.skip_in_prod
Expand Down

0 comments on commit 577c278

Please sign in to comment.