Bug: https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1480492
If python3-apparmor is not installed, aa-status aborts due to the added
import to handle fancier exception handling failing. This patch makes
aa-status(8) work even in that case, falling back to normal python
exceptions, to keep its required dependencies as small as possible.
Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
Acked-by: Christian Boltz <apparmor@cboltz.de>
write_prof_data[hat] is correct (it only contains one profile, see bug 1528139),
write_prof_data[profile][hat] is not and returns an empty (sub)hasher.
This affects RE_PROFILE_START and RE_PROFILE_BARE_FILE_ENTRY.
Acked-by: Kshitij Gupta <kgupta8592@gmail.com> for trunk, 2.9 and 2.10
a) change log_dict to profile_storage()
Change collapse_log() to initialize log_dict[aamode][profile][hat]
as profile_storage() instead of a hasher().
This also means path events need to go into
log_dict[aamode][profile][hat]['allow']['path']
instead of
log_dict[aamode][profile][hat]['path']
to match the profile_storage() layout.
b) Simplify log translation
The translation from logparser.py's output to *Rule events was more ugly
than needed. This patch removes one step.
Instead of translating log_dict to log_obj in ask_the_questions(), add
*Rule objects to log_dict and adjust ask_the_questions() to use log_dict
instead of log_obj.
This also means log_obj in ask_the_questions() is now superfluous and
can be removed.
c) Other small changes:
- use is_known_rule() instead of .is_covered() for capability events,
which means included files are also checked now.
- remove the "if rule_obj.log_event != aamode:" check, because
a) it depends on the content of *Rule.log_event (which means it
ignores events with log_event != 'ALLOWING' or 'REJECTING'
b) it's superfluous because the whole code section is wrapped in a
"for aamode in sorted(log.dict.keys())" which means we have
separate loops for enforce and complain mode already
Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
If the program specified as get_output param isn't executable or doesn't
exist at all, get_output() returns with ret = -1.
Raising an exception looks like a better option, especially because
other possible exec failures already raise an exception ("Unable to
fork").
Note: get_output is only used by get_reqs() which also does the
os.access() check for x permissions (and raises an exception), so in
practise raising an exception in get_output() doesn't change anything.
This change also allows to rewrite and simplify get_output() quite a bit.
Another minor change (and fix) is in the removal of the last line. The
old code removed the last line if output contained at least two items.
This had two not-so-nice effects:
- an empty output resulted in [''] instead of []
- if a command didn't add a \n on the last line, this line was deleted
nevertheless
The patch changes that to always remove the last line if it is empty,
which fixes both issues mentioned above.
Also add a test to ensure the exception is really raised, and adjust the
test that expects an empty stdout.
Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
To make these tests independent from the underlaying system, add a
fake_ldd script that provides hardcoded ldd output for the "known"
executables and libraries.
To avoid interferences with the real system (especially symlinks), all
paths in fake_ldd have '/AATest' prepended.
Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
To ensure aa-cleanprof works as expected (and writing the rules works
as expected), add some rules for every rule class to the cleanprof.in
and cleanprof.out test profiles.
Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
According to a discussion with John on IRC, denied_mask="x" can only
happen for 'exec' log events. This patch raises an exception if John
is wrong ;-)
Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
This should happen rarely, but nevertheless it can happen - and since
AppArmor needs the symlink target in the profile, we have to resolve all
symlinks.
Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
If a profile file contains multiple profiles and one of those profiles
contains a rule managed by a *Ruleset class,
serialize_profile_from_old_profile() crashes with an AttributeError.
This happens because profile_data / write_prof_data contain only one
profile with its hats, which explodes if a file contains multiple
profiles, as reported in lp#1528139
Fixing this would need lots of
write_prof_data[hat] -> write_prof_data[profile][hat]
changes (and of course also a change in the calling code) or, better
option, a full rewrite of serialize_profile_from_old_profile().
Unfortunately I don't have the time to do the rewrite at the moment (I
have other things on my TODO list), and changing write_prof_data[hat] ->
write_prof_data[profile][hat] is something that might introduce more
breakage, so I'm not too keen to do that.
Therefore this patch wraps the serialize_profile_from_old_profile() call
in try/except. If it fails, the diff will include an error message and
recommend to use 'View Changes b/w (C)lean profiles' instead, which is
known to work.
Note: I know using an error message as 'newprofile' isn't an usual way
to display an error message, but I found it more intuitive than
displaying it as a warning (without $PAGER).
References: https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1528139
Acked-by: Seth Arnold <seth.arnold@canonical.com> for trunk and 2.10
parser/tst/simple_tests/profile/profile_ns_bad8.sd was added in r3376
(trunk) / r3312 (2.10 branch) and contains the profile name ':ns/t'
which misses the terminating ':' for the namespace.
Unfortunately the tools don't understand namespaces yet and just use the
full profile name. This also means this test doesn't fail as expected
when tested against the utils code.
This patch adds profile_ns_bad8.sd to the exception list of
test-parser-simple-tests.py.
Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.10.
https://launchpad.net/bugs/1546455
Don't filter out AF_UNSPEC from the list of valid protocol families so
that the parser will accept rules such as 'network unspec,'.
There are certain syscalls, such as socket(2), where the LSM hooks are
called before the protocol family is validated. In these cases, AppArmor
was emitting denials even though socket(2) will eventually fail. There
may be cases where AF_UNSPEC sockets are accepted and we need to make
sure that we're mediating those appropriately.
Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Suggested-by: Steve Beattie <steve@nxnw.org>
Acked-by: John Johansen <john.johansen@canonical.com>
[cboltz: Add 'unspec' to the network domain keywords of the utils]
If a profile file contains multiple profiles, aa-mergeprof crashes on
saving in write_profile() because the second profile in the file is not
listed in 'changed'. (This happens only if the second profile didn't
change.)
This patch first checks if 'changed' contains the profile before
pop()ing it.
Reproducer: copy utils/test/cleanprof_test.in to your profile directory
and run aa-mergeprof utils/test/cleanprof_test.out. Then just press
's' to save the profile.
Acked-by: Kshitij Gupta <kgupta8592@gmail.com> for trunk, 2.10 and 2.9
If autodep() is called with a pname starting with / (which can happen
for (N)amed exec depending on the user input), this pname is mapped to
bin_name.
This might look like a good idea, however if the given pname doesn't
exist as file on-disk, autodep() returns None instead of a (mostly
empty) profile. (Reproducer: choose (N)amed, enter "/foo/bar")
Further down the road, this results in two things:
a) the None result gets written as empty profile file (with only a "Last
modified" line)
b) a crash if someone chooses to add an abstraction to the None, because
None doesn't support the delete_duplicates() method for obvious
reasons ;-)
Unfortunately this patch also introduces a regression - aa-logprof now
fails to follow the exec and doesn't ask about the log events for the
exec target anymore. However this doesn't really matter because of a) -
asking and saving to /dev/null vs. not asking isn't a real difference ;-)
Actually the patch slightly improves things - it creates a profile for
the exec target, but only with the depmod() defaults (abstractions/base)
and always in complain mode.
I'd prefer a patch that also creates a complete profile for the exec
target, but that isn't as easy as fixing the issues mentioned above and
therefore is something for a future fix. To avoid we forget it, I opened
https://bugs.launchpad.net/apparmor/+bug/1545155
Note: 2.9 "only" writes an empty file and doesn't crash - but writing
an empty profile is still an improvement.
Acked-by: Kshitij Gupta <kgupta8592@gmail.com> for trunk, 2.10 and 2.9
According to the discussion with John on IRC, exec log events for
directories should never happen, therefore let handle_children()
raise an exception.
Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
Most probably-file log events can also be network events. Therefore
check for request_mask in all events, not only file_perm, file_inherit
and (from the latest bugreport) file_receive.
References: https://bugs.launchpad.net/apparmor/+bug/1540562
Acked-by: Kshitij Gupta <kgupta8592@gmail.com> for trunk, 2.10 and 2.9.
On Debian and Ubuntu it's possible to have multiple ruby interpreters
installed, and the default to use is handled by the ruby-defaults
package, which includes a symlink from /usr/bin/ruby to the versioned
ruby interpreter.
This patch makes aa.py:get_interpreter_and_abstraction() take that into
account by using a regex to match possible versions of ruby. Testcases
are included. (I noticed this lack of support because on Ubuntu the ruby
test was failing because get_interpreter_and_abstraction() would get the
complete path, which on my 16.04 laptop would get /usr/bin/ruby2.2.)
Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
The rule classes have lots of
if self.all_foo:
foo_txt = _('ALL')
else:
foo_txt = self.foo
in logprof_header_localvars().
To avoid repeating this over and over, split it off to a
logprof_value_or_all() function.
This function can handle
- str (will be returned unmodified
- AARE (.regex will be used)
- sets/lists/tuples (will be ' '.join()ed and sorted)
Other types are returned unmodified.
Acked-by: Steve Beattie <steve@nxnw.org>
When hitting an unknown line while parsing a profile, it's a good idea
to include that line in the error message ;-)
Note: 2.9 would print a literal \n because it doesn't have apparmor.fail,
so it will get a slightly different patch with spaces instead of \n.
Acked-by: Steve Beattie <steve@nxnw.org> for trunk, 2.10 and 2.9.
Checking if two AARE objects are equal is not hard, but also not a
one-liner.
Since we need to do this more than once (and even more often in other
outstanding rule classes), split that code into an _is_equal_aare()
function and change PtraceRule and SignalRule to use it.
To make things even more easier, the parameters to use match the
_is_covered_aare() syntax.
Acked-by: Steve Beattie <steve@nxnw.org>
If a *Ruleset is empty, let __repr__() print/return
<FooRuleset (empty) />
instead of
<FooRuleset>
</FooRuleset>
Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.10.
PtraceRule 'access' and SignalRule 'access' and 'signal' can contain
more than one value. Therefore adjust is_covered_localvars() in both
to use the list (subset) instead of the plain (exactly equal) check.
Also add a testcase for each to ensure the list/subset check works as
expected.
Acked-by: Steve Beattie <steve@nxnw.org>
is_covered_localvars() in the rule classes need the same set of checks
again and again. This patch adds the helper functions _is_covered_list(),
_is_covered_aare() and _is_covered_plain() to check against lists, AARE
and plain variables like str.
The helpers check if the values from the other rule are valid (either
ALL or the value need to be set) and then check if the value is covered
by the other rule's values.
This results in replacing 7 lines with 2 in the rule classes and avoids
repeating code over and over.
Note that the helper functions depend on the *Rule.rule_name variable in
the exception message, therefore rule_name gets added to all rule
classes.
Acked-by: Steve Beattie <steve@nxnw.org>
'!' is a reserved symbol and needs to be escaped in AARE.
Note: aare.py only exists in trunk, therefore this part is trunk-only.
Acked-by: Seth Arnold <seth.arnold@canonical.com> for trunk, 2.10 and 2.9 as needed.
We need to check a rule part if it is *Rule.ALL or a string at various
places. Therefore split off the checks in PtraceRule's and SignalRule's
__init__() to the new _aare_or_alll() function in BaseRule.
This also makes the *Rule __init__() much more readable because we now
have one line to set self.foo and self.all_foo instead of 10 lines of
nested if conditions.
Acked-by: Steve Beattie <steve@nxnw.org>.
If parse_event_for_tree() raises an AppArmorException (for example
because of an invalid/unknown request_mask), catch it in read_log() and
re-raise it together with the log line causing the Exception.
Acked-by: Steve Beattie <steve@nxnw.org> for trunk, 2.10 and 2.9.
handle_children() has some special code for handling link events with
denied_mask = 'l'. Unfortunately this special code depends on a regex
that matches the old, obsolete log format - in a not really parsed
format ("^from .* to .*$").
The result was that aa-logprof did not ask about events containing 'l'
in denied_mask.
Fortunately the fix is easy - delete the code with the special handling
for 'l' events, and the remaining code that handles other file
permissions will handle it :-)
References: Bugreport by pfak on IRC
Testcase (with hand-tuned log event):
aa-logprof -f <( echo 'Jan 7 03:11:24 mail kernel: [191223.562261] type=1400 audit(1452136284.727:344): apparmor="ALLOWED" operation="link" profile="/usr/sbin/smbd" name="/foo" pid=10262 comm=616D617669736420286368362D3130 requested_mask="l" denied_mask="l" fsuid=110 ouid=110 target="/bar"')
should ask to add '/foo l,' to the profile.
Acked-by: Seth Arnold <seth.arnold@canonical.com> for trunk, 2.10 and 2.9.
Thanks to http://bugs.python.org/issue10076, we need to implement this
ourself :-/
Also add some tests to ensure __deepcopy__() works as expected.
I found this bug while testing the dbus patch series, which crashed
aa-cleanprof with
TypeError: cannot deepcopy this pattern object
Acked-by: John Johansen <john.johansen@canonical.com>
collapse_log() creates temporary SignalRule etc. objects which are then
checked against the existing profile content.
These temporary objects are based on log events, therefore flag them as
such. This will ensure proper handling and escaping by the AARE class.
Acked-by: John Johansen <john.johansen@canonical.com>
In detail, this means:
- handle ptrace events in logparser.py
- "translate" those events in aa.py - from log (logparser.py readlog())
to prelog (handle_children()) to log_dict (collapse_log()) to
log_obj (ask_the_questions())
(yes, really! :-/ - needless to say that this is ugly...)
- finally ask the user about the ptrace in ask_the_questions()
Also add a logparser test to test-ptrace.py to ensure the logparser step
works as expected.
Note that the aa.py changes are not covered by tests, however they
worked in a manual test.
If you want to test manually, try this (faked) log line:
msg=audit(1409700683.304:547661): apparmor="DENIED" operation="ptrace" profile="/usr/sbin/smbd" pid=22465 comm="ptrace" requested_mask="trace" denied_mask="trace" peer="/foo/bar"
Acked-by: John Johansen <john.johansen@canonical.com>
"Everywhere" means aa-mergeprof and aa-cleanprof. In theory also
aa-logprof, but that needs some code that parses ptrace log events ;-)
Acked-by: John Johansen <john.johansen@canonical.com>
Change aa.py to use PtraceRule and PtraceRuleset in profile_storage(),
parse_profile_data() and write_ptrace(). This also means we can drop the
now unused parse_ptrace_rule() and write_ptrace_rules() functions.
Raw_Ptrace_Rule in rules.py is now also unused and can be dropped.
Also adjust logparser.py to include the peer in the result, and shorten
the list of known-failing tests in test-parser-simple-tests.py.
Acked-by: John Johansen <john.johansen@canonical.com>
As usual, we have 100% test coverage :-)
Those tests include all tests from test-ptrace_parse.py, therefore
delete this file.
Acked-by: John Johansen <john.johansen@canonical.com>
The tests in test-ptrace_parse.py used aa.parse_ptrace_rule(), which is
based on Raw_Ptrace_Rule (= regex check + "just store it").
This patch changes the tests to test against PtraceRule.get_clean().
Since get_clean does some cleanups, the expected result slightly differs
from the original rule.
Finally switch to the AATest class and setup_all_loops() we use in most
tests.
Also change test-regex_matches.py to import RE_PROFILE_SIGNAL directly
from apparmor.regex instead of apparmor.aa (where it will vanish soon).
Acked-by: John Johansen <john.johansen@canonical.com>
Those classes will be used to parse and handle ptrace rules.
They understand the syntax of ptrace rules.
Note that get_clean() doesn't output superfluos things, so
ptrace ( trace ),
will become
ptrace trace,
Acked-by: John Johansen <john.johansen@canonical.com>