From b9bfc10d0fb5a59dec4958e0d851ea815ff5bee6 Mon Sep 17 00:00:00 2001 From: Marc Deslauriers Date: Fri, 27 May 2011 15:08:50 -0400 Subject: [PATCH] - added new aa-status tool rewritten in Python - moved old Perl aa-status tool to the deprecated directory --- deprecated/utils/aa-status | 218 +++++++++++++++++++++ utils/Makefile | 4 +- utils/aa-status | 384 ++++++++++++++++++------------------- 3 files changed, 403 insertions(+), 203 deletions(-) create mode 100644 deprecated/utils/aa-status mode change 100644 => 100755 utils/aa-status diff --git a/deprecated/utils/aa-status b/deprecated/utils/aa-status new file mode 100644 index 000000000..7192912b1 --- /dev/null +++ b/deprecated/utils/aa-status @@ -0,0 +1,218 @@ +#!/usr/bin/perl -w +# ------------------------------------------------------------------ +# +# Copyright (C) 2005-2006 Novell/SUSE +# +# 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. +# +# ------------------------------------------------------------------ + + +use strict; +use Getopt::Long; +use Cwd 'abs_path'; + +my $confdir = "/etc/apparmor"; +my $sd_mountpoint; +my $check_enabled = 0; +my $count_enforced = 0; +my $count_profiled = 0; +my $count_complain = 0; +my $verbose = 0; +my $help; + +GetOptions( + 'complaining' => \$count_complain, + 'enabled' => \$check_enabled, + 'enforced' => \$count_enforced, + 'profiled' => \$count_profiled, + 'verbose|v' => \$verbose, + 'help|h' => \$help, +) or usage(); + +sub usage { + print "Usage: $0 [OPTIONS]\n"; + print "Displays various information about the currently loaded AppArmor policy.\n"; + print "OPTIONS (one only):\n"; + print " --enabled returns error code if subdomain not enabled\n"; + print " --profiled prints the number of loaded policies\n"; + print " --enforced prints the number of loaded enforcing policies\n"; + print " --complaining prints the number of loaded non-enforcing policies\n"; + print " --verbose (default) displays multiple data points about loaded policy set\n"; + print " --help this message\n"; + exit; +} + +$verbose = 1 if ($count_complain + $check_enabled + $count_enforced + $count_profiled == 0); +usage() if $help or ($count_complain + $check_enabled + $count_enforced + $count_profiled + $verbose > 1); + +sub is_subdomain_loaded() { + return 1 if (-d "/sys/module/apparmor"); + if(open(MODULES, "/proc/modules")) { + while() { + return 1 if m/^(subdomain|apparmor)\s+/; + } + } + + return 0; +} + +sub find_subdomainfs() { + + my $sd_mountpoint; + if(open(MOUNTS, "/proc/mounts")) { + while() { + $sd_mountpoint = "$1/apparmor" if m/^\S+\s+(\S+)\s+securityfs\s/ && -e "$1/apparmor"; + $sd_mountpoint = "$1/subdomain" if m/^\S+\s+(\S+)\s+securityfs\s/ && -e "$1/subdomain"; + $sd_mountpoint = $1 if m/^\S+\s+(\S+)\s+subdomainfs\s/ && -e "$1"; + } + close(MOUNTS); + } + + return $sd_mountpoint; +} + +sub get_profiles { + my $mountpoint = shift; + my %profiles = (); + + if (open(PROFILES, "$mountpoint/profiles")) { + while() { + $profiles{$1} = $2 if m/^([^\(]+)\s+\((\w+)\)$/; + } + close(PROFILES); + } + return (%profiles); +} + +sub get_processes { + my %profiles = @_; + my %processes = (); + if (opendir(PROC, "/proc")) { + my $file; + while (defined($file = readdir(PROC))) { + if ($file =~ m/^\d+/) { + if (open(CURRENT, "/proc/$file/attr/current")) { + while () { + if (m/^([^\(]+)\s+\((\w+)\)$/) { + $processes{$file}{'profile'} = $1; + $processes{$file}{'mode'} = $2; + } elsif (grep(abs_path("/proc/$file/exe") eq $_ , keys(%profiles))) { + # keep only unconfined processes that have a profile defined + $processes{$file}{'profile'} = abs_path("/proc/$file/exe"); + $processes{$file}{'mode'} = 'unconfined'; + } + } + close(CURRENT); + } + } + } + closedir(PROC); + } + return (%processes); +} + +my $is_loaded = is_subdomain_loaded(); + +if (!$is_loaded) { + print STDERR "apparmor module is not loaded.\n" if $verbose; + exit 1; +} + +print "apparmor module is loaded.\n" if $verbose; + +$sd_mountpoint = find_subdomainfs(); +if (!$sd_mountpoint) { + print STDERR "apparmor filesystem is not mounted.\n" if $verbose; + exit 3; +} + +if (! -r "$sd_mountpoint/profiles") { + print STDERR "You do not have enough privilege to read the profile set.\n" if $verbose; + exit 4; +} + +#print "subdomainfs is at $sd_mountpoint.\n" if $verbose; + +# processes is a hash table : +# * keys : processes pid +# * values : hash containing information about the running process: +# * 'profile' : name of the profile applied to the running process +# * 'mode' : mode of the profile applied to the running process +my %processes = (); +my %enforced_processes = (); +my %complain_processes = (); +my %unconfined_processes = (); + +# profiles is a hash table : +# * keys : profile name +# * value : profile mode +my %profiles; +my @enforced_profiles = (); +my @complain_profiles = (); + +%profiles = get_profiles($sd_mountpoint); +@enforced_profiles = grep { $profiles{$_} eq 'enforce' } keys %profiles; +@complain_profiles = grep { $profiles{$_} eq 'complain' } keys %profiles; + +# we consider the case where no profiles are loaded to be "disabled" as well +my $rc = (keys(%profiles) == 0) ? 2 : 0; + +if ($check_enabled) { + exit $rc; +} + +if ($count_profiled) { + print scalar(keys(%profiles)). "\n"; + exit $rc; +} + +if ($count_enforced) { + print $#enforced_profiles + 1 . "\n"; + exit $rc; +} + +if ($count_complain) { + print $#complain_profiles + 1 . "\n"; + exit $rc; +} + + +if ($verbose) { + print keys(%profiles) . " profiles are loaded.\n"; + print $#enforced_profiles + 1 . " profiles are in enforce mode.\n"; + for (sort(@enforced_profiles)) { + print " " . $_ . "\n"; + } + print $#complain_profiles + 1 . " profiles are in complain mode.\n"; + for (sort(@complain_profiles)) { + print " " . $_ . "\n"; + } +} + +%processes = get_processes(%profiles); +if ($verbose) { + for (keys(%processes)) { + $enforced_processes{$_} = $processes{$_} if $processes{$_}{'mode'} eq 'enforce'; + $complain_processes{$_} = $processes{$_} if $processes{$_}{'mode'} eq 'complain'; + # some early code uses unconfined instead of unconfined. + $unconfined_processes{$_} = $processes{$_} if $processes{$_}{'mode'} =~ /uncon(fi|strai)ned/; + } + print keys(%processes) . " processes have profiles defined.\n"; + print keys(%enforced_processes) . " processes are in enforce mode :\n"; + for (sort { $enforced_processes{$a}{'profile'} cmp $enforced_processes{$b}{'profile'} } keys(%enforced_processes)) { + print " " . $enforced_processes{$_}{'profile'} . " ($_) \n"; + } + print keys(%complain_processes) . " processes are in complain mode.\n"; + for (sort { $complain_processes{$a}{'profile'} cmp $complain_processes{$b}{'profile'} } keys(%complain_processes)) { + print " " . $complain_processes{$_}{'profile'} . " ($_) \n"; + } + print keys(%unconfined_processes) . " processes are unconfined but have a profile defined.\n"; + for (sort { $unconfined_processes{$a}{'profile'} cmp $unconfined_processes{$b}{'profile'} } keys(%unconfined_processes)) { + print " " . $unconfined_processes{$_}{'profile'} . " ($_) \n"; + } +} + +exit $rc; diff --git a/utils/Makefile b/utils/Makefile index d0b848bc1..28b7fcec7 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -28,8 +28,8 @@ endif 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 diff --git a/utils/aa-status b/utils/aa-status old mode 100644 new mode 100755 index 7192912b1..49104fe27 --- a/utils/aa-status +++ b/utils/aa-status @@ -1,7 +1,8 @@ -#!/usr/bin/perl -w +#!/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 @@ -9,210 +10,191 @@ # # ------------------------------------------------------------------ +import re, os, sys -use strict; -use Getopt::Long; -use Cwd 'abs_path'; +def cmd_enabled(): + '''Returns error code if AppArmor is not enabled''' + if get_profiles() == {}: + sys.exit(2) -my $confdir = "/etc/apparmor"; -my $sd_mountpoint; -my $check_enabled = 0; -my $count_enforced = 0; -my $count_profiled = 0; -my $count_complain = 0; -my $verbose = 0; -my $help; +def cmd_profiled(): + '''Prints the number of loaded profiles''' + profiles = get_profiles() + sys.stdout.write("%d\n" % len(profiles)) + if profiles == {}: + sys.exit(2) -GetOptions( - 'complaining' => \$count_complain, - 'enabled' => \$check_enabled, - 'enforced' => \$count_enforced, - 'profiled' => \$count_profiled, - 'verbose|v' => \$verbose, - 'help|h' => \$help, -) or usage(); +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) -sub usage { - print "Usage: $0 [OPTIONS]\n"; - print "Displays various information about the currently loaded AppArmor policy.\n"; - print "OPTIONS (one only):\n"; - print " --enabled returns error code if subdomain not enabled\n"; - print " --profiled prints the number of loaded policies\n"; - print " --enforced prints the number of loaded enforcing policies\n"; - print " --complaining prints the number of loaded non-enforcing policies\n"; - print " --verbose (default) displays multiple data points about loaded policy set\n"; - print " --help this message\n"; - exit; +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', 'unconfined'): + filtered_processes = filter_processes(processes, status) + if status == 'unconfined': + stdmsg("%d processes are unconfined but have a profile defined." % len(filtered_processes)) + else: + stdmsg("%d processes are in %s mode." % (len(filtered_processes), status)) + # Sort by name, and then by pid + filtered_processes.sort(key=lambda x: int(x[0])) + filtered_processes.sort(key=lambda x: x[1]) + for (pid, process) in filtered_processes: + stdmsg(" %s (%s) " % (process, pid)) + + 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 list of processes that have a particular status''' + filtered = [] + for key, value in list(processes.items()): + if value['mode'] == status: + filtered.append([key, value['profile']]) + 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 } -$verbose = 1 if ($count_complain + $check_enabled + $count_enforced + $count_profiled == 0); -usage() if $help or ($count_complain + $check_enabled + $count_enforced + $count_profiled + $verbose > 1); +if cmd in commands: + commands[cmd]() + sys.exit(0) +else: + sys.stderr.write("Error: Invalid command.\n") + print_usage() + sys.exit(1) -sub is_subdomain_loaded() { - return 1 if (-d "/sys/module/apparmor"); - if(open(MODULES, "/proc/modules")) { - while() { - return 1 if m/^(subdomain|apparmor)\s+/; - } - } - - return 0; -} - -sub find_subdomainfs() { - - my $sd_mountpoint; - if(open(MOUNTS, "/proc/mounts")) { - while() { - $sd_mountpoint = "$1/apparmor" if m/^\S+\s+(\S+)\s+securityfs\s/ && -e "$1/apparmor"; - $sd_mountpoint = "$1/subdomain" if m/^\S+\s+(\S+)\s+securityfs\s/ && -e "$1/subdomain"; - $sd_mountpoint = $1 if m/^\S+\s+(\S+)\s+subdomainfs\s/ && -e "$1"; - } - close(MOUNTS); - } - - return $sd_mountpoint; -} - -sub get_profiles { - my $mountpoint = shift; - my %profiles = (); - - if (open(PROFILES, "$mountpoint/profiles")) { - while() { - $profiles{$1} = $2 if m/^([^\(]+)\s+\((\w+)\)$/; - } - close(PROFILES); - } - return (%profiles); -} - -sub get_processes { - my %profiles = @_; - my %processes = (); - if (opendir(PROC, "/proc")) { - my $file; - while (defined($file = readdir(PROC))) { - if ($file =~ m/^\d+/) { - if (open(CURRENT, "/proc/$file/attr/current")) { - while () { - if (m/^([^\(]+)\s+\((\w+)\)$/) { - $processes{$file}{'profile'} = $1; - $processes{$file}{'mode'} = $2; - } elsif (grep(abs_path("/proc/$file/exe") eq $_ , keys(%profiles))) { - # keep only unconfined processes that have a profile defined - $processes{$file}{'profile'} = abs_path("/proc/$file/exe"); - $processes{$file}{'mode'} = 'unconfined'; - } - } - close(CURRENT); - } - } - } - closedir(PROC); - } - return (%processes); -} - -my $is_loaded = is_subdomain_loaded(); - -if (!$is_loaded) { - print STDERR "apparmor module is not loaded.\n" if $verbose; - exit 1; -} - -print "apparmor module is loaded.\n" if $verbose; - -$sd_mountpoint = find_subdomainfs(); -if (!$sd_mountpoint) { - print STDERR "apparmor filesystem is not mounted.\n" if $verbose; - exit 3; -} - -if (! -r "$sd_mountpoint/profiles") { - print STDERR "You do not have enough privilege to read the profile set.\n" if $verbose; - exit 4; -} - -#print "subdomainfs is at $sd_mountpoint.\n" if $verbose; - -# processes is a hash table : -# * keys : processes pid -# * values : hash containing information about the running process: -# * 'profile' : name of the profile applied to the running process -# * 'mode' : mode of the profile applied to the running process -my %processes = (); -my %enforced_processes = (); -my %complain_processes = (); -my %unconfined_processes = (); - -# profiles is a hash table : -# * keys : profile name -# * value : profile mode -my %profiles; -my @enforced_profiles = (); -my @complain_profiles = (); - -%profiles = get_profiles($sd_mountpoint); -@enforced_profiles = grep { $profiles{$_} eq 'enforce' } keys %profiles; -@complain_profiles = grep { $profiles{$_} eq 'complain' } keys %profiles; - -# we consider the case where no profiles are loaded to be "disabled" as well -my $rc = (keys(%profiles) == 0) ? 2 : 0; - -if ($check_enabled) { - exit $rc; -} - -if ($count_profiled) { - print scalar(keys(%profiles)). "\n"; - exit $rc; -} - -if ($count_enforced) { - print $#enforced_profiles + 1 . "\n"; - exit $rc; -} - -if ($count_complain) { - print $#complain_profiles + 1 . "\n"; - exit $rc; -} - - -if ($verbose) { - print keys(%profiles) . " profiles are loaded.\n"; - print $#enforced_profiles + 1 . " profiles are in enforce mode.\n"; - for (sort(@enforced_profiles)) { - print " " . $_ . "\n"; - } - print $#complain_profiles + 1 . " profiles are in complain mode.\n"; - for (sort(@complain_profiles)) { - print " " . $_ . "\n"; - } -} - -%processes = get_processes(%profiles); -if ($verbose) { - for (keys(%processes)) { - $enforced_processes{$_} = $processes{$_} if $processes{$_}{'mode'} eq 'enforce'; - $complain_processes{$_} = $processes{$_} if $processes{$_}{'mode'} eq 'complain'; - # some early code uses unconfined instead of unconfined. - $unconfined_processes{$_} = $processes{$_} if $processes{$_}{'mode'} =~ /uncon(fi|strai)ned/; - } - print keys(%processes) . " processes have profiles defined.\n"; - print keys(%enforced_processes) . " processes are in enforce mode :\n"; - for (sort { $enforced_processes{$a}{'profile'} cmp $enforced_processes{$b}{'profile'} } keys(%enforced_processes)) { - print " " . $enforced_processes{$_}{'profile'} . " ($_) \n"; - } - print keys(%complain_processes) . " processes are in complain mode.\n"; - for (sort { $complain_processes{$a}{'profile'} cmp $complain_processes{$b}{'profile'} } keys(%complain_processes)) { - print " " . $complain_processes{$_}{'profile'} . " ($_) \n"; - } - print keys(%unconfined_processes) . " processes are unconfined but have a profile defined.\n"; - for (sort { $unconfined_processes{$a}{'profile'} cmp $unconfined_processes{$b}{'profile'} } keys(%unconfined_processes)) { - print " " . $unconfined_processes{$_}{'profile'} . " ($_) \n"; - } -} - -exit $rc;