------------------------------------------------------------ revno: 3648 fixes bug: https://launchpad.net/bugs/1668892 committer: Tyler Hicks branch nick: apparmor timestamp: Fri 2017-03-24 05:08:01 +0000 message: utils: Add aa-remove-unknown utility to unload unknown profiles https://launchpad.net/bugs/1668892 This patch creates a new utility, with the code previously used in the init script 'restart' action, that removes unknown profiles which are not found in /etc/apparmor.d/. The functionality was removed from the common init script code in the fix for CVE-2017-6507. The new utility prints a message containing the name of each unknown profile before the profiles are removed. It also supports a dry run mode so that an administrator can check which profiles will be removed before unloading any unknown profiles. If you backport this utility with the fix for CVE-2017-6507 to an apparmor 2.10 release and your backported aa-remove-unknown utility is sourcing the upstream rc.apparmor.functions file, you'll want to include the following bug fix to prevent the aa-remove-unknown utility from removing child profiles that it shouldn't remove: r3440 - Fix: parser: incorrect output of child profile names Signed-off-by: Tyler Hicks Acked-by: Seth Arnold Acked-by: John Johansen ------------------------------------------------------------ revno: 3647 fixes bug: https://launchpad.net/bugs/1668892 committer: Tyler Hicks branch nick: apparmor timestamp: Fri 2017-03-24 05:06:07 +0000 message: parser: Preserve unknown profiles when restarting apparmor init/job/unit CVE-2017-6507 https://launchpad.net/bugs/1668892 The common AppArmor 'restart' code used by some init scripts, upstart jobs, and/or systemd units contained functionality that is no longer appropriate to retain. Any profiles not found /etc/apparmor.d/ were assumed to be obsolete and were unloaded. That behavior became problematic now that there's a growing number of projects that maintain their own internal set of AppArmor profiles outside of /etc/apparmor.d/. It resulted in the AppArmor 'restart' code leaving some important processes running unconfined. A couple examples are profiles managed by LXD and Docker. Signed-off-by: Tyler Hicks Acked-by: Seth Arnold Acked-by: John Johansen ------------------------------------------------------------ revno: 3646 committer: Seth Arnold branch nick: apparmor timestamp: Tue 2017-03-21 12:09:59 -0700 message: parser: Fix delete after new[] -- patch from Oleg Strikov ------------------------------------------------------------ revno: 3645 [merge] committer: Tyler Hicks branch nick: apparmor timestamp: Thu 2017-03-16 02:51:03 +0000 message: profiles: Update nvidia abstraction for newer nvidia drivers Signed-off-by: Tyler Hicks Acked-by: Jamie Strandboge ------------------------------------------------------------ revno: 3644 committer: Christian Boltz branch nick: apparmor timestamp: Fri 2017-03-03 13:14:55 +0100 message: Fix regressions caused by init_aa() With the init_aa() patch series commited, minitools_test.py showed several test failures - which effectively means the -d option of aa-complain, aa-cleanprof etc. was broken. These failures were caused by - calling init_aa() too late in tools.py - _after_ setting the profiledir, which then got overwritten by init_aa() - calling init_aa() twice (because apparmor.aa gets imported in two modules used by aa-cleanprof), which overwrote the manually set values on the second run This patch fixes the call order in tools.py and adds a check to init_aa() so that it only runs once and ignores additional calls. Acked-by: Tyler Hicks Acked-by: Seth Arnold ------------------------------------------------------------ revno: 3643 committer: Christian Boltz branch nick: apparmor timestamp: Fri 2017-03-03 13:14:03 +0100 message: test-parser-simple-tests.py: No longer skip testing generated_perms_leading profiles FileRule understands leading permissions, so the reason to skip those (generated) test profiles in test-parser-simple-tests.py is gone. However, the gen-xtrans.pl script generates profiles with a not-so-valid mix of uppercase and lowercase, for example "Pux" and "Cux". The parser accepts this, but the tools complain about such rules. Therefore add the affected profiles to the exception list. In total, this means we now test 319 of the 380 generated_perms_leading test profiles. The patch also moves some lines around to get the \-escaped profiles out of the mixed uppercase/lowercase exec rule section. Acked-by: Seth Arnold ------------------------------------------------------------ revno: 3642 committer: Tyler Hicks branch nick: apparmor timestamp: Thu 2017-03-02 21:25:01 +0000 message: utils: Fix apparmor.easyprof import in test-aa-easyprof.py The test-aa-easyprof.py script was attempting to do its own special setup to import the in-tree easyprof module. However, this proved to be very flaky and resulted in the test periodically failing due to an AttributeError the first time easyprof.parse_args() was called. This patch removes the flakiness by trusting that PYTHONPATH is set up appropriately before the test script is ran. PYTHONPATH is already initialized appropriately by utils/test/Makefile according to the USE_SYSTEM make variable. Signed-off-by: Tyler Hicks Acked-by: Seth Arnold Acked-by: Christian Boltz ------------------------------------------------------------ revno: 3641 committer: Tyler Hicks branch nick: apparmor timestamp: Thu 2017-03-02 21:24:33 +0000 message: utils: Set parser executable path according to USE_SYSTEM make variable if USE_SYSTEM is not set, the utils make check target will instruct test-aa-easyprof.py to provide the path of the in-tree parser executable to aa-easyprof. If USE_SYSTEM is set, the default parser path (/sbin/apparmor_parser or the result of `which apparmor_parser`) is used. The test-aa-easyprof.py script receives the parser path by checking the __AA_PARSER environment variable. This environment variable is strictly used by the test script and not any user-facing code so two leading underscores were used. Signed-off-by: Tyler Hicks Acked-by: Christian Boltz Acked-by: Seth Arnold ------------------------------------------------------------ revno: 3640 committer: Tyler Hicks branch nick: apparmor timestamp: Thu 2017-03-02 21:24:05 +0000 message: utils: Add option to aa-easyprof to specify the apparmor_parser path When testing against a clean system without the apparmor_parser binary installed, the test-aa-easyprof.py script ends up skipping profile verification because it can't find the parser binary. This even causes a test failure due to the test_genpolicy_invalid_template_policy test. Adding a --parser option to aa-easyprof is the first step in addressing this problem. Signed-off-by: Tyler Hicks Acked-by: Christian Boltz Acked-by: Seth Arnold ------------------------------------------------------------ revno: 3639 committer: Tyler Hicks branch nick: apparmor timestamp: Thu 2017-03-02 21:23:32 +0000 message: utils: Set parser base path according to USE_SYSTEM make variable If USE_SYSTEM is not set, the utils make check target will instruct test-aa-easyprof.py to provide the path of the in-tree profiles/apparmor.d directory to aa-easyprof as the parser base directory. If USE_SYSTEM is set, the default base directory (/etc/apparmor.d) is used. The test-aa-easyprof.py script receives the base path by checking the __AA_BASEDIR environment variable. This environment variable is strictly used by the test script and not any user-facing code so two leading underscores were used. Signed-off-by: Tyler Hicks Acked-by: Christian Boltz Acked-by: Seth Arnold ------------------------------------------------------------ revno: 3638 fixes bug: https://launchpad.net/bugs/1521031 committer: Tyler Hicks branch nick: apparmor timestamp: Thu 2017-03-02 21:22:57 +0000 message: utils: Accept parser base and include options in aa-easyprof https://launchpad.net/bugs/1521031 aa-easyprof accepts a list of abstractions to include and, by default, execs apparmor_parser to verify the generated profile including any abstractions. However, aa-easyprof didn't provide the same flexibility as apparmor_parser when it came to where in the filesystem the abstraction files could exist. The parser supports --base (defaulting to /etc/apparmor.d) and --Include (defaulting to unset) options to specify the search paths for abstraction files. This patch adds the same options to aa-easyprof to aide in two different situations: 1) Some Ubuntu packages use aa-easyprof to generate AppArmor profiles at build time. Something that has been previously needed is a way for those packages to ship their own abstractions file(s) that are #included in the easyprof-generated profile. That's not been possible since the abstraction file(s) have not yet been installed during the package build. 2) The test-aa-easyprof.py script contains some tests that specify abstractions that should be #included. Without the ability to specify a different --base or --Include directory, the abstractions were required to be present in /etc/apparmor.d/abstractions/ or the tests would fail. This prevents the Python utils from being able to strictly test against in-tree code/profiles/etc. I don't like the names of the command line options --base and --Include. They're not particularly descriptive and the capital 'I' is not user friendly. However, I decided to preserve the name of the options from apparmor_parser. Signed-off-by: Tyler Hicks Acked-by: Christian Boltz Acked-by: Seth Arnold ------------------------------------------------------------ revno: 3637 committer: Tyler Hicks branch nick: apparmor timestamp: Thu 2017-03-02 21:21:53 +0000 message: utils: Require apparmor.aa users to call init_aa() Introduce an apparmor.aa.init_aa() method and move the initialization code of the apparmor.aa module into it. Note that this change will break any external users of apparmor.aa because global variables that were previously initialized when importing apparmor.aa will not be initialized unless a call to the new apparmor.aa.init_aa() method is made. The main purpose of this change is to allow the utils tests to be able to set a non-default location for configuration files. Instead of hard-coding the location of logprof.conf and other utils related configuration files to /etc/apparmor/, this patch allows it to be configured by calling apparmor.aa.init_aa(confdir=PATH). This allows for the make check target to use the in-tree config file, profiles, and parser by default. A helper method, setup_aa(), is added to common_test.py that checks for an environment variable containing a non-default configuration directory path prior to calling apparmor.aa.init_aa(). All test scripts that use apparmor.aa are updated to call setup_aa(). Signed-off-by: Tyler Hicks Suggested-by: Christian Boltz Acked-by: Seth Arnold Acked-by: Christian Boltz ------------------------------------------------------------ revno: 3636 committer: Tyler Hicks branch nick: apparmor timestamp: Thu 2017-03-02 21:21:21 +0000 message: utils: Update the logprof.conf in the test dir to point to in-tree paths The utils tests should make use of the logprof.conf that resides in utils/test/ when testing against the in-tree parser and profiles. When testing against the system, it the utils tests should continue to use the system logprof.conf. This patch updates the parser and profiles paths to point to the in-tree paths. Another patch is needed to get aa.py to honor a non-hardcoded search path for logprof.conf and other configuration files. Signed-off-by: Tyler Hicks Acked-by: Christian Boltz Acked-by: Seth Arnold ------------------------------------------------------------ revno: 3635 committer: Tyler Hicks branch nick: apparmor timestamp: Thu 2017-03-02 21:20:45 +0000 message: utils: Improve error messages when profiles/parser is not found When aa.py is imported, it looks for a set of profiles and it also looks for the parser. Both of these paths are configured by logprof.conf but it isn't always obvious which logprof.conf file was used and, therefore, it isn't always obvious where aa.py is looking. This patch includes the paths in the error messages. Signed-off-by: Tyler Hicks Acked-by: Christian Boltz Acked-by: Seth Arnold ------------------------------------------------------------ revno: 3634 fixes bug: https://launchpad.net/bugs/1628286 committer: Tyler Hicks branch nick: apparmor timestamp: Tue 2017-02-28 23:04:24 +0000 message: utils: Don't enforce ordering of dbus rule attributes https://launchpad.net/bugs/1628286 The utils were enforcing that the dbus rule attributes were strictly ordered in the following fashion: bus -> path -> interface -> member -> peer However, the parser has always accepted the attributes in any order. If the system contained a profile which did not use the strict ordering enforced by the utils, the utils would refuse to operate at all. This patch eases the restriction on the ordering at the expense of the utils no longer being able to detect and reject a single attribute that is repeated multiple times. In that situation, only the last occurrence of the attribute will be honored by the utils. Signed-off-by: Tyler Hicks Acked-by: Christian Boltz ------------------------------------------------------------ revno: 3633 committer: Tyler Hicks branch nick: apparmor timestamp: Tue 2017-02-28 23:03:25 +0000 message: utils: Fix failing tests in test-aa.py The merged /usr patches to the policy broke some utils tests due to a change in the expected output. Fixes: r3600 update lots of profiles for usrMerge Signed-off-by: Tyler Hicks Acked-by: Christian Boltz Acked-by: Seth Arnold ------------------------------------------------------------ revno: 3632 committer: Christian Boltz branch nick: apparmor timestamp: Thu 2017-02-23 01:00:36 +0100 message: Ignore change_hat events with error=-1 and "unconfined can not change_hat" That's much better than crashing aa-logprof ;-) (use the log line in the added testcase if you want to see the crash) Reported by pfak on IRC. Acked-by: Seth Arnold for trunk, 2.10 and 2.9. ------------------------------------------------------------ revno: 3631 committer: Christian Boltz branch nick: apparmor timestamp: Tue 2017-02-21 18:46:36 +0100 message: Remove re.LOCALE flag Starting with python 3.6, the re.LOCALE flag can only be used with byte patterns, and errors out if used with str. This patch removes the flag in get_translated_hotkey(). References: https://bugs.launchpad.net/apparmor/+bug/1661766 Acked-by: Steve Beattie for trunk, 2.10 and 2.9 ------------------------------------------------------------ revno: 3630 committer: Steve Beattie branch nick: apparmor timestamp: Wed 2017-02-01 21:41:52 -0800 message: regression tests: fix environ fail case In the environ regression test, when the exec() of the child process fails, we don't report FAIL to stdout, so the regression tests consider it an error rather than a failure and abort, short-circuiting the test script. This commit fixes this by emitting the FAIL message when the result from the wait() syscall indicates the child process did not succeed. Signed-off-by: Steve Beattie Acked-by: Seth Arnold ------------------------------------------------------------ revno: 3629 committer: Christian Boltz branch nick: apparmor timestamp: Mon 2017-01-30 20:48:50 +0100 message: Rename global variable "pid" to "log_pid" aa.py has a global variable "pid", but it also has several functions that use "pid" as a local variable name. do_logprof_pass() even uses both - first, it passes the global variable to ReadLog, and then it creates a local variable in the "for pid in ..." loop. This patch renames the global variable to log_pid to get rid of the confusion. Note that the global variable is only handed over to ReadLog, and the only case where its previous content _might_ be used is aa-genprof which does multipe do_logprof_pass() runs. Maybe we could even get rid of this variable in aa.py and make it local to the ReadLog class, but I'm not sure if that would affect aa-genprof in interesting[tm] ways. Acked-by: John Johansen ------------------------------------------------------------ Use --include-merged or -n0 to see merged revisions. === added file 'libraries/libapparmor/testsuite/test_multi/unconfined-change_hat.err' === added file 'libraries/libapparmor/testsuite/test_multi/unconfined-change_hat.in' --- libraries/libapparmor/testsuite/test_multi/unconfined-change_hat.in 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/unconfined-change_hat.in 2017-02-23 00:00:36 +0000 @@ -0,0 +1,1 @@ +Feb 21 23:22:01 mail-20170118 kernel: [1222198.459750] audit: type=1400 audit(1487719321.954:218): apparmor="ALLOWED" operation="change_hat" info="unconfined can not change_hat" error=-1 profile="unconfined" pid=19941 comm="apache2" === added file 'libraries/libapparmor/testsuite/test_multi/unconfined-change_hat.out' --- libraries/libapparmor/testsuite/test_multi/unconfined-change_hat.out 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/unconfined-change_hat.out 2017-02-23 00:00:36 +0000 @@ -0,0 +1,12 @@ +START +File: unconfined-change_hat.in +Event type: AA_RECORD_ALLOWED +Audit ID: 1487719321.954:218 +Operation: change_hat +Profile: unconfined +Command: apache2 +Info: unconfined can not change_hat +ErrorCode: 1 +PID: 19941 +Epoch: 1487719321 +Audit subid: 218 === added file 'libraries/libapparmor/testsuite/test_multi/unconfined-change_hat.profile' --- libraries/libapparmor/testsuite/test_multi/unconfined-change_hat.profile 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/unconfined-change_hat.profile 2017-02-23 00:00:36 +0000 @@ -0,0 +1,2 @@ +profile unconfined { +} === modified file 'parser/libapparmor_re/expr-tree.h' --- parser/libapparmor_re/expr-tree.h 2016-01-19 23:07:04 +0000 +++ parser/libapparmor_re/expr-tree.h 2017-03-21 19:09:59 +0000 @@ -672,7 +672,7 @@ ~hashedNodeVec() { - delete nodes; + delete [] nodes; } unsigned long size()const { return len; } === modified file 'parser/rc.apparmor.functions' --- parser/rc.apparmor.functions 2012-02-24 12:21:59 +0000 +++ parser/rc.apparmor.functions 2017-03-24 05:06:07 +0000 @@ -451,34 +451,7 @@ configure_owlsm parse_profiles reload - # Clean out running profiles not associated with the current profile - # set, excluding the libvirt dynamically generated profiles. - # Note that we reverse sort the list of profiles to remove to - # ensure that child profiles (e.g. hats) are removed before the - # parent. We *do* need to remove the child profile and not rely - # on removing the parent profile when the profile has had its - # child profile names changed. - profiles_names_list | awk ' -BEGIN { - while (getline < "'${SFS_MOUNTPOINT}'/profiles" ) { - str = sub(/ \((enforce|complain)\)$/, "", $0); - if (match($0, /^libvirt-[0-9a-f\-]+$/) == 0) - arr[$str] = $str - } -} - -{ if (length(arr[$0]) > 0) { delete arr[$0] } } - -END { - for (key in arr) - if (length(arr[key]) > 0) { - printf("%s\n", arr[key]) - } -} -' | LC_COLLATE=C sort -r | while IFS= read profile ; do - echo -n "$profile" > "$SFS_MOUNTPOINT/.remove" - done - # will not catch all errors, but still better than nothing + rc=$? aa_log_end_msg $rc return $rc === modified file 'profiles/apparmor.d/abstractions/nvidia' --- profiles/apparmor.d/abstractions/nvidia 2014-06-06 18:50:58 +0000 +++ profiles/apparmor.d/abstractions/nvidia 2017-03-06 18:59:43 +0000 @@ -8,8 +8,9 @@ /etc/vdpau_wrapper.cfg r, # device files - /dev/nvidia0 rw, - /dev/nvidiactl rw, + /dev/nvidiactl rw, + /dev/nvidia-modeset rw, + /dev/nvidia[0-9]* rw, @{PROC}/interrupts r, @{PROC}/sys/vm/max_map_count r, @@ -18,3 +19,5 @@ owner @{HOME}/.nv/GLCache/ r, owner @{HOME}/.nv/GLCache/** rwk, + + unix (send, receive) type=dgram peer=(addr="@nvidia[0-9a-f]*"), === modified file 'tests/regression/apparmor/environ.c' --- tests/regression/apparmor/environ.c 2010-12-20 20:29:10 +0000 +++ tests/regression/apparmor/environ.c 2017-02-02 05:41:52 +0000 @@ -63,6 +63,8 @@ if (retval == RET_CHLD_SUCCESS) { printf("PASS\n"); retval = 0; + } else { + printf("FAIL: Child failed\n"); } } else if (pid == 0) { === modified file 'utils/Makefile' --- utils/Makefile 2016-12-10 18:25:31 +0000 +++ utils/Makefile 2017-03-24 05:08:01 +0000 @@ -24,7 +24,7 @@ PYTOOLS = aa-easyprof aa-genprof aa-logprof aa-cleanprof aa-mergeprof \ aa-autodep aa-audit aa-complain aa-enforce aa-disable \ aa-status aa-unconfined -TOOLS = ${PERLTOOLS} ${PYTOOLS} aa-decode +TOOLS = ${PERLTOOLS} ${PYTOOLS} aa-decode aa-remove-unknown PYSETUP = python-tools-setup.py PYMODULES = $(wildcard apparmor/*.py apparmor/rule/*.py) === modified file 'utils/aa-easyprof.pod' --- utils/aa-easyprof.pod 2015-03-27 21:33:35 +0000 +++ utils/aa-easyprof.pod 2017-03-02 21:24:05 +0000 @@ -57,6 +57,12 @@ AppArmor rules or policies. They are similar to AppArmor abstractions, but usually encompass more policy rules. +=item --parser PATH + +Specify the PATH of the apparmor_parser binary to use when verifying +policy. If this option is not specified, aa-easyprof will attempt to +locate the path starting with /sbin/apparmor_parser. + =item -a ABSTRACTIONS, --abstractions=ABSTRACTIONS Specify ABSTRACTIONS as a comma-separated list of AppArmor abstractions. It is @@ -64,6 +70,16 @@ convenience. AppArmor abstractions are located in /etc/apparmor.d/abstractions. See apparmor.d(5) for details. +=item -b PATH, --base=PATH + +Set the base PATH for resolving abstractions specified by --abstractions. +See the same option in apparmor_parser(8) for details. + +=item -I PATH, --Include=PATH + +Add PATH to the search paths used for resolving abstractions specified by +--abstractions. See the same option in apparmor_parser(8) for details. + =item -r PATH, --read-path=PATH Specify a PATH to allow owner reads. May be specified multiple times. If the === modified file 'utils/aa-genprof' --- utils/aa-genprof 2016-10-01 18:57:09 +0000 +++ utils/aa-genprof 2017-03-02 21:21:53 +0000 @@ -66,6 +66,7 @@ profiling = args.program profiledir = args.dir +apparmor.init_aa() apparmor.set_logfile(args.file) aa_mountpoint = apparmor.check_for_apparmor() === modified file 'utils/aa-logprof' --- utils/aa-logprof 2016-10-01 18:57:09 +0000 +++ utils/aa-logprof 2017-03-02 21:21:53 +0000 @@ -34,6 +34,7 @@ profiledir = args.dir logmark = args.mark or '' +apparmor.init_aa() apparmor.set_logfile(args.file) aa_mountpoint = apparmor.check_for_apparmor() === modified file 'utils/aa-mergeprof' --- utils/aa-mergeprof 2017-01-19 15:54:47 +0000 +++ utils/aa-mergeprof 2017-03-02 21:21:53 +0000 @@ -43,6 +43,8 @@ args.other = None +apparmor.aa.init_aa() + profiles = args.files profiledir = args.dir === added file 'utils/aa-remove-unknown' --- utils/aa-remove-unknown 1970-01-01 00:00:00 +0000 +++ utils/aa-remove-unknown 2017-03-24 05:08:01 +0000 @@ -0,0 +1,108 @@ +#!/bin/sh +# ---------------------------------------------------------------------- +# Copyright (c) 2017 Canonical Ltd. (All rights reserved) +# +# 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. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# ---------------------------------------------------------------------- + +APPARMOR_FUNCTIONS=/lib/apparmor/rc.apparmor.functions +APPARMORFS=/sys/kernel/security/apparmor +PROFILES="${APPARMORFS}/profiles" +REMOVE="${APPARMORFS}/.remove" + +DRY_RUN=0 + +. $APPARMOR_FUNCTIONS + +usage() { + local progname="$1" + local rc="$2" + local msg="usage: ${progname} [options]\n +Remove profiles unknown to the system + +Options: + -h, --help Show this help message and exit + -n Dry run; don't remove profiles" + + if [ "$rc" -ne 0 ] ; then + echo "$msg" 1>&2 + else + echo "$msg" + fi + + exit "$rc" +} + +if [ "$#" -gt 1 ] ; then + usage "$0" 1 +elif [ "$#" -eq 1 ] ; then + if [ "$1" = "-h" -o "$1" = "--help" ] ; then + usage "$0" 0 + elif [ "$1" = "-n" ] ; then + DRY_RUN=1 + else + usage "$0" 1 + fi +fi + + +# We can't use a -r test here because while $PROFILES is world-readable, +# apparmorfs may still return EACCES from open() +# +# We have to do this check because error checking awk's getline() below is +# tricky and, as is, results in an infinite loop when apparmorfs returns an +# error from open(). +if ! IFS= read line < "$PROFILES" ; then + echo "ERROR: Unable to read apparmorfs profiles file" 1>&2 + exit 1 +elif [ ! -w "$REMOVE" ] ; then + echo "ERROR: Unable to write to apparmorfs remove file" 1>&2 + exit 1 +fi + +# Clean out running profiles not associated with the current profile +# set, excluding the libvirt dynamically generated profiles. +# Note that we reverse sort the list of profiles to remove to +# ensure that child profiles (e.g. hats) are removed before the +# parent. We *do* need to remove the child profile and not rely +# on removing the parent profile when the profile has had its +# child profile names changed. +profiles_names_list | awk ' +BEGIN { + while (getline < "'${PROFILES}'" ) { + str = sub(/ \((enforce|complain)\)$/, "", $0); + if (match($0, /^libvirt-[0-9a-f\-]+$/) == 0) + arr[$str] = $str + } +} + +{ if (length(arr[$0]) > 0) { delete arr[$0] } } + +END { + for (key in arr) + if (length(arr[key]) > 0) { + printf("%s\n", arr[key]) + } +} +' | LC_COLLATE=C sort -r | \ + while IFS= read profile ; do + if [ "$DRY_RUN" -ne 0 ]; then + echo "Would remove '${profile}'" + else + echo "Removing '${profile}'" + echo -n "$profile" > "${REMOVE}" + fi + done + +# will not catch all errors, but still better than nothing +exit $? === added file 'utils/aa-remove-unknown.pod' --- utils/aa-remove-unknown.pod 1970-01-01 00:00:00 +0000 +++ utils/aa-remove-unknown.pod 2017-03-24 05:08:01 +0000 @@ -0,0 +1,51 @@ +=pod + +=head1 NAME + +aa-remove-unknown - remove unknown AppArmor profiles + +=head1 SYNOPSIS + +B [option] + +=head1 DESCRIPTION + +B will inventory all profiles in /etc/apparmor.d/, compare +that list to the profiles currently loaded into the kernel, and then remove all +of the loaded profiles that were not found in /etc/apparmor.d/. It will also +report the name of each profile that it removes on standard out. + +=head1 OPTIONS + +=over 4 + +=item -h, --help + +displays a short usage statement. + +=item -n + +dry run; only prints the names of profiles that would be removed + +=back + +=head1 EXAMPLES + + $ sudo ./aa-remove-unknown -n + Would remove 'test//null-/usr/bin/whoami' + Would remove 'test' + + $ sudo ./aa-remove-unknown + Removing 'test//null-/usr/bin/whoami' + Removing 'test' + +=head1 BUGS + +None. Please report any you find to Launchpad at +L. + +=head1 SEE ALSO + +apparmor(7) + +=cut === modified file 'utils/aa-unconfined' --- utils/aa-unconfined 2016-12-30 20:22:58 +0000 +++ utils/aa-unconfined 2017-03-02 21:21:53 +0000 @@ -40,6 +40,7 @@ paranoid = args.paranoid +aa.init_aa() aa_mountpoint = aa.check_for_apparmor() if not aa_mountpoint: raise aa.AppArmorException(_("It seems AppArmor was not started. Please enable AppArmor and try again.")) === modified file 'utils/apparmor/aa.py' --- utils/apparmor/aa.py 2017-01-20 00:20:41 +0000 +++ utils/apparmor/aa.py 2017-03-03 12:14:55 +0000 @@ -73,14 +73,14 @@ # Setup logging incase of debugging is enabled debug_logger = DebugLogger('aa') -CONFDIR = '/etc/apparmor' - # The database for severity sev_db = None # The file to read log messages from ### Was our logfile = None +CONFDIR = None +conf = None cfg = None repo_cfg = None @@ -105,7 +105,7 @@ extras = hasher() # Inactive profiles from extras ### end our log = [] -pid = dict() +log_pid = dict() # handed over to ReadLog, gets filled in logparser.py. The only case the previous content of this variable _might_(?) be used is aa-genprof (multiple do_logprof_pass() runs) profile_changes = hasher() prelog = hasher() @@ -1857,7 +1857,7 @@ elif os.path.isdir(logfile): raise AppArmorException(_('%s is a directory. Please specify a file as logfile') % logfile) -def do_logprof_pass(logmark='', passno=0, pid=pid): +def do_logprof_pass(logmark='', passno=0, log_pid=log_pid): # set up variables for this pass # transitions = hasher() global log @@ -1885,7 +1885,7 @@ ## if not repo_cfg['repository'].get('enabled', False) or repo_cfg['repository]['enabled'] not in ['yes', 'no']: ## UI_ask_to_enable_repo() - log_reader = apparmor.logparser.ReadLog(pid, logfile, existing_profiles, profile_dir, log) + log_reader = apparmor.logparser.ReadLog(log_pid, logfile, existing_profiles, profile_dir, log) log = log_reader.read_log(logmark) #read_log(logmark) @@ -3741,24 +3741,36 @@ ######Initialisations###### -conf = apparmor.config.Config('ini', CONFDIR) -cfg = conf.read_config('logprof.conf') - -# prevent various failures if logprof.conf doesn't exist -if not cfg.sections(): - cfg.add_section('settings') - cfg.add_section('required_hats') - -if cfg['settings'].get('default_owner_prompt', False): - cfg['settings']['default_owner_prompt'] = '' - -profile_dir = conf.find_first_dir(cfg['settings'].get('profiledir')) or '/etc/apparmor.d' -if not os.path.isdir(profile_dir): - raise AppArmorException('Can\'t find AppArmor profiles') - -extra_profile_dir = conf.find_first_dir(cfg['settings'].get('inactive_profiledir')) or '/usr/share/apparmor/extra-profiles/' - -parser = conf.find_first_file(cfg['settings'].get('parser')) or '/sbin/apparmor_parser' -if not os.path.isfile(parser) or not os.access(parser, os.EX_OK): - raise AppArmorException('Can\'t find apparmor_parser') +def init_aa(confdir="/etc/apparmor"): + global CONFDIR + global conf + global cfg + global profile_dir + global extra_profile_dir + global parser + + if CONFDIR: + return # config already initialized (and possibly changed afterwards), so don't overwrite the config variables + + CONFDIR = confdir + conf = apparmor.config.Config('ini', CONFDIR) + cfg = conf.read_config('logprof.conf') + + # prevent various failures if logprof.conf doesn't exist + if not cfg.sections(): + cfg.add_section('settings') + cfg.add_section('required_hats') + + if cfg['settings'].get('default_owner_prompt', False): + cfg['settings']['default_owner_prompt'] = '' + + profile_dir = conf.find_first_dir(cfg['settings'].get('profiledir')) or '/etc/apparmor.d' + if not os.path.isdir(profile_dir): + raise AppArmorException('Can\'t find AppArmor profiles in %s' % (profile_dir)) + + extra_profile_dir = conf.find_first_dir(cfg['settings'].get('inactive_profiledir')) or '/usr/share/apparmor/extra-profiles/' + + parser = conf.find_first_file(cfg['settings'].get('parser')) or '/sbin/apparmor_parser' + if not os.path.isfile(parser) or not os.access(parser, os.EX_OK): + raise AppArmorException('Can\'t find apparmor_parser at %s' % (parser)) === modified file 'utils/apparmor/cleanprofile.py' --- utils/apparmor/cleanprofile.py 2016-10-01 17:54:48 +0000 +++ utils/apparmor/cleanprofile.py 2017-03-02 21:21:53 +0000 @@ -16,6 +16,7 @@ class Prof(object): def __init__(self, filename): + apparmor.init_aa() self.aa = apparmor.aa self.filelist = apparmor.filelist self.include = apparmor.include === modified file 'utils/apparmor/easyprof.py' --- utils/apparmor/easyprof.py 2015-03-28 12:16:22 +0000 +++ utils/apparmor/easyprof.py 2017-03-02 21:24:05 +0000 @@ -259,14 +259,11 @@ return orig -def verify_policy(policy): +def verify_policy(policy, exe, base=None, include=None): '''Verify policy compiles''' - exe = "/sbin/apparmor_parser" - if not os.path.exists(exe): - rc, exe = cmd(['which', 'apparmor_parser']) - if rc != 0: - warn("Could not find apparmor_parser. Skipping verify") - return True + if not exe: + warn("Could not find apparmor_parser. Skipping verify") + return True fn = "" # if policy starts with '/' and is one line, assume it is a path @@ -279,7 +276,14 @@ os.write(f, policy) os.close(f) - rc, out = cmd([exe, '-QTK', fn]) + command = [exe, '-QTK'] + if base: + command.extend(['-b', base]) + if include: + command.extend(['-I', include]) + command.append(fn) + + rc, out = cmd(command) os.unlink(fn) if rc == 0: return True @@ -302,6 +306,22 @@ if os.path.isfile(self.conffile): self._get_defaults() + self.parser_path = '/sbin/apparmor_parser' + if opt.parser_path: + self.parser_path = opt.parser_path + elif not os.path.exists(self.parser_path): + rc, self.parser_path = cmd(['which', 'apparmor_parser']) + if rc != 0: + self.parser_path = None + + self.parser_base = "/etc/apparmor.d" + if opt.parser_base: + self.parser_base = opt.parser_base + + self.parser_include = None + if opt.parser_include: + self.parser_include = opt.parser_include + if opt.templates_dir and os.path.isdir(opt.templates_dir): self.dirs['templates'] = os.path.abspath(opt.templates_dir) elif not opt.templates_dir and \ @@ -350,8 +370,6 @@ if not 'policygroups' in self.dirs: raise AppArmorException("Could not find policygroups directory") - self.aa_topdir = "/etc/apparmor.d" - self.binary = binary if binary: if not valid_binary_path(binary): @@ -506,9 +524,15 @@ def gen_abstraction_rule(self, abstraction): '''Generate an abstraction rule''' - p = os.path.join(self.aa_topdir, "abstractions", abstraction) - if not os.path.exists(p): - raise AppArmorException("%s does not exist" % p) + base = os.path.join(self.parser_base, "abstractions", abstraction) + if not os.path.exists(base): + if not self.parser_include: + raise AppArmorException("%s does not exist" % base) + + include = os.path.join(self.parser_include, "abstractions", abstraction) + if not os.path.exists(include): + raise AppArmorException("Neither %s nor %s exist" % (base, include)) + return "#include " % abstraction def gen_variable_declaration(self, dec): @@ -661,7 +685,7 @@ if no_verify: debug("Skipping policy verification") - elif not verify_policy(policy): + elif not verify_policy(policy, self.parser_path, self.parser_base, self.parser_include): msg("\n" + policy) raise AppArmorException("Invalid policy") @@ -804,6 +828,10 @@ def add_parser_policy_args(parser): '''Add parser arguments''' + parser.add_option("--parser", + dest="parser_path", + help="The path to the profile parser used for verification", + metavar="PATH") parser.add_option("-a", "--abstractions", action="callback", callback=check_for_manifest_arg, @@ -811,6 +839,14 @@ dest="abstractions", help="Comma-separated list of abstractions", metavar="ABSTRACTIONS") + parser.add_option("-b", "--base", + dest="parser_base", + help="Set the base directory for resolving abstractions", + metavar="DIR") + parser.add_option("-I", "--Include", + dest="parser_include", + help="Add a directory to the search path when resolving abstractions", + metavar="DIR") parser.add_option("--read-path", action="callback", callback=check_for_manifest_arg_append, === modified file 'utils/apparmor/logparser.py' --- utils/apparmor/logparser.py 2016-12-06 21:24:56 +0000 +++ utils/apparmor/logparser.py 2017-02-23 00:00:36 +0000 @@ -243,6 +243,8 @@ if e['operation'] == 'change_hat': if aamode != 'HINT' and aamode != 'PERMITTING': return None + if e['error_code'] == 1 and e['info'] == 'unconfined can not change_hat': + return None profile = e['name2'] #hat = None if '//' in e['name2']: === modified file 'utils/apparmor/rule/dbus.py' --- utils/apparmor/rule/dbus.py 2016-10-01 18:08:10 +0000 +++ utils/apparmor/rule/dbus.py 2017-02-28 23:04:24 +0000 @@ -37,14 +37,16 @@ RE_FLAG = '(?P<%s>(\S+|"[^"]+"|\(\s*\S+\s*\)|\(\s*"[^"]+"\)\s*))' # string without spaces, or quoted string, optionally wrapped in (...). %s is the match group name # plaintext version: | * | "* " | ( * ) | ( " * " ) | +# XXX this regex will allow repeated parameters, last one wins +# XXX (the parser will reject such rules) RE_DBUS_DETAILS = re.compile( '^' + '(\s+(?P' + RE_ACCESS_KEYWORDS + '))?' + # optional access keyword(s) - '(\s+(bus\s*=\s*' + RE_FLAG % 'bus' + '))?' + # optional bus= system | session | AARE, (...) optional - '(\s+(path\s*=\s*' + RE_FLAG % 'path' + '))?' + # optional path=AARE, (...) optional - '(\s+(name\s*=\s*' + RE_FLAG % 'name' + '))?' + # optional name=AARE, (...) optional - '(\s+(interface\s*=\s*' + RE_FLAG % 'interface' + '))?' + # optional interface=AARE, (...) optional - '(\s+(member\s*=\s*' + RE_FLAG % 'member' + '))?' + # optional member=AARE, (...) optional + '((\s+(bus\s*=\s*' + RE_FLAG % 'bus' + '))?|' + # optional bus= system | session | AARE, (...) optional + '(\s+(path\s*=\s*' + RE_FLAG % 'path' + '))?|' + # optional path=AARE, (...) optional + '(\s+(name\s*=\s*' + RE_FLAG % 'name' + '))?|' + # optional name=AARE, (...) optional + '(\s+(interface\s*=\s*' + RE_FLAG % 'interface' + '))?|' + # optional interface=AARE, (...) optional + '(\s+(member\s*=\s*' + RE_FLAG % 'member' + '))?|' + # optional member=AARE, (...) optional '(\s+(peer\s*=\s*\((,|\s)*' + # optional peer=( name=AARE and/or label=AARE ), (...) required '(' + '(' + '(,|\s)*' + ')' + # empty peer=() @@ -57,7 +59,7 @@ '|' # or '(' + 'label\s*=\s*' + RE_PROFILE_NAME % 'peerlabel3' + '(,|\s)+' + 'name\s*=\s*' + RE_PROFILE_NAME % 'peername3' + ')' + # peer label + name (match name peername3/peerlabel3) ')' - '(,|\s)*\)))?' + '(,|\s)*\)))?){0,6}' '\s*$') === modified file 'utils/apparmor/tools.py' --- utils/apparmor/tools.py 2015-06-06 12:31:03 +0000 +++ utils/apparmor/tools.py 2017-03-03 12:14:55 +0000 @@ -24,6 +24,8 @@ class aa_tools: def __init__(self, tool_name, args): + apparmor.init_aa() + self.name = tool_name self.profiledir = args.dir self.profiling = args.program === modified file 'utils/apparmor/ui.py' --- utils/apparmor/ui.py 2016-10-03 19:01:29 +0000 +++ utils/apparmor/ui.py 2017-02-21 17:46:36 +0000 @@ -64,8 +64,8 @@ msg = 'PromptUser: ' + _('Invalid hotkey for') # Originally (\S) was used but with translations it would not work :( - if re.search('\((\S+)\)', translated, re.LOCALE): - return re.search('\((\S+)\)', translated, re.LOCALE).groups()[0] + if re.search('\((\S+)\)', translated): + return re.search('\((\S+)\)', translated).groups()[0] else: if cmsg: raise AppArmorException(cmsg) === modified file 'utils/test/Makefile' --- utils/test/Makefile 2016-05-10 12:31:25 +0000 +++ utils/test/Makefile 2017-03-02 21:24:33 +0000 @@ -23,11 +23,17 @@ ifdef USE_SYSTEM LD_LIBRARY_PATH= PYTHONPATH= + CONFDIR= + BASEDIR= + PARSER= else # PYTHON_DIST_BUILD_PATH based on libapparmor/swig/python/test/Makefile.am PYTHON_DIST_BUILD_PATH = ../../libraries/libapparmor/swig/python/build/$$($(PYTHON) -c "import distutils.util; import platform; print(\"lib.%s-%s\" %(distutils.util.get_platform(), platform.python_version()[:3]))") LD_LIBRARY_PATH=../../libraries/libapparmor/src/.libs/ PYTHONPATH=..:$(PYTHON_DIST_BUILD_PATH) + CONFDIR=$(CURDIR) + BASEDIR=../../profiles/apparmor.d + PARSER=../../parser/apparmor_parser endif .PHONY: __libapparmor @@ -62,10 +68,10 @@ rm -rf __pycache__/ .coverage htmlcov check: __libapparmor - export PYTHONPATH=$(PYTHONPATH) ; export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) ; export LC_ALL=C; $(foreach test, $(wildcard test-*.py), echo ; echo === $(test) === ; $(call pyalldo, $(test))) + export PYTHONPATH=$(PYTHONPATH) LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) LC_ALL=C __AA_CONFDIR=$(CONFDIR) __AA_BASEDIR=$(BASEDIR) __AA_PARSER=$(PARSER) ; $(foreach test, $(wildcard test-*.py), echo ; echo === $(test) === ; $(call pyalldo, $(test))) .coverage: $(wildcard ../aa-* ../apparmor/*.py test-*.py) __libapparmor - export PYTHONPATH=$(PYTHONPATH) ; export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH); export LC_ALL=C; $(COVERAGE_IGNORE_FAILURES_CMD) ; $(foreach test, $(wildcard test-*.py), echo ; echo === $(test) === ; $(PYTHON) -m coverage run --branch -p $(test); ) + export PYTHONPATH=$(PYTHONPATH) LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) LC_ALL=C __AA_CONFDIR=$(CONFDIR) __AA_BASEDIR=$(BASEDIR) __AA_PARSER=$(PARSER) ; $(COVERAGE_IGNORE_FAILURES_CMD) ; $(foreach test, $(wildcard test-*.py), echo ; echo === $(test) === ; $(PYTHON) -m coverage run --branch -p $(test); ) $(PYTHON) -m coverage combine coverage: .coverage === modified file 'utils/test/common_test.py' --- utils/test/common_test.py 2016-05-23 21:15:19 +0000 +++ utils/test/common_test.py 2017-03-02 21:21:53 +0000 @@ -103,6 +103,17 @@ stub_test.__doc__ = "test '%s': %s" % (line, desc) setattr(test_class, 'test_%d' % (i), stub_test) +def setup_aa(aa): + confdir = os.getenv('__AA_CONFDIR') + try: + if confdir: + aa.init_aa(confdir=confdir) + else: + aa.init_aa() + except AttributeError: + # apparmor.aa module versions <= 2.11 do not have the init_aa() method + pass + def write_file(directory, file, contents): '''construct path, write contents to it, and return the constructed path''' path = os.path.join(directory, file) === modified file 'utils/test/logprof.conf' --- utils/test/logprof.conf 2014-08-21 00:14:24 +0000 +++ utils/test/logprof.conf 2017-03-02 21:21:21 +0000 @@ -10,11 +10,11 @@ # ------------------------------------------------------------------ [settings] - profiledir = /etc/apparmor.d /etc/subdomain.d - inactive_profiledir = /usr/share/doc/apparmor-profiles/extras + profiledir = ../../profiles/apparmor.d + inactive_profiledir = ../../profiles/apparmor/profiles/extra logfiles = /var/log/audit/audit.log /var/log/syslog /var/log/messages - parser = /sbin/apparmor_parser /sbin/subdomain_parser + parser = ../../parser/apparmor_parser ldd = /usr/bin/ldd logger = /bin/logger /usr/bin/logger === modified file 'utils/test/minitools_test.py' --- utils/test/minitools_test.py 2015-06-26 23:29:46 +0000 +++ utils/test/minitools_test.py 2017-03-02 21:21:53 +0000 @@ -16,7 +16,7 @@ import subprocess import sys import unittest -from common_test import AATest, setup_all_loops +from common_test import AATest, setup_all_loops, setup_aa import apparmor.aa as apparmor from common_test import read_file @@ -156,6 +156,7 @@ self.assertEqual(exp_content, real_content, 'Failed to cleanup profile properly') +setup_aa(apparmor) setup_all_loops(__name__) if __name__ == '__main__': unittest.main(verbosity=2) === modified file 'utils/test/test-aa-easyprof.py' --- utils/test/test-aa-easyprof.py 2016-10-01 18:57:09 +0000 +++ utils/test/test-aa-easyprof.py 2017-03-02 21:25:01 +0000 @@ -18,6 +18,8 @@ import tempfile import unittest +import apparmor.easyprof as easyprof + topdir = None debugging = False @@ -163,6 +165,23 @@ self.binary = "/opt/bin/foo" self.full_args = ['-c', self.conffile, self.binary] + # Check __AA_BASEDIR, which may be set by the Makefile, to see if + # we should use a non-default base directory path to find + # abstraction files + # + # NOTE: Individual tests can append another --base path to the + # args list and override a base path set here + base = os.getenv('__AA_BASEDIR') + if base: + self.full_args.append('--base=%s' % base) + + # Check __AA_PARSER, which may be set by the Makefile, to see if + # we should use a non-default apparmor_parser path to verify + # policy + parser = os.getenv('__AA_PARSER') + if parser: + self.full_args.append('--parser=%s' % parser) + if debugging: self.full_args.append('-d') @@ -913,6 +932,94 @@ raise raise Exception ("abstraction '%s' should be invalid" % s) + def _create_tmp_base_dir(self, prefix='', abstractions=[], tunables=[]): + '''Create a temporary base dir layout''' + base_name = 'apparmor.d' + if prefix: + base_name = '%s-%s' % (prefix, base_name) + base_dir = os.path.join(self.tmpdir, base_name) + abstractions_dir = os.path.join(base_dir, 'abstractions') + tunables_dir = os.path.join(base_dir, 'tunables') + + os.mkdir(base_dir) + os.mkdir(abstractions_dir) + os.mkdir(tunables_dir) + + for f in abstractions: + contents = ''' + # Abstraction file for testing + /%s r, +''' % (f) + open(os.path.join(abstractions_dir, f), 'w').write(contents) + + for f in tunables: + contents = ''' +# Tunable file for testing +@{AA_TEST_%s}=foo +''' % (f) + open(os.path.join(tunables_dir, f), 'w').write(contents) + + return base_dir + + def test_genpolicy_abstractions_custom_base(self): + '''Test genpolicy (custom base dir)''' + abstraction = "custom-base-dir-test-abstraction" + # The default template #includes the base abstraction and global + # tunable so we need to create placeholders + base = self._create_tmp_base_dir(abstractions=['base', abstraction], tunables=['global']) + args = ['--abstractions=%s' % abstraction, '--base=%s' % base] + + p = self._gen_policy(extra_args=args) + search = "#include " % abstraction + self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p)) + inv_s = '###ABSTRACTIONS###' + self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p)) + + def test_genpolicy_abstractions_custom_base_bad(self): + '''Test genpolicy (custom base dir - bad base dirs)''' + abstraction = "custom-base-dir-test-abstraction" + bad = [ None, '/etc/apparmor.d', '/' ] + for base in bad: + try: + args = ['--abstractions=%s' % abstraction] + if base: + args.append('--base=%s' % base) + self._gen_policy(extra_args=args) + except easyprof.AppArmorException: + continue + except Exception: + raise + raise Exception ("abstraction '%s' should be invalid" % abstraction) + + def test_genpolicy_abstractions_custom_include(self): + '''Test genpolicy (custom include dir)''' + abstraction = "custom-include-dir-test-abstraction" + # No need to create placeholders for the base abstraction or global + # tunable since we're not adjusting the base directory + include = self._create_tmp_base_dir(abstractions=[abstraction]) + args = ['--abstractions=%s' % abstraction, '--Include=%s' % include] + p = self._gen_policy(extra_args=args) + search = "#include " % abstraction + self.assertTrue(search in p, "Could not find '%s' in:\n%s" % (search, p)) + inv_s = '###ABSTRACTIONS###' + self.assertFalse(inv_s in p, "Found '%s' in :\n%s" % (inv_s, p)) + + def test_genpolicy_abstractions_custom_include_bad(self): + '''Test genpolicy (custom include dir - bad include dirs)''' + abstraction = "custom-include-dir-test-abstraction" + bad = [ None, '/etc/apparmor.d', '/' ] + for include in bad: + try: + args = ['--abstractions=%s' % abstraction] + if include: + args.append('--Include=%s' % include) + self._gen_policy(extra_args=args) + except easyprof.AppArmorException: + continue + except Exception: + raise + raise Exception ("abstraction '%s' should be invalid" % abstraction) + def test_genpolicy_profile_name_bad(self): '''Test genpolicy (profile name - bad values)''' bad = [ @@ -2568,40 +2675,16 @@ # Main # if __name__ == '__main__': - def cleanup(files): - for f in files: - if os.path.exists(f): - os.unlink(f) - absfn = os.path.abspath(sys.argv[0]) topdir = os.path.dirname(os.path.dirname(absfn)) if len(sys.argv) > 1 and (sys.argv[1] == '-d' or sys.argv[1] == '--debug'): debugging = True - created = [] - - # Create the necessary files to import aa-easyprof - init = os.path.join(os.path.dirname(absfn), '__init__.py') - if not os.path.exists(init): - open(init, 'a').close() - created.append(init) - - symlink = os.path.join(os.path.dirname(absfn), 'easyprof.py') - if not os.path.exists(symlink): - os.symlink(os.path.join(topdir, 'apparmor', 'easyprof.py'), symlink) - created.append(symlink) - created.append(symlink + 'c') - - # Now that we have everything we need, import aa-easyprof - import easyprof - # run the tests suite = unittest.TestSuite() suite.addTest(unittest.TestLoader().loadTestsFromTestCase(T)) rc = unittest.TextTestRunner(verbosity=2).run(suite) - cleanup(created) - if not rc.wasSuccessful(): sys.exit(1) === modified file 'utils/test/test-aa.py' --- utils/test/test-aa.py 2016-12-30 23:48:41 +0000 +++ utils/test/test-aa.py 2017-03-02 21:21:53 +0000 @@ -10,7 +10,7 @@ # ------------------------------------------------------------------ import unittest -from common_test import AATest, setup_all_loops +from common_test import AATest, setup_all_loops, setup_aa from common_test import read_file, write_file import os @@ -784,8 +784,8 @@ ('/dev/null', {'allow': {'all': {'r', 'w', 'k'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/dev/null'} }), ('/foo/bar', {'allow': {'all': {'r', 'w'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/foo/bar'} }), # exec perms not included ('/no/thing', {'allow': {'all': set(), 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': set() }), - ('/usr/lib/ispell/', {'allow': {'all': {'r'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/lib/ispell/', '/usr/lib{,32,64}/**'} }), # from abstractions/enchant - ('/usr/lib/aspell/*.so', {'allow': {'all': {'m', 'r'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/lib/aspell/*', '/usr/lib/aspell/*.so', '/usr/lib{,32,64}/**'} }), # from abstractions/aspell via abstractions/enchant + ('/usr/lib/ispell/', {'allow': {'all': {'r'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/lib/ispell/', '/{usr/,}lib{,32,64}/**'} }), # from abstractions/enchant + ('/usr/lib/aspell/*.so', {'allow': {'all': {'m', 'r'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/lib/aspell/*', '/usr/lib/aspell/*.so', '/{usr/,}lib{,32,64}/**'} }), # from abstractions/aspell via abstractions/enchant ] def _run_test(self, params, expected): @@ -820,8 +820,8 @@ (['/usr/share/common-licenses/foo/bar', 'w'], ['/usr/share/common*/foo/* rw,', '/usr/share/common-licenses/** rw,', '/usr/share/common-licenses/foo/bar rw,'] ), (['/dev/null', 'wk'], ['/dev/null rwk,'] ), (['/foo/bar', 'rw'], ['/foo/bar rw,'] ), - (['/usr/lib/ispell/', 'w'], ['/usr/lib{,32,64}/** rw,', '/usr/lib/ispell/ rw,'] ), - (['/usr/lib/aspell/some.so', 'k'], ['/usr/lib/aspell/* mrk,', '/usr/lib/aspell/*.so mrk,', '/usr/lib{,32,64}/** mrk,', '/usr/lib/aspell/some.so mrk,'] ), + (['/usr/lib/ispell/', 'w'], ['/{usr/,}lib{,32,64}/** rw,', '/usr/lib/ispell/ rw,'] ), + (['/usr/lib/aspell/some.so', 'k'], ['/usr/lib/aspell/* mrk,', '/usr/lib/aspell/*.so mrk,', '/{usr/,}lib{,32,64}/** mrk,', '/usr/lib/aspell/some.so mrk,'] ), ] def _run_test(self, params, expected): @@ -855,6 +855,7 @@ proposals = propose_file_rules(profile, rule_obj) self.assertEqual(proposals, expected) +setup_aa(apparmor.aa) setup_all_loops(__name__) if __name__ == '__main__': unittest.main(verbosity=2) === modified file 'utils/test/test-config.py' --- utils/test/test-config.py 2016-10-10 21:27:19 +0000 +++ utils/test/test-config.py 2017-03-02 21:21:21 +0000 @@ -24,7 +24,7 @@ conf = ini_config.read_config('logprof.conf') logprof_sections = ['settings', 'repository', 'qualifiers', 'required_hats', 'defaulthat', 'globs'] logprof_sections_options = ['profiledir', 'inactive_profiledir', 'logfiles', 'parser', 'ldd', 'logger', 'default_owner_prompt', 'custom_includes'] - logprof_settings_parser = '/sbin/apparmor_parser /sbin/subdomain_parser' + logprof_settings_parser = '../../parser/apparmor_parser' self.assertEqual(conf.sections(), logprof_sections) self.assertEqual(conf.options('settings'), logprof_sections_options) === modified file 'utils/test/test-dbus.py' --- utils/test/test-dbus.py 2016-11-19 09:55:03 +0000 +++ utils/test/test-dbus.py 2017-02-28 23:04:24 +0000 @@ -89,6 +89,10 @@ ('dbus peer=(, label = bar, name = foo ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar,', False)), # XXX peerlabel includes the comma ('dbus peer=( label = bar , name = foo ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), ('dbus peer=( label = "bar" name = "foo" ),' , exp(False, False, False, '', None , True , None, True, None, True, None, True, None, True, None, True, 'foo', False, 'bar', False)), + ('dbus path=/foo/bar bus=session,' , exp(False, False, False, '', None , True , 'session', False, '/foo/bar', False, None, True, None, True, None, True, None, True, None, True)), + ('dbus bus=system path=/foo/bar bus=session,' , exp(False, False, False, '', None , True , 'session', False, '/foo/bar', False, None, True, None, True, None, True, None, True, None, True)), # XXX bus= specified twice, last one wins + ('dbus send peer=(label="foo") bus=session,' , exp(False, False, False, '', {'send'}, False, 'session', False, None, True, None, True, None, True, None, True, None, True, 'foo', False)), + ('dbus bus=1 bus=2 bus=3 bus=4 bus=5 bus=6,' , exp(False, False, False, '', None , True , '6', False, None, True, None, True, None, True, None, True, None, True, None, True)), # XXX bus= specified multiple times, last one wins ] def _run_test(self, rawrule, expected): @@ -108,6 +112,8 @@ ('dbus peer=(label=foo) path=,' , AppArmorException), ('dbus (invalid),' , AppArmorException), ('dbus peer=,' , AppArmorException), + ('dbus bus=session bind bus=system,', AppArmorException), + ('dbus bus=1 bus=2 bus=3 bus=4 bus=5 bus=6 bus=7,', AppArmorException), ] def _run_test(self, rawrule, expected): === modified file 'utils/test/test-libapparmor-test_multi.py' --- utils/test/test-libapparmor-test_multi.py 2017-01-19 15:52:38 +0000 +++ utils/test/test-libapparmor-test_multi.py 2017-03-02 21:21:53 +0000 @@ -10,7 +10,7 @@ # ------------------------------------------------------------------ import unittest -from common_test import AATest, setup_all_loops, read_file +from common_test import AATest, setup_all_loops, setup_aa, read_file import os from apparmor.common import open_file_read @@ -267,6 +267,7 @@ TestLibapparmorTestMulti.tests = find_test_multi('../../libraries/libapparmor/testsuite/test_multi/') TestLogToProfile.tests = find_test_multi('../../libraries/libapparmor/testsuite/test_multi/') +setup_aa(apparmor.aa) setup_all_loops(__name__) if __name__ == '__main__': unittest.main(verbosity=1) # reduced verbosity due to the big number of tests === modified file 'utils/test/test-mount_parse.py' --- utils/test/test-mount_parse.py 2016-10-01 18:57:09 +0000 +++ utils/test/test-mount_parse.py 2017-03-02 21:21:53 +0000 @@ -11,7 +11,7 @@ import apparmor.aa as aa import unittest -from common_test import AAParseTest, setup_regex_tests +from common_test import AAParseTest, setup_regex_tests, setup_aa class BaseAAParseMountTest(AAParseTest): def setUp(self): @@ -39,6 +39,7 @@ ('unmount /mnt/external,', 'unmount with mount point'), ] +setup_aa(aa) if __name__ == '__main__': setup_regex_tests(AAParseMountTest) setup_regex_tests(AAParseRemountTest) === modified file 'utils/test/test-parser-simple-tests.py' --- utils/test/test-parser-simple-tests.py 2016-10-01 18:57:09 +0000 +++ utils/test/test-parser-simple-tests.py 2017-03-03 12:14:03 +0000 @@ -10,7 +10,7 @@ # ------------------------------------------------------------------ import unittest -from common_test import AATest, setup_all_loops +from common_test import AATest, setup_all_loops, setup_aa import apparmor.aa as apparmor import os @@ -30,9 +30,6 @@ 'generated_x/ambiguous-', 'generated_x/dominate-', - # permissions before path - 'generated_perms_leading/', - # 'safe' and 'unsafe' keywords 'generated_perms_safe/', @@ -52,6 +49,7 @@ 'change_profile/onx_conflict_unsafe1.sd', 'change_profile/onx_conflict_unsafe2.sd', + 'dbus/bad_modifier_2.sd', 'dbus/bad_regex_01.sd', 'dbus/bad_regex_02.sd', 'dbus/bad_regex_03.sd', @@ -154,6 +152,8 @@ 'vars/vars_dbus_bad_01.sd', 'vars/vars_dbus_bad_02.sd', 'vars/vars_dbus_bad_03.sd', + 'vars/vars_dbus_bad_04.sd', + 'vars/vars_dbus_bad_05.sd', 'vars/vars_dbus_bad_06.sd', 'vars/vars_dbus_bad_07.sd', 'vars/vars_file_evaluation_7.sd', @@ -256,16 +256,75 @@ 'file/ok_5.sd', # Invalid mode UX 'file/ok_2.sd', # Invalid mode RWM 'file/ok_4.sd', # Invalid mode iX + 'xtrans/simple_ok_pix_1.sd', # Invalid mode pIx + 'xtrans/simple_ok_pux_1.sd', # Invalid mode rPux + + # unexpected uppercase vs. lowercase in *x rules - generated_perms_leading directory + 'generated_perms_leading/exact-re-Puxtarget.sd', + 'generated_perms_leading/dominate-ownerCuxtarget2.sd', + 'generated_perms_leading/ambiguous-Cux.sd', + 'generated_perms_leading/dominate-ownerPux.sd', + 'generated_perms_leading/exact-re-ownerPux.sd', + 'generated_perms_leading/overlap-ownerCuxtarget.sd', + 'generated_perms_leading/exact-re-ownerCuxtarget.sd', + 'generated_perms_leading/dominate-Puxtarget2.sd', + 'generated_perms_leading/dominate-ownerCuxtarget.sd', + 'generated_perms_leading/dominate-ownerPuxtarget.sd', + 'generated_perms_leading/ambiguous-Pux.sd', + 'generated_perms_leading/ambiguous-Cuxtarget2.sd', + 'generated_perms_leading/exact-Puxtarget2.sd', + 'generated_perms_leading/ambiguous-ownerCux.sd', + 'generated_perms_leading/exact-ownerPux.sd', + 'generated_perms_leading/ambiguous-ownerPuxtarget.sd', + 'generated_perms_leading/exact-re-ownerPuxtarget.sd', + 'generated_perms_leading/exact-re-Cuxtarget.sd', + 'generated_perms_leading/exact-re-Puxtarget2.sd', + 'generated_perms_leading/dominate-Cux.sd', + 'generated_perms_leading/exact-re-ownerCuxtarget2.sd', + 'generated_perms_leading/ambiguous-ownerCuxtarget.sd', + 'generated_perms_leading/exact-re-Cuxtarget2.sd', + 'generated_perms_leading/ambiguous-Puxtarget.sd', + 'generated_perms_leading/overlap-Puxtarget.sd', + 'generated_perms_leading/ambiguous-Puxtarget2.sd', + 'generated_perms_leading/overlap-Puxtarget2.sd', + 'generated_perms_leading/exact-Puxtarget.sd', + 'generated_perms_leading/overlap-ownerPuxtarget.sd', + 'generated_perms_leading/exact-ownerCuxtarget.sd', + 'generated_perms_leading/exact-re-ownerCux.sd', + 'generated_perms_leading/exact-ownerPuxtarget2.sd', + 'generated_perms_leading/exact-ownerCux.sd', + 'generated_perms_leading/overlap-Cuxtarget2.sd', + 'generated_perms_leading/ambiguous-Cuxtarget.sd', + 'generated_perms_leading/ambiguous-ownerPuxtarget2.sd', + 'generated_perms_leading/dominate-ownerCux.sd', + 'generated_perms_leading/exact-Pux.sd', + 'generated_perms_leading/exact-Cuxtarget.sd', + 'generated_perms_leading/overlap-ownerCuxtarget2.sd', + 'generated_perms_leading/overlap-Pux.sd', + 'generated_perms_leading/overlap-ownerPux.sd', + 'generated_perms_leading/ambiguous-ownerCuxtarget2.sd', + 'generated_perms_leading/exact-re-Cux.sd', + 'generated_perms_leading/exact-re-Pux.sd', + 'generated_perms_leading/overlap-Cuxtarget.sd', + 'generated_perms_leading/exact-re-ownerPuxtarget2.sd', + 'generated_perms_leading/exact-Cuxtarget2.sd', + 'generated_perms_leading/exact-Cux.sd', + 'generated_perms_leading/overlap-Cux.sd', + 'generated_perms_leading/overlap-ownerCux.sd', + 'generated_perms_leading/exact-ownerPuxtarget.sd', + 'generated_perms_leading/dominate-Pux.sd', + 'generated_perms_leading/exact-ownerCuxtarget2.sd', + 'generated_perms_leading/dominate-Puxtarget.sd', + 'generated_perms_leading/ambiguous-ownerPux.sd', + 'generated_perms_leading/overlap-ownerPuxtarget2.sd', + 'generated_perms_leading/dominate-Cuxtarget2.sd', + 'generated_perms_leading/dominate-Cuxtarget.sd', + 'generated_perms_leading/dominate-ownerPuxtarget2.sd', + + # escaping with \ 'file/ok_embedded_spaces_4.sd', # \-escaped space 'file/file/ok_embedded_spaces_4.sd', # \-escaped space 'file/ok_quoted_4.sd', # quoted string including \" - 'xtrans/simple_ok_pix_1.sd', # Invalid mode pIx - 'xtrans/simple_ok_pux_1.sd', # Invalid mode rPux - - # dbus regex mismatch - 'vars/vars_dbus_4.sd', - 'vars/vars_dbus_9.sd', - 'vars/vars_dbus_2.sd', # misc 'vars/vars_dbus_8.sd', # Path doesn't start with / or variable: {/@{TLDS}/foo,/com/@{DOMAINS}} @@ -399,6 +458,7 @@ print('Running %s parser simple_tests...' % len(TestParseParserTests.tests)) +setup_aa(apparmor) find_and_setup_test_profiles('../../parser/tst/simple_tests/') setup_all_loops(__name__) === modified file 'utils/test/test-pivot_root_parse.py' --- utils/test/test-pivot_root_parse.py 2016-10-01 18:57:09 +0000 +++ utils/test/test-pivot_root_parse.py 2017-03-02 21:21:53 +0000 @@ -11,7 +11,7 @@ import apparmor.aa as aa import unittest -from common_test import AAParseTest, setup_regex_tests +from common_test import AAParseTest, setup_regex_tests, setup_aa class AAParsePivotRootTest(AAParseTest): def setUp(self): @@ -24,6 +24,7 @@ ('pivot_root /old /new -> /usr/bin/child,', 'pivot_root child rule'), ] +setup_aa(aa) if __name__ == '__main__': setup_regex_tests(AAParsePivotRootTest) unittest.main(verbosity=2) === modified file 'utils/test/test-regex_matches.py' --- utils/test/test-regex_matches.py 2016-10-01 18:57:09 +0000 +++ utils/test/test-regex_matches.py 2017-03-02 21:21:53 +0000 @@ -11,7 +11,7 @@ import apparmor.aa as aa import unittest -from common_test import AATest, setup_all_loops +from common_test import AATest, setup_all_loops, setup_aa from apparmor.common import AppArmorBug, AppArmorException from apparmor.regex import ( strip_parenthesis, strip_quotes, parse_profile_start_line, re_match_include, @@ -502,6 +502,7 @@ +setup_aa(aa) setup_all_loops(__name__) if __name__ == '__main__': # these two are not converted to a tests[] loop yet === modified file 'utils/test/test-unix_parse.py' --- utils/test/test-unix_parse.py 2016-10-01 18:57:09 +0000 +++ utils/test/test-unix_parse.py 2017-03-02 21:21:53 +0000 @@ -11,7 +11,7 @@ import apparmor.aa as aa import unittest -from common_test import AAParseTest, setup_regex_tests +from common_test import AAParseTest, setup_regex_tests, setup_aa class AAParseUnixTest(AAParseTest): @@ -34,6 +34,7 @@ 'complex unix rule'), ] +setup_aa(aa) if __name__ == '__main__': setup_regex_tests(AAParseUnixTest) unittest.main(verbosity=2) vim:ft=diff