[apparmor] [PATCH v2] Convert aa-status to Python
Marc Deslauriers
marc.deslauriers at canonical.com
Thu May 26 19:59:21 UTC 2011
[v2: cleaned up for Python v3 support, moves Perl aa-status tool to the
deprecated directory for distros that still need it.]
Here is a patch that converts the aa-status tool to Python, so it can be
packaged separately from the rest of the perl tools and not require a
bunch of perl modules to be installed on a minimal system.
Marc.
=== added directory 'deprecated/utils'
=== renamed file 'utils/aa-status' => 'deprecated/utils/aa-status'
=== modified file 'utils/Makefile'
--- utils/Makefile 2011-02-08 18:39:44 +0000
+++ utils/Makefile 2011-05-26 17:53:59 +0000
@@ -28,8 +28,8 @@
MODDIR = Immunix
PERLTOOLS = aa-genprof aa-logprof aa-autodep aa-audit aa-complain aa-enforce \
- aa-unconfined aa-status aa-notify aa-disable
-TOOLS = ${PERLTOOLS} aa-decode
+ aa-unconfined aa-notify aa-disable
+TOOLS = ${PERLTOOLS} aa-decode aa-status
MODULES = ${MODDIR}/AppArmor.pm ${MODDIR}/Repository.pm \
${MODDIR}/Config.pm ${MODDIR}/Severity.pm
=== added file 'utils/aa-status'
--- utils/aa-status 1970-01-01 00:00:00 +0000
+++ utils/aa-status 2011-05-26 19:53:25 +0000
@@ -0,0 +1,203 @@
+#!/usr/bin/python
+# ------------------------------------------------------------------
+#
+# Copyright (C) 2005-2006 Novell/SUSE
+# Copyright (C) 2011 Canonical Ltd.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License published by the Free Software Foundation.
+#
+# ------------------------------------------------------------------
+
+import re, os, sys
+
+def cmd_enabled():
+ '''Returns error code if AppArmor is not enabled'''
+ if get_profiles() == {}:
+ sys.exit(2)
+
+def cmd_profiled():
+ '''Prints the number of loaded profiles'''
+ profiles = get_profiles()
+ sys.stdout.write("%d\n" % len(profiles))
+ if profiles == {}:
+ sys.exit(2)
+
+def cmd_enforced():
+ '''Prints the number of loaded enforcing profiles'''
+ profiles = get_profiles()
+ sys.stdout.write("%d\n" % len(filter_profiles(profiles, 'enforce')))
+ if profiles == {}:
+ sys.exit(2)
+
+def cmd_complaining():
+ '''Prints the number of loaded non-enforcing profiles'''
+ profiles = get_profiles()
+ sys.stdout.write("%d\n" % len(filter_profiles(profiles, 'complain')))
+ if profiles == {}:
+ sys.exit(2)
+
+def cmd_verbose():
+ '''Displays multiple data points about loaded profile set'''
+ global verbose
+ verbose = True
+ profiles = get_profiles()
+ processes = get_processes(profiles)
+
+ stdmsg("%d profiles are loaded." % len(profiles))
+ for status in ('enforce', 'complain'):
+ filtered_profiles = filter_profiles(profiles, status)
+ stdmsg("%d profiles are in %s mode." % (len(filtered_profiles), status))
+ for item in filtered_profiles:
+ stdmsg(" %s" % item)
+
+ stdmsg("%d processes have profiles defined." % len(processes))
+ for status in ('enforce', 'complain'):
+ filtered_processes = filter_processes(processes, status)
+ stdmsg("%d processes are in %s mode." % (len(filtered_processes), status))
+ sorted_list = list(filtered_processes.keys())
+ sorted_list.sort()
+ for item in sorted_list:
+ stdmsg(" %s (%s) " % (item, filtered_processes[item]))
+
+ filtered_processes = filter_processes(processes, 'unconfined')
+ stdmsg("%d processes are unconfined but have a profile defined." % len(filtered_processes))
+ sorted_list = list(filtered_processes.keys())
+ sorted_list.sort()
+ for item in sorted_list:
+ stdmsg(" %s (%s) " % (item, filtered_processes[item]))
+
+ if profiles == {}:
+ sys.exit(2)
+
+def get_profiles():
+ '''Fetch loaded profiles'''
+
+ profiles = {}
+
+ if os.path.exists("/sys/module/apparmor"):
+ stdmsg("apparmor module is loaded.")
+ else:
+ errormsg("apparmor module is not loaded.")
+ sys.exit(1)
+
+ apparmorfs = find_apparmorfs()
+ if not apparmorfs:
+ errormsg("apparmor filesystem is not mounted.")
+ sys.exit(3)
+
+ apparmor_profiles = os.path.join(apparmorfs, "profiles")
+ if not os.access(apparmor_profiles, os.R_OK):
+ errormsg("You do not have enough privilege to read the profile set.")
+ sys.exit(4)
+
+ for p in open(apparmor_profiles).readlines():
+ match = re.search("^([^\(]+)\s+\((\w+)\)$", p)
+ profiles[match.group(1)] = match.group(2)
+
+ return profiles
+
+def get_processes(profiles):
+ '''Fetch process list'''
+ processes = {}
+ contents = os.listdir("/proc")
+ for filename in contents:
+ if filename.isdigit():
+ try:
+ 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:
+ # keep only unconfined processes that have a profile defined
+ processes[filename] = { 'profile' : os.path.realpath("/proc/%s/exe" % filename), \
+ 'mode' : 'unconfined' }
+ except:
+ pass
+ return processes
+
+def filter_profiles(profiles, status):
+ '''Return a list of profiles that have a particular status'''
+ filtered = []
+ for key, value in list(profiles.items()):
+ if value == status:
+ filtered.append(key)
+ filtered.sort()
+ return filtered
+
+def filter_processes(processes, status):
+ '''Return a dict of processes that have a particular status'''
+ filtered = {}
+ for key, value in list(processes.items()):
+ if value['mode'] == status:
+ filtered[value['profile']] = key
+ return filtered
+
+def find_apparmorfs():
+ '''Finds AppArmor mount point'''
+ for p in open("/proc/mounts").readlines():
+ if p.split()[2] == "securityfs" and \
+ os.path.exists(os.path.join(p.split()[1], "apparmor")):
+ return os.path.join(p.split()[1], "apparmor")
+ return False
+
+def errormsg(message):
+ '''Prints to stderr if verbose mode is on'''
+ global verbose
+ if verbose:
+ sys.stderr.write(message + "\n")
+
+def stdmsg(message):
+ '''Prints to stdout if verbose mode is on'''
+ global verbose
+ if verbose:
+ sys.stdout.write(message + "\n")
+
+def print_usage():
+ '''Print usage information'''
+ sys.stdout.write('''Usage: %s [OPTIONS]
+Displays various information about the currently loaded AppArmor policy.
+OPTIONS (one only):
+ --enabled returns error code if AppArmor not enabled
+ --profiled prints the number of loaded policies
+ --enforced prints the number of loaded enforcing policies
+ --complaining prints the number of loaded non-enforcing policies
+ --verbose (default) displays multiple data points about loaded policy set
+ --help this message
+''' % sys.argv[0])
+
+# Main
+global verbose
+verbose = False
+
+if len(sys.argv) > 2:
+ sys.stderr.write("Error: Too many options.\n")
+ print_usage()
+ sys.exit(1)
+elif len(sys.argv) == 2:
+ cmd = sys.argv.pop(1)
+else:
+ cmd = '--verbose'
+
+# Command dispatch:
+commands = {
+ '--enabled' : cmd_enabled,
+ '--profiled' : cmd_profiled,
+ '--enforced' : cmd_enforced,
+ '--complaining' : cmd_complaining,
+ '--verbose' : cmd_verbose,
+ '-v' : cmd_verbose,
+ '--help' : print_usage,
+ '-h' : print_usage
+}
+
+if cmd in commands:
+ commands[cmd]()
+ sys.exit(0)
+else:
+ sys.stderr.write("Error: Invalid command.\n")
+ print_usage()
+ sys.exit(1)
+
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20110526/31000130/attachment-0001.pgp>
More information about the AppArmor
mailing list