From 5276e507b697c1ff0b2d8fef5ea2bbb3abb35514 Mon Sep 17 00:00:00 2001
From: Simon McVittie <simon.mcvittie@collabora.co.uk>
Date: Wed, 20 Jul 2016 20:37:22 +0100
Subject: [PATCH] session-lockdown: don't assume that unparsed contexts are
 unconfined

In practice, the regex should always match: AppArmor "confinement
strings" appear to always contain a label and mode, except in the
special case "unconfined". However, if this is untrue for whatever
reason, we should log it as an error, not carry on blindly.

Reviewed-by: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
Differential Revision: https://phabricator.apertis.org/D3637
---
 apparmor/session-lockdown/no-deny | 35 +++++++++++++++++++++++++------
 1 file changed, 29 insertions(+), 6 deletions(-)

diff --git a/apparmor/session-lockdown/no-deny b/apparmor/session-lockdown/no-deny
index fe20d6c..01a2a6a 100755
--- a/apparmor/session-lockdown/no-deny
+++ b/apparmor/session-lockdown/no-deny
@@ -23,6 +23,21 @@ ORDINARY_USER = 'user'
 ORDINARY_UID = subprocess.check_output(['id', '-u', ORDINARY_USER],
         universal_newlines=True).strip()
 
+test_number = 0
+failures = 0
+
+def ok(details):
+    global test_number
+    test_number += 1
+    print('ok {} - {}'.format(test_number, details))
+
+def not_ok(details):
+    global test_number
+    global failures
+    test_number += 1
+    failures += 1
+    print('not ok {} - {}'.format(test_number, details))
+
 def stdmsg(*x):
     print(*x)
 
@@ -72,17 +87,22 @@ def get_processes(profiles):
     for filename in contents:
         if filename.isdigit():
             try:
+                exe = os.path.realpath("/proc/%s/exe" % filename)
                 for p in open("/proc/%s/attr/current" % filename).readlines():
                     match = re.search("^([^\(]+)\s+\((\w+)\)$", p)
                     if match:
                         processes[filename] = { 'profile' : match.group(1), \
                                                 'mode' : match.group(2) }
-                    elif os.path.realpath("/proc/%s/exe" % filename) in profiles:
+                    elif p.strip() == 'unconfined' and exe in profiles:
                         # keep only unconfined processes that have a profile defined
-                        processes[filename] = { 'profile' : os.path.realpath("/proc/%s/exe" % filename), \
+                        processes[filename] = { 'profile' : exe,
                                                 'mode' : 'unconfined' }
+                    elif p.strip() != 'unconfined':
+                        not_ok('process {} {!r} context {!r} could not be '
+                               'parsed'.format(filename, exe, p))
             except:
                 pass
+
     return processes
 
 def filter_profiles(profiles, status):
@@ -203,14 +223,12 @@ def after_reboot():
                 'aa_log_extract_tokens.pl')
 
 def wrap_test(name, func):
-    print('1..1')
     try:
         func()
     except Exception as e:
-        print('not ok 1 - {}: {}'.format(name, e))
-        raise
+        not_ok('{}: {}'.format(name, e))
     else:
-        print('ok 1 - {}'.format(name))
+        ok(name)
 
 if __name__ == '__main__':
     if os.getuid() != 0:
@@ -230,3 +248,8 @@ Usage: {argv0} before-reboot
        sudo reboot
        {argv0} after-reboot
 '''.format(argv0=sys.argv[0]))
+
+    print('1..{}'.format(test_number))
+
+    if failures > 0:
+        raise SystemExit(1)
-- 
GitLab