[apparmor] [PATCH v2] Convert aa-status to Python

Jamie Strandboge jamie at canonical.com
Thu May 26 20:25:49 UTC 2011


On Thu, 2011-05-26 at 15:59 -0400, Marc Deslauriers wrote:
> [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.
> 
Thanks for this! However, the output between the perl version and the
python version does not match. Specifically, on my system I have this
with no args and with verbose:

== arg: '--verbose' ==
--- /tmp/tmp.Z2gmWQ2abW/old--verbose	2011-05-26 15:19:51.000000000 -0500
+++ /tmp/tmp.Z2gmWQ2abW/new--verbose	2011-05-26 15:19:51.000000000 -0500
@@ -36,14 +36,11 @@
    libvirt-7effd7a3-af40-abd9-1ac1-d7e70f9ea6c7
 0 profiles are in complain mode.
 15 processes have profiles defined.
-15 processes are in enforce mode :
+12 processes are in enforce mode.
    /sbin/dhclient (7397) 
    /usr/bin/empathy (23691) 
    /usr/lib/telepathy/mission-control-5 (23567) 
-   /usr/lib/telepathy/telepathy-* (7502) 
    /usr/lib/telepathy/telepathy-* (23574) 
-   /usr/lib/telepathy/telepathy-* (7479) 
-   /usr/lib/telepathy/telepathy-* (23585) 
    /usr/sbin/cupsd (1658) 
    /usr/sbin/dnsmasq (2145) 
    /usr/sbin/libvirtd (1998)

Attached is the test script (crude) I used for this.

> 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)
> +
> 

-- 
Jamie Strandboge             | http://www.canonical.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: aa-status-test.sh
Type: application/x-shellscript
Size: 459 bytes
Desc: not available
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20110526/1d4e252c/attachment.bin>
-------------- 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/1d4e252c/attachment.pgp>


More information about the AppArmor mailing list