2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-31 14:25:52 +00:00

Compare commits

...

56 Commits

Author SHA1 Message Date
John Johansen
fff8d65985 common/Version: prepare for 2.10.1 release 2016-04-20 02:07:34 -07:00
John Johansen
8e2595c634 Fix: parser: incorrect output of child profile names
BugLink: http://bugs.launchpad.net/bugs/1551950

The apparmor_parser is incorrectly outputting the names of child profiles
and hats, by adding a : between the parent and the child profile name

  Eg.
    /usr/sbin/httpd{,2}-prefork
    /usr/sbin/httpd{,2}-prefork://DEFAULT_URI
    /usr/sbin/httpd{,2}-prefork://HANDLING_UNTRUSTED_INPUT

  instead of what it should be
    /usr/sbin/httpd{,2}-prefork
    /usr/sbin/httpd{,2}-prefork//DEFAULT_URI
    /usr/sbin/httpd{,2}-prefork//HANDLING_UNTRUSTED_INPUT

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2016-04-18 13:09:51 -07:00
Christian Boltz
711ca72c6b smbd profile needs capability sys_admin
smbd stores ACLS in the security.NTACL namespace, which means it needs
capability sys_admin.

References: https://bugzilla.opensuse.org/show_bug.cgi?id=964971
            http://samba-technical.samba.narkive.com/eHtOW8DE/nt-acls-using-the-security-namespace-for-ntacl-considered-improper



Acked-by: Steve Beattie <steve@nxnw.org> for 2.10 and 2.9
(trunk got this and other changes via a merge request from Simon already)
2016-04-13 23:21:31 +02:00
Tyler Hicks
af8ccba6d2 profiles: Add attach_disconnected flag to dnsmasq profile
https://launchpad.net/bugs/1569316

When Ubuntu made the jump from network-manager 1.0.4 to 1.1.93, the
dnsmasq process spawned from network-manager started hitting a
disconnected path denial:

  audit: type=1400 audit(1460463960.943:31702): apparmor="ALLOWED"
    operation="connect" info="Failed name lookup - disconnected path"
    error=-13 profile="/usr/sbin/dnsmasq"
    name="run/dbus/system_bus_socket" pid=3448 comm="dnsmasq"
    requested_mask="wr" denied_mask="wr" fsuid=65534 ouid=0

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Christian Boltz <apparmor@cboltz.de>
2016-04-12 16:37:26 -05:00
Christian Boltz
fa5d235f28 dovecot/auth: allow access to /var/run/dovecot/stats-user
Since the latest openSUSE Tumbleweed update (dovecot 2.2.21 -> 2.2.22),
dovecot/auth writes to /var/run/dovecot/stats-user.


Acked-by: Seth Arnold <seth.arnold@canonical.com> for trunk, 2.10 and 2.9.
2016-04-07 00:53:30 +02:00
Christian Boltz
bdb18c5ccf Update abstractions/ssl_* for acmetool-generated certificates
acmetool is an alternative client for Let's Encrypt.
(https://github.com/hlandau/acme/)

It stores the certificates etc. in the following directory layout:

    /var/lib/acme/live/<domain> -> ../certs/<hash>
    /var/lib/acme/certs/<hash>/cert
    /var/lib/acme/certs/<hash>/chain
    /var/lib/acme/certs/<hash>/privkey -> ../../keys/<hash>/privkey
    /var/lib/acme/certs/<hash>/url
    /var/lib/acme/certs/<hash>/fullchain
    /var/lib/acme/keys/<hash>/privkey

This patch adds the needed permissions to the ssl_certs and ssl_keys
abstractions so that the certificates can be used.


Acked-by: Seth Arnold <seth.arnold@canonical.com> for trunk, 2.10 and 2.9.
2016-03-28 21:43:23 +02:00
Christian Boltz
0cd0743b47 nscd profile: allow paranoia mode
In /etc/nscd.conf there is an option allowing to restart nscd after a
certain time. However, this requires reading /proc/self/cmdline -
otherwise nscd will disable paranoia mode.


References: https://bugzilla.opensuse.org/show_bug.cgi?id=971790


Acked-By: Jamie Strandboge <jamie@canonical.com> for trunk, 2.10 and 2.9
2016-03-21 21:31:06 +01:00
Steve Beattie
6e1e27a931 utils: make aa-status(8) work without python3-apparmor
Merge from trunk commit 3391

Bug: https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1480492

If python3-apparmor is not installed, aa-status aborts due to the
ded
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>
2016-03-18 13:32:45 -07:00
Christian Boltz
150350c42c Fix wrong usage of write_prof_data in serialize_profile_from_old_profile()
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
2016-03-01 21:25:29 +01:00
Christian Boltz
61ee9623c5 Prevent crash caused by serialize_profile_from_old_profile()
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
2016-02-20 13:33:17 +01:00
Christian Boltz
458f696f8e dovecot-lda profile: allow tempfiles and executing sendmail
dovecot-lda needs to read and write /tmp/dovecot.lda.*.

It also needs to be able to execute sendmail to send sieve vacation
mails.

For now, I'm using a child profile for sendmail to avoid introducing a
new profile with possible regressions. This child profile is based on
the usr.sbin.sendmail profile in extras and should cover both postfix'
and sendmail's sendmail.
I also mixed in some bits that were needed for (postfix) sendmail on my
servers, and dropped some rules that were obsolete (directory rules not
ending with a /) or covered by an abstraction.

In the future, we might want to provide a stand-alone profile for
sendmail (based on this child profile) and change the rule in the
dovecot-lda profile to Px.


References: https://bugzilla.opensuse.org/show_bug.cgi?id=954959
            https://bugzilla.opensuse.org/show_bug.cgi?id=954958



Acked-by: Seth Arnold <seth.arnold@canonical.com> for trunk, 2.10 and 2.9.
2016-02-20 01:16:49 +01:00
Christian Boltz
331e54b36e Add simple_tests/profile/profile_ns_bad8.sd to utils test exception list
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.
2016-02-19 00:25:20 +01:00
Tyler Hicks
85be9528ec parser: Allow AF_UNSPEC family in network rules
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]
2016-02-18 16:31:56 -06:00
Tyler Hicks
5493e01408 parser: Properly parse named transition targets
https://launchpad.net/bugs/1540666

Reuse the new parse_label() function to initialize named_transition
structs so that transition targets, when used with change_profile, are
properly seperated into a profile namespace and profile name.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2016-02-18 16:00:05 -06:00
Tyler Hicks
da0daadf40 parser: Allow the profile keyword to be used with namespaces
https://launchpad.net/bugs/1544387

Don't split namespaces from profile names using YACC grammar. Instead,
treat the entire string as a label in the grammer. The label can then be
split into a namespace and a profile name using the new parse_label()
function.

This fixes a bug that caused the profile keyword to not be used with a
label containing a namespace in the profile declaration.

Fixing this bug uncovered a bad parser test case at
simple_tests/profile/profile_ns_ok1.sd. The test case mistakenly
included two definitions of the :foo:unattached profile despite being
marked as expected to pass. I've adjusted the name of one of the
profiles to :foo:unattached2.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2016-02-18 16:00:04 -06:00
Christian Boltz
6d05fa4a6e Fix aa-mergeprof crash with files containing multiple profiles
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
2016-02-12 22:09:36 +01:00
Christian Boltz
f5df1bf45e Remove pname to bin_name mapping in autodep()
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
2016-02-12 21:57:25 +01:00
Christian Boltz
a80c75e308 apparmor.d.pod: document 'deny x'
deny rules don't allow ix, Px, Ux etc. - only 'deny /foo x,' is allowed.


Acked-by: Seth Arnold <seth.arnold@canonical.com> for trunk and 2.10


Note: Seth mentioned in the mail that he doesn't like the 'deny x'
section too much, but we didn't find a better solution when discussing
it on IRC. Therefore I keep the patch unchanged, but will happily
review a follow-up patch if someone sends one ;-)
2016-02-12 21:43:42 +01:00
Christian Boltz
f5462aa931 logparser.py: do sanity check for all file events
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.
2016-02-10 19:09:57 +01:00
Tyler Hicks
91e73d54fe pam_apparmor: Don't leak /dev/urandom fd
If reading /dev/urandom failed, the corresponding file descriptor was
leaked through the error path.

Coverity CID #56012

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
2016-02-01 10:35:57 -06:00
Tyler Hicks
57cdc4257d libapparmor: Correct meaning of EPERM in aa_change_profile man page
I suspect that the incorrect description of EPERM was copied from
the aa_change_hat man page, where it is possible to see EPERM if the
application is not confined by AppArmor.

This patch corrects the description by documenting that the only
possible way to see EPERM is if a confined application has the
no_new_privs bit set.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Reported-by: Seth Arnold <seth.arnold@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2016-01-27 13:38:39 -06:00
Tyler Hicks
4c04a05996 libapparmor: Open fds may be revalidated after aa_change_profile()
It is possible that file descriptors will be revalidated after an
aa_change_profile() but there is a lot of complexity involved that
doesn't need to be spelled out in the man page. Instead, mention that
revalidation is possible but the only way to ensure that file
descriptors are not passed on is to close them.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Reported-by: Seth Arnold <seth.arnold@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2016-01-27 13:38:39 -06:00
Tyler Hicks
a492bcfc80 libapparmor: Remove incorrect statement in aa_change_profile man page
The statement was meant to convey the difference between aa_change_hat()
and aa_change_profile(). Unfortunately, it read as if there was
something preventing a program from using aa_change_profile() twice to
move from profile A to profile B and back to profile A, even if profiles
A and B contained the necessary rules.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Reported-by: Seth Arnold <seth.arnold@canonical.com>
Acked-by: Christian Boltz <apparmor@cboltz.de>
2016-01-27 13:38:39 -06:00
Steve Beattie
ec9292bd5e utils: handle versioned ruby interpreters
Merge from trunk revision 3353

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>
2016-01-25 23:05:47 -08:00
Christian Boltz
703cc22b52 utils/test/Makefile: print test filenames in 'make check' and 'make coverage'
This makes it easier to find the file that contains a failing test.



Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.10.

Bug: https://launchpad.net/bugs/1526085
2016-01-25 23:49:54 +01:00
Christian Boltz
4fd66468d8 Better error message on unknown profile lines
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.

Bug: https://launchpad.net/bugs/1525119
2016-01-25 23:45:52 +01:00
Christian Boltz
b80aadd624 Improve __repr__() for *Ruleset
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.

Bug: https://launchpad.net/bugs/1523297
2016-01-25 23:42:45 +01:00
Steve Beattie
0dde5efc62 regression tests: define arch specific bits for s390x
Merge from trunk revision 3342

bug: https://bugs.launchpad.net/bugs/1531325

This patch defines the arch specific registers struct for s390 for the
ptrace regression test.

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: John Johansen <john.johansen@canonical.com>
2016-01-21 13:23:23 -08:00
Christian Boltz
99b59f7169 AARE: escape exclamation mark
'!' 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.
2016-01-20 21:51:52 +01:00
Christian Boltz
061c76c5b1 Fix a missing comma in parser_misc.c capnames
The capnames list missed a comma, which lead to the funny
"mac_overridesyslog" capability name.

__debug_capabilities() seems to be the only user of capnames, which
might explain why this bug wasn't noticed earlier.


Acked-by: Seth Arnold <seth.arnold@canonical.com> for trunk, 2.10 and 2.9.
2016-01-16 11:27:26 +01:00
John Johansen
dab2636a27 Fix: segfault when processing directories
BugLink: http://bugs.launchpad.net/bugs/1534405

Patch -r 2952 switched over to using the library kernel interface, and
added a kernel_interface parameter to the dir_cb struct, that is used
to process directories.

Unfortunately kernel_interface parameter of the dir_cb struct is not being
properly initialized resulting in odd failures and sefaults when the parser
is processing directories.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
2016-01-14 17:30:33 -08:00
Christian Boltz
a41bff515f More useful logparser failure reports
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.
2016-01-12 19:49:52 +01:00
Christian Boltz
483f11d06c Fix handling of link events in aa-logprof
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.
2016-01-07 21:26:46 +01:00
Jamie Strandboge
1140e54442 Merge from trunk
allow read on /run/systemd/resolve/resolv.conf for systems using networkd
(LP: #1529074)

Signed-Off-By: Jamie Strandboge <jamie@canonical.com>
Acked-by: Christian Boltz <apparmor@cboltz.de>
2016-01-05 17:04:34 -06:00
Christian Boltz
b54d1f2049 Write unix rules when saving a profile
r2637 added support for parsing unix rules, but forgot to add write
support. The result was that a profile lost its unix rules when it was
saved.

This patch adds the write_unix_rules() and write_unix() functions (based
on the write_pivot_root() and write_pivot_root_rules() functions) and
makes sure they get called at the right place.

The cleanprof testcase gets an unix rule added to ensure it's not
deleted when writing the profile. (Note that minitools_test.py is not
part of the default "make check", however I always run it.)


References: https://bugs.launchpad.net/apparmor/+bug/1522938
            https://bugzilla.opensuse.org/show_bug.cgi?id=954104



Acked-by: Tyler Hicks <tyhicks@canonical.com> for trunk, 2.10 and 2.9.
2015-12-17 23:48:43 +01:00
Christian Boltz
6e846245ab Adjust test-aa.py for python2
This means:
- expect unicode (instead of str) when reading from a file in py2
- convert keys() result to a set to avoid test failures because of
  dict_keys type

After this change, all tests work for both py2 and py3.


Acked-by: Tyler Hicks <tyhicks@canonical.com> for trunk and 2.10.
2015-12-17 23:45:33 +01:00
Christian Boltz
218cb42fbe Adjust type(x) == str checks in the rule classes for py2
python 3 uses only the 'str' type, while python 2 also uses 'unicode'.
This patch adds a type_is_str() function to common.py - depending on the
python version, it checks for both. This helper function is used to keep
the complexity outside of the rule classes.

The rule classes get adjusted to use type_is_str() instead of checking
for type(x) == str, which means they support both python versions.

Finally, add test-common.py with some tests for type_is_str().


References: https://bugs.launchpad.net/apparmor/+bug/1513880


Acked-by: Tyler Hicks <tyhicks@canonical.com> for trunk and 2.10

Note: 2.10 doesn't contain SignalRule and aare.py, and rule/__init__.py
doesn't have check_and_split_list(), therefore it doesn't get those
parts of the patch.
2015-12-17 23:38:02 +01:00
Tyler Hicks
df12e87fb5 utils: Use apparmor.fail for AppArmorException handling in aa-easyprof
Don't catch AppArmorExceptions in aa-easyprof any longer and rely on
apparmor.fail to print the exception to stderr.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Christian Boltz <apparmor@cboltz.de>
2015-12-16 16:12:59 -06:00
Christian Boltz
5a2da347d4 Let the apparmor.fail error handler print to stderr
The patch also switches to using error() instead of a plain print() for
AppArmorException, which means prefixing the error message with 'ERROR: '



References: https://bugs.launchpad.net/apparmor/+bug/1521400


Acked-by: Tyler Hicks <tyhicks@canonical.com> for trunk and 2.10.
2015-12-16 12:00:00 +01:00
Tyler Hicks
cfbc1a2a79 parser: Honor the --namespace-string commandline option
https://launchpad.net/bugs/1526085

Revno 2934 'Add fns to handle profile removal to the kernel interface'
introduced a regression in the parser's namespace support by causing the
--namespace-string option to be ignored. This resulted in the profile(s)
being loaded into the global namespace rather than the namespace
specified on the command line.

This patch fixes the bug by setting the Profile object's ns member, if
the --namespace-string option was specified, immediately after the
Profile object is allocated.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2015-12-15 16:41:35 -06:00
Christian Boltz
51a0d5d863 ignore log event if request_mask == ''
We already check for None, but '' != None ;-)


References: https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1525119


Acked-by: John Johansen <john.johansen@canonical.com> for 2.9, 2.10 and trunk.
2015-12-12 13:31:20 +01:00
Christian Boltz
3ab596ed83 Fix logparser.py crash on change_hat events
'change_hat' events have the target profile in 'name2', not in 'name'
(which is None and therefore causes a crash when checking if it contains
'//')

Also add the log event causing this crash to the libapparmor testsuite.

References: https://bugs.launchpad.net/apparmor/+bug/1523297


Acked-by: John Johansen <john.johansen@canonical.com> for trunk, 2.10 and 2.9.
2015-12-12 13:06:57 +01:00
Christian Boltz
d5824674d1 Several fixes for variable handling
Parsing variables was broken in several ways:
- empty quotes (representing an intentionally empty value) were lost,
  causing parser failures
- items consisting of only one letter were lost due to a bug in RE_VARS
- RE_VARS didn't start with ^, which means leading garbage (= syntax
  errors) was ignored
- trailing garbage was also ignored

This patch fixes those issues in separate_vars() and changes
var_transform() to write out empty quotes (instead of nothing) for empty
values.

Also add some tests for separate_vars() with empty quotes and adjust
several tests with invalid syntax to expect an AppArmorException.

var_transform() gets some tests added.

Finally, remove 3 testcases from the "fails to raise an exception" list
in test-parser-simple-tests.py.



Acked-by: John Johansen <john.johansen@canonical.com> for trunk and 2.9
(which also implies 2.10)


Note: 2.9 doesn't have test-parser-simple-tests.py, therefore it won't
get that part of the patch.
2015-12-12 13:01:19 +01:00
Christian Boltz
3ef80d788a Add realtime signals to SIGNALS list in apparmor.d
Acked-by: Seth Arnold <seth.arnold@canonical.com> for trunk, 2.10 and 2.9.
2015-12-02 22:06:07 +01:00
Christian Boltz
d579fc51d4 Add realtime signal example to the apparmor.d manpage
Acked-by: Steve Beattie <steve@nxnw.org> for trunk, 2.10 and 2.9.
2015-12-02 20:10:24 +01:00
Christian Boltz
8d68618f0b Add missing variables to the apparmor.d manpage
@{pids} and @{apparmorfs} was not mentioned in the apparmor.d manpage.


Acked-by: John Johansen <john.johansen@canonical.com> for trunk, 2.10 and 2.9
2015-11-28 21:45:20 +01:00
Christian Boltz
f6dcade84f Change abstract methods in BaseRule to use NotImplementedError
As Kshitij mentioned, abstract methods should use NotImplementedError
instead of AppArmorBug.

While changing this, I noticed that __repr__() needs to be robust against
NotImplementedError because get_raw() is not available in BaseRule.
Therefore the patch changes __repr__() to catch NotImplementedError.

Of course the change to NotImplementedError also needs several
adjustments in the tests.


Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
(long before branching off 2.10, therefore I also commit to 2.10)


Note: 2.10 doesn't have test-signal.py, which means it can't be patched ;-)
2015-11-24 00:22:37 +01:00
Christian Boltz
0f4310d301 Map c (create) log events to w instead of a
Creating a file is in theory covered by the 'a' permission, however
discussion on IRC brought up that depending on the open flags it might
not be enough (real-world example: creating the apache pid file).

Therefore change the mapping to 'w' permissions. That might allow more
than needed in some cases, but makes sure the profile always works.


Acked-by: Kshitij Gupta <kgupta8592@gmail.com> for 2.9, 2.10 and trunk
2015-11-19 21:23:31 +01:00
Christian Boltz
84ab95d263 Also add python 3.5 to logprof.conf
Acked-by: Kshitij Gupta <kgupta8592@gmail.com> for 2.9, 2.10 and trunk
2015-11-19 20:23:26 +01:00
Jamie Strandboge
eb2adf119b Description: update python abstraction for python 3.
Signed-off-by: Jamie Strandboge <jamie@canonical.com>
Acked-by: Christian Boltz <apparmor@cboltz.de>
2015-11-19 08:52:39 -06:00
Christian Boltz
68041e0d2e Add debug info to profile_storage()
For debugging, it's helpful to know which part of the code initialized a
profile_storage and for which profile and hat this was done.

This patch adds an 'info' array with that information, adds the
corresponding parameters to profile_storage() and changes the callers to
deliver some useful content.


Acked-by: John Johansen <john.johansen@canonical.com> for trunk and 2.10
2015-11-18 22:02:53 +01:00
Christian Boltz
4c0e6334b5 Fix parsing/storing bare file rules
We replaced parse_audit_allow() with parse_modifiers() in r2833, but
overlooked that parse_modifiers() returns allow/deny as boolean. This
resulted in storing bare file rules in aa[profile][hat]['path'][False]
instead of aa[profile][hat]['path']['allow'] (or True instead of 'deny'
for 'deny file,' rules), with the user-visible result of loosing bare
file rules when saving the profile.

This patch converts the boolean value from parse_modifiers back to a
string.

Note: 2.9 is not affected because the old parse_audit_allow() returns
'allow' or 'deny' as string, not as boolean.


Acked-by: Kshitij Gupta <kgupta8592@gmail.com> for trunk and 2.10
2015-11-18 21:31:45 +01:00
Christian Boltz
73cdd97596 update PYMODULES in tools/Makefile
PYMODULES is used for generating *.pod, so it should include rule/*.PYMODULES


Acked-by: Kshitij Gupta <kgupta8592@gmail.com> for trunk and 2.10
2015-11-18 21:29:25 +01:00
Christian Boltz
dfe58983bb utils/test/Makefile: add libapparmor to PYTHONPATH
The last utils/test/Makefile change switched to using the in-tree
libapparmor by default (unless USE_SYSTEM=1 is given). However, I missed
to add the swig/python parts of libapparmor to PYTHONPATH, so the
system-wide LibAppArmor/__init__.py was always used.

This patch adds the in-tree libapparmor python module to PYTHONPATH.

I'm sorry for the interesting[tm] way to find out that path, but
a) I don't know a better / less ugly way and
b) a similar monster already works in libapparmor/swig/python/test/ ;-)


Acked-by: John Johansen <john.johansen@canonical.com> for 2.9 and trunk
(that also implies 2.10 ;-)
2015-11-18 13:45:47 +01:00
Christian Boltz
07b6148fd1 Add python to the "no Px rule" list in logprof.conf
To make things more interesting, /usr/bin/python and /usr/bin/python[23]
are symlinks to /usr/bin/python[23].[0-9], so we have to explicitely
list several versions.


Acked-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com> for 2.9, 2.10 and trunk
2015-11-18 13:39:33 +01:00
Steve Beattie
d04a03359c Update REPO_URL to point to the apparmor 2.10 branch 2015-11-18 01:32:49 -08:00
61 changed files with 654 additions and 219 deletions

View File

@@ -18,7 +18,7 @@ DIRS=parser \
#REPO_URL?=lp:apparmor
# --per-file-timestamps is failing over SSH, https://bugs.launchpad.net/bzr/+bug/1257078
REPO_URL?=https://code.launchpad.net/~apparmor-dev/apparmor/master
REPO_URL?=https://code.launchpad.net/~apparmor-dev/apparmor/2.10
# alternate possibilities to export from
#REPO_URL=.
#REPO_URL="bzr+ssh://bazaar.launchpad.net/~sbeattie/+junk/apparmor-dev/"

View File

@@ -111,6 +111,7 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags,
sizeof(magic_token));
if (retval < 0) {
pam_syslog(pamh, LOG_ERR, "Can't read from /dev/urandom\n");
close(fd);
return PAM_PERM_DENIED;
}
} while ((magic_token == 0) || (retval != sizeof(magic_token)));

View File

@@ -98,7 +98,7 @@ list_capabilities: /usr/include/linux/capability.h
# to mediate. We use PF_ here since that is what is required in
# bits/socket.h, but we will rewrite these as AF_.
FILTER_FAMILIES=PF_UNSPEC PF_UNIX
FILTER_FAMILIES=PF_UNIX
__FILTER=$(shell echo $(strip $(FILTER_FAMILIES)) | sed -e 's/ /\\\|/g')

View File

@@ -1 +1 @@
2.10
2.10.1

View File

@@ -40,16 +40,15 @@ An AppArmor profile applies to an executable program; if a portion of
the program needs different access permissions than other portions,
the program can "change profile" to a different profile. To change into a
new profile, it can use the aa_change_profile() function to do so. It passes
in a pointer to the I<profile> to transition to. Transitioning to another
profile via aa_change_profile() is permanent and the process is not
permitted to transition back to the original profile. Confined programs
wanting to use aa_change_profile() need to have rules permitting changing
to the named profile. See apparmor.d(8) for details.
in a pointer to the I<profile> to transition to. Confined programs wanting to
use aa_change_profile() need to have rules permitting changing to the named
profile. See apparmor.d(8) for details.
If a program wants to return out of the current profile to the
original profile, it should use aa_change_hat(2) instead.
original profile, it may use aa_change_hat(2). Otherwise, the two profiles must
have rules permitting changing between the two profiles.
Open file descriptors are not remediated after a call to aa_change_profile()
Open file descriptors may not be remediated after a call to aa_change_profile()
so the calling program must close(2) open file descriptors to ensure they
are not available after calling aa_change_profile(). As aa_change_profile()
is typically used just before execve(2), you may want to use open(2) or
@@ -84,8 +83,8 @@ Insufficient kernel memory was available.
=item B<EPERM>
The calling application is not confined by apparmor, or the no_new_privs
bit is set.
The calling application is confined by apparmor and the no_new_privs bit is
set.
=item B<EACCES>

View File

@@ -0,0 +1 @@
type=AVC msg=audit(1449442292.901:961): apparmor="ALLOWED" operation="change_hat" profile="/usr/sbin/httpd{,2}-prefork" pid=8527 comm="httpd-prefork" target="/usr/sbin/httpd{,2}-prefork//HANDLING_UNTRUSTED_INPUT"

View File

@@ -0,0 +1,11 @@
START
File: testcase_changehat_01.in
Event type: AA_RECORD_ALLOWED
Audit ID: 1449442292.901:961
Operation: change_hat
Profile: /usr/sbin/httpd{,2}-prefork
Command: httpd-prefork
Name2: /usr/sbin/httpd{,2}-prefork//HANDLING_UNTRUSTED_INPUT
PID: 8527
Epoch: 1449442292
Audit subid: 961

View File

@@ -161,7 +161,7 @@ B<SIGNAL SET> = 'set' '=' '(' I<SIGNAL LIST> ')'
B<SIGNAL LIST> = Comma or space separated list of I<SIGNALS>
B<SIGNALS> = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | 'sys' | 'emt' | 'exists' )
B<SIGNALS> = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | 'sys' | 'emt' | 'exists' | 'rtmin+0' ... 'rtmin+32' )
B<SIGNAL PEER> = 'peer' '=' I<AARE>
@@ -251,7 +251,7 @@ B<UNQUOTED FILEGLOB> = (must start with '/' (after variable expansion), B<AARE>
B<ACCESS> = ( 'r' | 'w' | 'a' | 'l' | 'k' | 'm' | I<EXEC TRANSITION> )+ (not all combinations are allowed; see below.)
B<EXEC TRANSITION> = ( 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx' | 'Cx' | 'pix' | 'Pix' | 'cix' | 'Cix' | 'pux' | 'PUx' | 'cux' | 'CUx' )
B<EXEC TRANSITION> = ( 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx' | 'Cx' | 'pix' | 'Pix' | 'cix' | 'Cix' | 'pux' | 'PUx' | 'cux' | 'CUx' | 'x' ) ('x' is only allowed in rules with the deny qualifier, everything else only without the deny qualifier)
B<EXEC TARGET> = name (requires I<EXEC TRANSITION> specified)
@@ -366,6 +366,10 @@ modes:
- transition to subprofile on execute with fallback to unconfined -- scrub the environment
=item B<deny x>
- disallow execute (in rules with the deny qualifier)
=item B<m>
- allow PROT_EXEC with mmap(2) calls
@@ -425,7 +429,7 @@ over the callee. Use this mode only if the child absolutely must be
run unconfined and LD_PRELOAD must be used. Any profile using this mode
provides negligible security. Use at your own risk.
Incompatible with other exec transition modes.
Incompatible with other exec transition modes and the deny qualifier.
=item B<Ux - unconfined execute -- scrub the environment>
@@ -439,7 +443,7 @@ designated child processes to be run without any AppArmor protection.
Use this mode only if the child absolutely must be run unconfined. Use
at your own risk.
Incompatible with other exec transition modes.
Incompatible with other exec transition modes and the deny qualifier.
=item B<px - Discrete Profile execute mode>
@@ -451,7 +455,7 @@ B<WARNING> 'px' does not scrub the environment of variables such as
LD_PRELOAD; as a result, the calling domain may have an undue amount of
influence over the callee.
Incompatible with other exec transition modes.
Incompatible with other exec transition modes and the deny qualifier.
=item B<Px - Discrete Profile execute mode -- scrub the environment>
@@ -460,7 +464,7 @@ will invoke the Linux Kernel's B<unsafe_exec> routines to scrub
the environment, similar to setuid programs. (See ld.so(8) for some
information on setuid/setgid environment scrubbing.)
Incompatible with other exec transition modes.
Incompatible with other exec transition modes and the deny qualifier.
=item B<cx - Transition to Subprofile execute mode>
@@ -472,7 +476,7 @@ B<WARNING> 'cx' does not scrub the environment of variables such as
LD_PRELOAD; as a result, the calling domain may have an undue amount of
influence over the callee.
Incompatible with other exec transition modes.
Incompatible with other exec transition modes and the deny qualifier.
=item B<Cx - Transition to Subprofile execute mode -- scrub the environment>
@@ -481,7 +485,7 @@ will invoke the Linux Kernel's B<unsafe_exec> routines to scrub
the environment, similar to setuid programs. (See ld.so(8) for some
information on setuid/setgid environment scrubbing.)
Incompatible with other exec transition modes.
Incompatible with other exec transition modes and the deny qualifier.
=item B<ix - Inherit execute mode>
@@ -495,7 +499,7 @@ profile, or losing the permissions of the current profile. There is no
version to scrub the environment because 'ix' executions don't change
privileges.
Incompatible with other exec transition modes.
Incompatible with other exec transition modes and the deny qualifier.
=item B<Profile transition with inheritance fallback execute mode>
@@ -509,7 +513,7 @@ the 'ix' transition mode.
'Cix' == 'Cx' with fallback to 'ix'
'cix' == 'cx' with fallback to 'ix'
Incompatible with other exec transition modes.
Incompatible with other exec transition modes and the deny qualifier.
=item B<Profile transition with unconfined fallback execute mode>
@@ -524,7 +528,14 @@ if 'PUx', 'CUx' is used.
'CUx' == 'Cx' with fallback to 'Ux'
'cux' == 'cx' with fallback to 'ux'
Incompatible with other exec transition modes.
Incompatible with other exec transition modes and the deny qualifier.
=item B<deny x - Deny execute>
For rules including the deny modifier, only 'x' is allowed to deny execute.
The 'ix', 'Px', 'px', 'Cx', 'cx' and the fallback modes conflict with the deny
modifier.
=item B<Directed profile transitions>
@@ -964,6 +975,9 @@ Example AppArmor signal rules:
# Allow us to signal ourselves using the built-in @{profile_name} variable
signal peer=@{profile_name},
# Allow two real-time signals
signal set=(rtmin+0 rtmin+32),
=head2 DBus rules
AppArmor supports DBus mediation. The mediation is performed in conjunction
@@ -1229,8 +1243,10 @@ provided AppArmor policy:
@{HOMEDIRS}
@{multiarch}
@{pid}
@{pids}
@{PROC}
@{securityfs}
@{apparmorfs}
@{sys}
@{tid}
@{XDG_DESKTOP_DIR}

View File

@@ -393,6 +393,9 @@ extern int get_rlimit(const char *name);
extern char *process_var(const char *var);
extern int parse_mode(const char *mode);
extern int parse_X_mode(const char *X, int valid, const char *str_mode, int *mode, int fail);
void parse_label(char **ns, char **name, const char *label);
void parse_named_transition_target(struct named_transition *nt,
const char *target);
extern struct cod_entry *new_entry(char *ns, char *id, int mode, char *link_id);
/* returns -1 if value != true or false, otherwise 0 == false, 1 == true */

View File

@@ -225,8 +225,8 @@ SET_VAR_PREFIX @
SET_VARIABLE {SET_VAR_PREFIX}(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
BOOL_VARIABLE $(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
PATHNAME (\/|{SET_VARIABLE}{POST_VAR_ID}){ID}*
QPATHNAME \"(\/|{SET_VAR_PREFIX})([^\0"]|\\\")*\"
LABEL (\/|{SET_VARIABLE}{POST_VAR_ID}|{COLON}){ID}*
QUOTED_LABEL \"(\/|{SET_VAR_PREFIX}|{COLON})([^\0"]|\\\")*\"
OPEN_PAREN \(
CLOSE_PAREN \)
@@ -510,7 +510,7 @@ LT_EQUAL <=
}
<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
({IDS_NOEQ}|{PATHNAME}|{QUOTED_ID}) {
({IDS_NOEQ}|{LABEL}|{QUOTED_ID}) {
yylval.id = processid(yytext, yyleng);
RETURN_TOKEN(TOK_ID);
}
@@ -557,7 +557,7 @@ include/{WS} {
{CLOSE_BRACE} { RETURN_TOKEN(TOK_CLOSE); }
({PATHNAME}|{QPATHNAME}) {
({LABEL}|{QUOTED_LABEL}) {
yylval.id = processid(yytext, yyleng);
RETURN_TOKEN(TOK_ID);
}

View File

@@ -964,8 +964,10 @@ int main(int argc, char *argv[])
void *data);
struct dir_cb_data cb_data;
memset(&cb_data, 0, sizeof(struct dir_cb_data));
cb_data.dirname = profilename;
cb_data.cachedir = cacheloc;
cb_data.kernel_interface = kernel_interface;
cb = binary_input ? binary_dir_cb : profile_dir_cb;
if ((retval = dirat_for_each(AT_FDCWD, profilename,
&cb_data, cb))) {

View File

@@ -569,6 +569,65 @@ int parse_X_mode(const char *X, int valid, const char *str_mode, int *mode, int
return 1;
}
void parse_label(char **ns, char **name, const char *label)
{
const char *name_start = NULL;
char *_ns = NULL;
char *_name = NULL;
if (label[0] != ':') {
/* There is no namespace specified in the label */
name_start = label;
} else {
/* A leading ':' indicates that a namespace is specified */
const char *ns_start = label + 1;
const char *ns_end = strstr(ns_start, ":");
if (!ns_end)
yyerror(_("Namespace not terminated: %s\n"), label);
else if (ns_end - ns_start == 0)
yyerror(_("Empty namespace: %s\n"), label);
/**
* Handle either of the two namespace formats:
* 1) :ns:name
* 2) :ns://name
*/
name_start = ns_end + 1;
if (!strncmp(name_start, "//", 2))
name_start += 2;
_ns = strndup(ns_start, ns_end - ns_start);
if (!_ns)
yyerror(_("Memory allocation error."));
}
if (!strlen(name_start))
yyerror(_("Empty named transition profile name: %s\n"), label);
_name = strdup(name_start);
if (!_name) {
free(_ns);
yyerror(_("Memory allocation error."));
}
*ns = _ns;
*name = _name;
}
void parse_named_transition_target(struct named_transition *nt,
const char *target)
{
memset(nt, 0, sizeof(*nt));
if (!target) {
/* Return with nt->present set to 0 (thanks to the memset) */
return;
}
parse_label(&nt->ns, &nt->name, target);
nt->present = 1;
}
struct cod_entry *new_entry(char *ns, char *id, int mode, char *link_id)
{
struct cod_entry *entry = NULL;
@@ -724,7 +783,7 @@ static const char *capnames[] = {
"audit_write",
"audit_control",
"setfcap",
"mac_override"
"mac_override",
"syslog",
};

View File

@@ -258,8 +258,6 @@ void add_local_entry(Profile *prof);
%type <boolean> opt_profile_flag
%type <boolean> opt_flags
%type <boolean> opt_perm_mode
%type <id> opt_ns
%type <id> ns_id
%type <id> opt_id
%type <prefix> opt_prefix
%type <fmode> dbus_perm
@@ -299,11 +297,6 @@ opt_profile_flag: { /* nothing */ $$ = 0; }
| TOK_PROFILE { $$ = 1; }
| hat_start { $$ = 2; }
ns_id: TOK_COLON id_or_var TOK_COLON { $$ = $2; }
opt_ns: { /* nothing */ $$ = NULL; }
| ns_id { $$ = $1; }
opt_id: { /* nothing */ $$ = NULL; }
| TOK_ID { $$ = $1; }
@@ -318,7 +311,26 @@ profile_base: TOK_ID opt_id_or_var flags TOK_OPEN rules TOK_CLOSE
yyerror(_("Memory allocation error."));
}
prof->name = $1;
parse_label(&prof->ns, &prof->name, $1);
free($1);
/* Honor the --namespace-string command line option */
if (profile_ns) {
/**
* Print warning if the profile specified a namespace
* different than the one specified with the
* --namespace-string command line option
*/
if (prof->ns && strcmp(prof->ns, profile_ns))
pwarn("%s: -n %s overriding policy specified namespace :%s:\n",
progname, profile_ns, prof->ns);
free(prof->ns);
prof->ns = strdup(profile_ns);
if (!prof->ns)
yyerror(_("Memory allocation error."));
}
prof->attachment = $2;
if ($2 && !($2[0] == '/' || strncmp($2, "@{", 2) == 0))
yyerror(_("Profile attachment must begin with a '/' or variable."));
@@ -340,25 +352,18 @@ profile_base: TOK_ID opt_id_or_var flags TOK_OPEN rules TOK_CLOSE
};
profile: opt_profile_flag opt_ns profile_base
profile: opt_profile_flag profile_base
{
Profile *prof = $3;
if ($2)
PDEBUG("Matched: %s://%s { ... }\n", $2, $3->name);
else
PDEBUG("Matched: %s { ... }\n", $3->name);
Profile *prof = $2;
if ($3->name[0] != '/' && !($1 || $2))
if ($2->ns)
PDEBUG("Matched: :%s://%s { ... }\n", $2->ns, $2->name);
else
PDEBUG("Matched: %s { ... }\n", $2->name);
if ($2->name[0] != '/' && !($1 || $2->ns))
yyerror(_("Profile names must begin with a '/', namespace or keyword 'profile' or 'hat'."));
if ($2 && profile_ns) {
pwarn("%s: -n %s overriding policy specified namespace :%s:\n", progname, profile_ns, $2);
free($2);
prof->ns = strdup(profile_ns);
if (!prof->ns)
yyerror(_("Memory allocation error."));
} else
prof->ns = $2;
if ($1 == 2)
prof->flags.hat = 1;
$$ = prof;
@@ -1035,21 +1040,12 @@ id_or_var: TOK_SET_VAR { $$ = $1; };
opt_named_transition:
{ /* nothing */
$$.present = 0;
$$.ns = NULL;
$$.name = NULL;
parse_named_transition_target(&$$, NULL);
}
| TOK_ARROW id_or_var
{
$$.present = 1;
$$.ns = NULL;
$$.name = $2;
}
| TOK_ARROW ns_id id_or_var
{
$$.present = 1;
$$.ns = $2;
$$.name = $3;
parse_named_transition_target(&$$, $2);
free($2);
};
rule: file_rule { $$ = $1; }
@@ -1490,27 +1486,25 @@ change_profile_head: TOK_CHANGE_PROFILE opt_id
$$ = $2;
}
change_profile: change_profile_head TOK_END_OF_RULE
change_profile: change_profile_head opt_named_transition TOK_END_OF_RULE
{
struct cod_entry *entry;
char *rule = strdup("**");
if (!rule)
yyerror(_("Memory allocation error."));
PDEBUG("Matched change_profile,\n");
entry = new_entry(NULL, rule, AA_CHANGE_PROFILE, $1);
if (!entry)
yyerror(_("Memory allocation error."));
PDEBUG("change_profile,\n");
$$ = entry;
};
change_profile: change_profile_head TOK_ARROW opt_ns TOK_ID TOK_END_OF_RULE
{
struct cod_entry *entry;
PDEBUG("Matched change_profile: tok_id (:%s://%s)\n", $3 ? $3 : "", $4);
entry = new_entry($3, $4, AA_CHANGE_PROFILE, $1);
if ($2.present) {
PDEBUG("Matched change_profile: tok_id (:%s://%s)\n",
$2.ns ? $2.ns : "", $2.name);
entry = new_entry($2.ns, $2.name, AA_CHANGE_PROFILE, $1);
} else {
char *rule = strdup("**");
if (!rule)
yyerror(_("Memory allocation error."));
PDEBUG("Matched change_profile,\n");
entry = new_entry(NULL, rule, AA_CHANGE_PROFILE, $1);
}
if (!entry)
yyerror(_("Memory allocation error."));
PDEBUG("change_profile.entry: (%s)\n", entry->name);
$$ = entry;
};

View File

@@ -225,7 +225,7 @@ public:
std::string fqname(void)
{
if (parent)
return parent->fqname() + "://" + name;
return parent->fqname() + "//" + name;
else if (!ns)
return hname();
return ":" + std::string(ns) + "://" + hname();

View File

@@ -3,6 +3,7 @@
#=EXRESULT PASS
#
/usr/bin/foo {
network unspec,
network inet,
network ax25,
network ipx,

View File

@@ -0,0 +1,9 @@
#
#=DESCRIPTION basic unspec network tests
#=EXRESULT PASS
#
/usr/bin/foo {
network unspec stream,
network unspec dgram,
network unspec raw,
}

View File

@@ -0,0 +1,9 @@
#
#=DESCRIPTION namespace with no profile name
#=EXRESULT FAIL
# vim:syntax=apparmor
# Last Modified: Thu Feb 11 00:14:20 2016
#
:namespace: {
/does/not/exist r,
}

View File

@@ -0,0 +1,13 @@
#
#=DESCRIPTION collision same profile, same namespace with profile keyword
#=EXRESULT FAIL
# vim:syntax=apparmor
# Last Modified: Thu Feb 11 00:14:20 2016
#
profile :ns:/t {
/does/not/exist r,
}
profile :ns:/t {
/does/not/exist r,
}

View File

@@ -0,0 +1,13 @@
#
#=DESCRIPTION collision same profile, same namespace w/ and w/o profile keyword
#=EXRESULT FAIL
# vim:syntax=apparmor
# Last Modified: Thu Feb 11 00:14:20 2016
#
:ns:/t {
/does/not/exist r,
}
profile :ns:/t {
/does/not/exist r,
}

View File

@@ -0,0 +1,9 @@
#
#=DESCRIPTION no terminating ':' for ns namespace (w/ profile keyword)
#=EXRESULT FAIL
# vim:syntax=apparmor
# Last Modified: Thu Feb 11 00:14:20 2016
#
profile :ns/t {
/does/not/exist r,
}

View File

@@ -40,7 +40,7 @@ profile :foo:/does/not/exist2 {
/bin/echo uxuxuxuxux,
}
profile :foo:unattached {
profile :foo:unattached2 {
#include <includes/base>
/usr/X11R6/lib/lib*so* rrr,

View File

@@ -38,6 +38,9 @@
# /etc/resolvconf/run/resolv.conf
/{,var/}run/resolvconf/resolv.conf r,
/etc/resolvconf/run/resolv.conf r,
# on systems using systemd's networkd, /etc/resolv.conf is a symlink to
# /run/systemd/resolve/resolv.conf
/{,var/}run/systemd/resolve/resolv.conf r,
/etc/samba/lmhosts r,
/etc/services r,

View File

@@ -10,18 +10,18 @@
#
# ------------------------------------------------------------------
/usr/lib{,32,64}/python{2.[4-7],3.[0-4]}/**.{pyc,so} mr,
/usr/lib{,32,64}/python{2.[4-7],3.[0-4]}/**.{egg,py,pth} r,
/usr/lib{,32,64}/python{2.[4-7],3.[0-4]}/{site,dist}-packages/ r,
/usr/lib{,32,64}/python3.[0-4]/lib-dynload/*.so mr,
/usr/lib{,32,64}/python{2.[4-7],3.[0-5]}/**.{pyc,so} mr,
/usr/lib{,32,64}/python{2.[4-7],3.[0-5]}/**.{egg,py,pth} r,
/usr/lib{,32,64}/python{2.[4-7],3.[0-5]}/{site,dist}-packages/ r,
/usr/lib{,32,64}/python3.[0-5]/lib-dynload/*.so mr,
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-4]}/**.{pyc,so} mr,
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-4]}/**.{egg,py,pth} r,
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-4]}/{site,dist}-packages/ r,
/usr/local/lib{,32,64}/python3.[0-4]/lib-dynload/*.so mr,
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-5]}/**.{pyc,so} mr,
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-5]}/**.{egg,py,pth} r,
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-5]}/{site,dist}-packages/ r,
/usr/local/lib{,32,64}/python3.[0-5]/lib-dynload/*.so mr,
# Site-wide configuration
/etc/python{2.[4-7],3.[0-4]}/** r,
/etc/python{2.[4-7],3.[0-5]}/** r,
# shared python paths
/usr/share/{pyshared,pycentral,python-support}/** r,
@@ -34,4 +34,4 @@
/usr/lib/wx/python/*.pth r,
# python build configuration and headers
/usr/include/python{2.[4-7],3.[0-4]}*/pyconfig.h r,
/usr/include/python{2.[4-7],3.[0-5]}*/pyconfig.h r,

View File

@@ -23,3 +23,7 @@
/usr/local/share/ca-certificates/** r,
/var/lib/ca-certificates/ r,
/var/lib/ca-certificates/** r,
# acmetool
/var/lib/acme/certs/*/chain r,
/var/lib/acme/certs/*/cert r,

View File

@@ -16,3 +16,7 @@
/etc/ssl/ r,
/etc/ssl/** r,
# acmetool
/var/lib/acme/live/* r,
/var/lib/acme/certs/** r,
/var/lib/acme/keys/** r,

View File

@@ -38,6 +38,7 @@
/var/tmp/smtp_* rw,
/{var/,}run/dovecot/auth-token-secret.dat{,.tmp} rw,
/{var/,}run/dovecot/stats-user w,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.auth>

View File

@@ -1,6 +1,6 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2013 Christian Boltz
# Copyright (C) 2013-2016 Christian Boltz
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@@ -24,10 +24,65 @@
/etc/dovecot/** r,
/proc/*/mounts r,
owner /tmp/dovecot.lda.* rw,
/{var/,}run/dovecot/mounts r,
/usr/bin/doveconf mrix,
/usr/lib/dovecot/dovecot-lda mrix,
/usr/sbin/sendmail Cx,
# Site-specific additions and overrides. See local/README for details.
#include <local/usr.lib.dovecot.dovecot-lda>
profile /usr/sbin/sendmail flags=(attach_disconnected) {
# this profile is based on the usr.sbin.sendmail profile in extras
# and should support both postfix' and sendmail's sendmail binary
#include <abstractions/base>
#include <abstractions/consoles>
#include <abstractions/nameservice>
#include <abstractions/user-tmp>
#include <abstractions/postfix-common>
capability sys_ptrace,
/etc/aliases rw, # newaliases is a symlink to sendmail, so it's
/etc/aliases.db rw, # actually the same binary
/etc/fstab r,
/etc/hosts.allow r,
/etc/hosts.deny r,
/etc/mail/* r,
/etc/mail/statistics rw,
/etc/mtab r,
/etc/postfix/aliases r,
/etc/postfix/aliases.db rw, # newaliases again
/etc/sendmail.cf r,
/etc/sendmail.cw r,
/etc/shells r,
/proc/loadavg r,
/proc/net/if_inet6 r,
/root/.forward r,
/root/dead.letter w,
/usr/bin/procmail Px,
/usr/lib/postfix/master Px,
/usr/lib/postfix/showq Px,
/usr/lib/postfix/smtpd Px,
/usr/sbin/postalias Px,
/usr/sbin/postdrop Px,
/usr/sbin/postfix Px,
/usr/sbin/postqueue Px,
/usr/sbin/sendmail mrix,
/usr/sbin/sendmail.postfix mrix,
/usr/sbin/sendmail.sendmail mrix,
/{var/,}run/sendmail.pid rwl,
/{var/,}run/sm-client.pid rwl,
/{var/,}run/utmp rw,
/var/spool/clientmqueue/* rwl,
/var/spool/mail/* rwl,
/var/spool/mqueue/* rwl,
/var/spool/postfix/maildrop/* rwl,
/var/spool/postfix/public/pickup w,
/var/spool/postfix/public/qmgr w,
/var/spool/postfix/public/showq w,
}
}

View File

@@ -12,7 +12,7 @@
@{TFTP_DIR}=/var/tftp /srv/tftpboot
#include <tunables/global>
/usr/sbin/dnsmasq {
/usr/sbin/dnsmasq flags=(attach_disconnected) {
#include <abstractions/base>
#include <abstractions/dbus>
#include <abstractions/nameservice>

View File

@@ -31,6 +31,7 @@
/{var/cache,var/run,run}/nscd/{passwd,group,services,hosts,netgroup} rw,
/{,var/}run/{nscd/,}nscd.pid rwl,
/var/log/nscd.log rw,
@{PROC}/@{pid}/cmdline r,
@{PROC}/@{pid}/fd/ r,
@{PROC}/@{pid}/fd/* r,
@{PROC}/@{pid}/mounts r,

View File

@@ -17,6 +17,7 @@
capability net_bind_service,
capability setgid,
capability setuid,
capability sys_admin, # needed to store ACLS in the security.NTACL namespace
capability sys_resource,
capability sys_tty_config,

View File

@@ -178,6 +178,7 @@ TESTS=access \
mount \
mult_mount \
named_pipe \
namespaces \
net_raw \
open \
openat \

View File

@@ -29,6 +29,7 @@ fqsubbase="$pwd/changeprofile"
fqsubtest="$fqsubbase//$subtest"
subtest2="$pwd//sub2"
subtest3="$pwd//sub3"
nstest=":ns:changeprofile"
touch $file $subfile
@@ -70,3 +71,10 @@ runchecktest "CHANGEPROFILE_RE (nochange access file)" pass nochange $file
runchecktest_errno EACCES "CHANGEPROFILE_RE (nochange access subfile)" fail nochange $subfile
runchecktest_errno EACCES "CHANGEPROFILE_RE (access file)" fail $fqsubtest $file
runchecktest "CHANGEPROFILE_RE (access sub file)" pass $fqsubtest $subfile
genprofile --stdin <<EOF
$test { file, change_profile -> ${nstest}, }
$nstest { $subfile ${okperm}, }
EOF
runchecktest "CHANGEPROFILE_NS (access sub file)" pass $nstest $subfile
runchecktest "CHANGEPROFILE_NS (access file)" fail $nstest $file

View File

@@ -0,0 +1,76 @@
#! /bin/bash
# Copyright (C) 2016 Canonical, Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, version 2 of the
# License.
#=NAME namespaces
#=DESCRIPTION
# Verifies basic namespace functionality
#=END
pwd=`dirname $0`
pwd=`cd $pwd ; /bin/pwd`
bin=$pwd
. $bin/prologue.inc
requires_namespace_interface
# unique_ns - Print a randomly generated, unused namespace identifier to stdout
unique_ns() {
# racy way of generating a namespace name that is likely to be unique
local ns=$(mktemp --dry-run -dp /sys/kernel/security/apparmor/policy/namespaces -t test_namespaces_XXXXXX)
basename "$ns"
}
# genprofile_ns - Generate and load a profile using a randomly generated namespace
# $1: The profile name to use (without a namespace)
# $2: Non-zero if the 'profile' keyword should be prefixed to the declaration
#
# Returns the randomly generated namespace that the profile was loaded into
genprofile_ns() {
local prefix=""
local ns=$(unique_ns)
local prof=$1
if [ $2 -ne 0 ]; then
prefix="profile "
fi
# override the sys_profiles variable with a bad path so that genprofile
# doesn't perform profile load checking in the wrong policy namespace
echo "${prefix}:${ns}:${prof} {}" | sys_profiles="${sys_profiles}XXX" genprofile --stdin
echo "$ns"
}
# genprofile_ns_and_verify - Generate and load a profile into a namespace and
# verify the creation of the profile and namespace
# $1: A description of this test
# $2: Non-zero if the 'profile' keyword should be prefixed to the declaration
genprofile_ns_and_verify() {
local desc=$1
local prof="p"
local ns=$(genprofile_ns "$prof" $2)
[ -d /sys/kernel/security/apparmor/policy/namespaces/${ns} ]
local dir_created=$?
[ -d /sys/kernel/security/apparmor/policy/namespaces/${ns}/profiles/${prof}* ]
local prof_created=$?
removeprofile
if [ $dir_created -ne 0 ]; then
echo "Error: ${testname} failed. Test '${desc}' did not create the expected namespace directory in apparmorfs: policy/namespaces/${ns}"
testfailed
elif [ $prof_created -ne 0 ]; then
echo "Error: ${testname} failed. Test '${desc}' did not create the expected namespaced profile directory in apparmorfs: policy/namespaces/${ns}/profiles/${prof}"
testfailed
elif [ -n "$VERBOSE" ]; then
echo "ok: ${desc}"
fi
}
genprofile_ns_and_verify "NAMESPACES create unique ns (w/o profile keyword prefix)" 0
genprofile_ns_and_verify "NAMESPACES create unique ns (w/ profile keyword prefix)" 1

View File

@@ -49,6 +49,15 @@ requires_kernel_features()
fi
}
requires_namespace_interface()
{
if [ ! -e "/sys/kernel/security/apparmor/policy/namespaces" ]
then
echo "Namespaces in apparmorfs policy interface not supported. Skipping tests ..."
exit 0
fi
}
requires_query_interface()
{
if [ ! -e "/sys/kernel/security/apparmor/.access" ]

View File

@@ -47,6 +47,8 @@ int interp_status(int status)
# endif
# elif defined(__arm__) || defined(__powerpc__) || defined(__powerpc64__)
# define ARCH_REGS_STRUCT struct pt_regs
# elif defined(__s390__) || defined(__s390x__)
# define ARCH_REGS_STRUCT struct _user_regs_struct
# else
# error "Need to define ARCH_REGS_STRUCT for this architecture"
# endif

View File

@@ -52,6 +52,10 @@ runchecktest "TCP (accept, connect) low numbered port/bind cap" pass 23
genprofile network:inet
runchecktest "TCP (accept, connect) low numbered port/no bind cap" fail 23
# FAIL TEST - make sure that unspec doesn't match
genprofile network:unspec
runchecktest "TCP (accept, connect) wrong socket family" fail 23
exit 0
# PASS TEST - accept via interface

View File

@@ -26,7 +26,7 @@ PYTOOLS = aa-easyprof aa-genprof aa-logprof aa-cleanprof aa-mergeprof \
aa-status aa-unconfined
TOOLS = ${PERLTOOLS} ${PYTOOLS} aa-decode
PYSETUP = python-tools-setup.py
PYMODULES = $(wildcard apparmor/*.py)
PYMODULES = $(wildcard apparmor/*.py apparmor/rule/*.py)
MANPAGES = ${TOOLS:=.8} logprof.conf.5

View File

@@ -10,7 +10,7 @@
# ------------------------------------------------------------------
import apparmor.easyprof
from apparmor.easyprof import AppArmorException, error
from apparmor.easyprof import error
import os
import sys
@@ -61,12 +61,7 @@ if __name__ == "__main__":
for (binary, options) in profiles:
if len(profiles) > 1:
count += 1
try:
easyp = apparmor.easyprof.AppArmorEasyProfile(binary, options)
except AppArmorException as e:
error(e.value)
except Exception:
raise
easyp = apparmor.easyprof.AppArmorEasyProfile(binary, options)
if options.list_templates:
apparmor.easyprof.print_basefilenames(easyp.get_templates())
@@ -118,7 +113,4 @@ if __name__ == "__main__":
sys.stdout.write('%s\n' % easyp.gen_manifest(params))
else:
params['no_verify'] = options.no_verify
try:
easyp.output_policy(params, count, opt.output_directory)
except AppArmorException as e:
error(e)
easyp.output_policy(params, count, opt.output_directory)

View File

@@ -12,9 +12,19 @@
import re, os, sys, errno
# PLEASE NOTE: we try to keep aa-status as minimal as possible, for
# environments where installing all of the python utils and python
# apparmor module may not make sense. Please think carefully before
# importing anything from apparmor; see how the apparmor.fail import is
# handled below.
# setup exception handling
from apparmor.fail import enable_aa_exception_handler
enable_aa_exception_handler()
try:
from apparmor.fail import enable_aa_exception_handler
enable_aa_exception_handler()
except ImportError:
# just let normal python exceptions happen (LP: #1480492)
pass
def cmd_enabled():
'''Returns error code if AppArmor is not enabled'''

View File

@@ -431,7 +431,7 @@ def get_interpreter_and_abstraction(exec_target):
abstraction = 'abstractions/perl'
elif re.search('^python([23]|[23]\.[0-9]+)?$', interpreter):
abstraction = 'abstractions/python'
elif interpreter == 'ruby':
elif re.search('^ruby([0-9]+(\.[0-9]+)*)?$', interpreter):
abstraction = 'abstractions/ruby'
else:
abstraction = None
@@ -443,12 +443,12 @@ def get_inactive_profile(local_profile):
return {local_profile: extras[local_profile]}
return dict()
def profile_storage():
def profile_storage(profilename, hat, calledby):
# keys used in aa[profile][hat]:
# a) rules (as dict): alias, include, lvar
# b) rules (as hasher): allow, deny
# c) one for each rule class
# d) other: external, flags, name, profile, attachment, initial_comment,
# d) other: external, flags, name, profile, attachment, initial_comment, filename, info,
# profile_keyword, header_comment (these two are currently only set by set_profile_flags())
# Note that this function doesn't explicitely init all those keys (yet).
@@ -456,6 +456,9 @@ def profile_storage():
profile = hasher()
# profile['info'] isn't used anywhere, but can be helpful in debugging.
profile['info'] = {'profile': profilename, 'hat': hat, 'calledby': calledby}
profile['capability'] = CapabilityRuleset()
profile['change_profile'] = ChangeProfileRuleset()
profile['network'] = NetworkRuleset()
@@ -472,7 +475,7 @@ def profile_storage():
def create_new_profile(localfile, is_stub=False):
local_profile = hasher()
local_profile[localfile] = profile_storage()
local_profile[localfile] = profile_storage('NEW', localfile, 'create_new_profile()')
local_profile[localfile]['flags'] = 'complain'
local_profile[localfile]['include']['abstractions/base'] = 1
@@ -504,7 +507,7 @@ def create_new_profile(localfile, is_stub=False):
if re.search(hatglob, localfile):
for hat in sorted(cfg['required_hats'][hatglob].split()):
if not local_profile.get(hat, False):
local_profile[hat] = profile_storage()
local_profile[hat] = profile_storage('NEW', hat, 'create_new_profile() required_hats')
local_profile[hat]['flags'] = 'complain'
if not is_stub:
@@ -631,8 +634,6 @@ def activate_repo_profiles(url, profiles, complain):
def autodep(bin_name, pname=''):
bin_full = None
global repo_cfg
if not bin_name and pname.startswith('/'):
bin_name = pname
if not repo_cfg and not cfg['repository'].get('url', False):
repo_conf = apparmor.config.Config('shell', CONFDIR)
repo_cfg = repo_conf.read_config('repository.conf')
@@ -1143,6 +1144,7 @@ def handle_children(profile, hat, root):
detail = detail.replace('*', '\*')
detail = detail.replace('{', '\{')
detail = detail.replace('}', '\}')
detail = detail.replace('!', '\!')
# Give Execute dialog if x access requested for something that's not a directory
# For directories force an 'ix' Path dialog
@@ -1156,25 +1158,7 @@ def handle_children(profile, hat, root):
else:
do_execute = True
if mode & apparmor.aamode.AA_MAY_LINK:
regex_link = re.compile('^from (.+) to (.+)$')
match = regex_link.search(detail)
if match:
path = match.groups()[0]
target = match.groups()[1]
frommode = str_to_mode('lr')
if prelog[aamode][profile][hat]['path'].get(path, False):
frommode |= prelog[aamode][profile][hat]['path'][path]
prelog[aamode][profile][hat]['path'][path] = frommode
tomode = str_to_mode('lr')
if prelog[aamode][profile][hat]['path'].get(target, False):
tomode |= prelog[aamode][profile][hat]['path'][target]
prelog[aamode][profile][hat]['path'][target] = tomode
else:
continue
elif mode:
if mode:
path = detail
if prelog[aamode][profile][hat]['path'].get(path, False):
@@ -1491,7 +1475,7 @@ def handle_children(profile, hat, root):
if ynans == 'y':
hat = exec_target
if not aa[profile].get(hat, False):
aa[profile][hat] = profile_storage()
aa[profile][hat] = profile_storage(profile, hat, 'handle_children()')
aa[profile][hat]['profile'] = True
if profile != hat:
@@ -1612,7 +1596,7 @@ def ask_the_questions():
hats = [profile] + hats
for hat in hats:
log_obj[profile][hat] = profile_storage()
log_obj[profile][hat] = profile_storage(profile, hat, 'ask_the_questions()')
for capability in sorted(log_dict[aamode][profile][hat]['capability'].keys()):
capability_obj = CapabilityRule(capability, log_event=aamode)
@@ -2300,7 +2284,12 @@ def save_profiles():
oldprofile = aa[which][which]['filename']
else:
oldprofile = get_profile_filename(which)
newprofile = serialize_profile_from_old_profile(aa[which], which, '')
try:
newprofile = serialize_profile_from_old_profile(aa[which], which, '')
except AttributeError:
# see https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1528139
newprofile = "###\n###\n### Internal error while generating diff, please use '%s' instead\n###\n###\n" % _('View Changes b/w (C)lean profiles')
display_changes_with_comments(oldprofile, newprofile)
@@ -2600,7 +2589,7 @@ def parse_profile_data(data, file, do_include):
if do_include:
profile = file
hat = file
profile_data[profile][hat] = profile_storage()
profile_data[profile][hat] = profile_storage(profile, hat, 'parse_profile_data() do_include')
profile_data[profile][hat]['filename'] = file
for lineno, line in enumerate(data):
@@ -2619,7 +2608,7 @@ def parse_profile_data(data, file, do_include):
raise AppArmorException('Profile %(profile)s defined twice in %(file)s, last found in line %(line)s' %
{ 'file': file, 'line': lineno + 1, 'profile': combine_name(profile, hat) })
profile_data[profile][hat] = profile_storage()
profile_data[profile][hat] = profile_storage(profile, hat, 'parse_profile_data() profile_start')
if attachment:
profile_data[profile][hat]['attachment'] = attachment
@@ -2739,7 +2728,7 @@ def parse_profile_data(data, file, do_include):
list_var = strip_quotes(matches[0])
var_operation = matches[1]
value = strip_quotes(matches[2])
value = matches[2]
if profile:
if not profile_data[profile][hat].get('lvar', False):
@@ -2768,8 +2757,12 @@ def parse_profile_data(data, file, do_include):
if not profile:
raise AppArmorException(_('Syntax Error: Unexpected bare file rule found in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1 })
audit, allow, allow_keyword, comment = parse_modifiers(matches)
audit, deny, allow_keyword, comment = parse_modifiers(matches)
# TODO: honor allow_keyword and comment
if deny:
allow = 'deny'
else:
allow = 'allow'
mode = apparmor.aamode.AA_BARE_FILE_MODE
if not matches.group('owner'):
@@ -3024,7 +3017,7 @@ def parse_profile_data(data, file, do_include):
# if hat is already known, the filelist check some lines below will error out.
# nevertheless, just to be sure, don't overwrite existing profile_data.
if not profile_data[profile].get(hat, False):
profile_data[profile][hat] = profile_storage()
profile_data[profile][hat] = profile_storage(profile, hat, 'parse_profile_data() hat_def')
profile_data[profile][hat]['filename'] = file
flags = matches.group('flags')
@@ -3065,7 +3058,7 @@ def parse_profile_data(data, file, do_include):
else:
lastline = line
else:
raise AppArmorException(_('Syntax Error: Unknown line found in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1 })
raise AppArmorException(_('Syntax Error: Unknown line found in file %(file)s line %(lineno)s:\n %(line)s') % { 'file': file, 'lineno': lineno + 1, 'line': line })
# Below is not required I'd say
if not do_include:
@@ -3074,7 +3067,7 @@ def parse_profile_data(data, file, do_include):
if re.search(hatglob, parsed_prof):
for hat in cfg['required_hats'][hatglob].split():
if not profile_data[parsed_prof].get(hat, False):
profile_data[parsed_prof][hat] = profile_storage()
profile_data[parsed_prof][hat] = profile_storage(parsed_prof, hat, 'parse_profile_data() required_hats')
# End of file reached but we're stuck in a profile
if profile and not do_include:
@@ -3120,12 +3113,16 @@ def parse_unix_rule(line):
def separate_vars(vs):
"""Returns a list of all the values for a variable"""
data = set()
vs = vs.strip()
RE_VARS = re.compile('\s*((\".+?\")|([^\"]\S+))\s*(.*)$')
RE_VARS = re.compile('^(("[^"]*")|([^"\s]+))\s*(.*)$')
while RE_VARS.search(vs):
matches = RE_VARS.search(vs).groups()
data.add(strip_quotes(matches[0]))
vs = matches[3]
vs = matches[3].strip()
if vs:
raise AppArmorException('Variable assignments contains invalid parts (unbalanced quotes?): %s' % vs)
return data
@@ -3253,6 +3250,8 @@ def write_rlimits(prof_data, depth):
def var_transform(ref):
data = []
for value in ref:
if not value:
value = '""'
data.append(quote_if_needed(value))
return ' '.join(data)
@@ -3361,6 +3360,24 @@ def write_pivot_root(prof_data, depth):
data += write_pivot_root_rules(prof_data, depth, 'allow')
return data
def write_unix_rules(prof_data, depth, allow):
pre = ' ' * depth
data = []
# no unix rules, so return
if not prof_data[allow].get('unix', False):
return data
for unix_rule in prof_data[allow]['unix']:
data.append('%s%s' % (pre, unix_rule.serialize()))
data.append('')
return data
def write_unix(prof_data, depth):
data = write_unix_rules(prof_data, depth, 'deny')
data += write_unix_rules(prof_data, depth, 'allow')
return data
def write_link_rules(prof_data, depth, allow):
pre = ' ' * depth
data = []
@@ -3472,6 +3489,7 @@ def write_rules(prof_data, depth):
data += write_signal(prof_data, depth)
data += write_ptrace(prof_data, depth)
data += write_pivot_root(prof_data, depth)
data += write_unix(prof_data, depth)
data += write_links(prof_data, depth)
data += write_paths(prof_data, depth)
data += write_change_profile(prof_data, depth)
@@ -3591,6 +3609,11 @@ def serialize_profile_from_old_profile(profile_data, name, options):
write_filelist = deepcopy(filelist[prof_filename])
write_prof_data = deepcopy(profile_data)
# XXX profile_data / write_prof_data contain only one profile with its hats
# XXX this will explode if a file contains multiple profiles, see https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1528139
# XXX fixing this needs lots of write_prof_data[hat] -> write_prof_data[profile][hat] changes (and of course also a change in the calling code)
# XXX (the better option is a full rewrite of serialize_profile_from_old_profile())
if options: # and type(options) == dict:
if options.get('METADATA', False):
include_metadata = True
@@ -3628,6 +3651,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
'signal': write_signal,
'ptrace': write_ptrace,
'pivot_root': write_pivot_root,
'unix': write_unix,
'link': write_links,
'path': write_paths,
'change_profile': write_change_profile,
@@ -3643,6 +3667,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
'signal',
'ptrace',
'pivot_root',
'unix',
'link',
'path',
'change_profile',
@@ -3659,6 +3684,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
'signal': True, # not handled otherwise yet
'ptrace': True, # not handled otherwise yet
'pivot_root': True, # not handled otherwise yet
'unix': True, # not handled otherwise yet
'link': False,
'path': False,
'change_profile': False,
@@ -3689,7 +3715,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
if RE_PROFILE_START.search(line):
(profile, hat, attachment, flags, in_contained_hat, correct) = serialize_parse_profile_start(
line, prof_filename, None, profile, hat, write_prof_data[profile][hat]['profile'], write_prof_data[profile][hat]['external'], correct)
line, prof_filename, None, profile, hat, write_prof_data[hat]['profile'], write_prof_data[hat]['external'], correct)
if not write_prof_data[hat]['name'] == profile:
correct = False
@@ -3925,7 +3951,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
if matches[0]:
audit = mode
path_rule = write_prof_data[profile][hat][allow]['path'][ALL]
path_rule = write_prof_data[hat][allow]['path'][ALL]
if path_rule.get('mode', set()) & mode and \
(not audit or path_rule.get('audit', set()) & audit) and \
path_rule.get('file_prefix', set()):
@@ -4071,7 +4097,11 @@ def write_profile(profile):
os.rename(newprof.name, prof_filename)
changed.pop(profile)
if profile in changed:
changed.pop(profile)
else:
debug_logger.info("Unchanged profile written: %s (not listed in 'changed' list)" % profile)
original_aa[profile] = deepcopy(aa[profile])
def matchliteral(aa_regexp, literal):

View File

@@ -245,6 +245,15 @@ def user_perm(prof_dir):
return False
return True
def type_is_str(var):
''' returns True if the given variable is a str (or unicode string when using python 2)'''
if type(var) == str:
return True
elif sys.version_info[0] < 3 and type(var) == unicode: # python 2 sometimes uses the 'unicode' type
return True
else:
return False
class DebugLogger(object):
def __init__(self, module_name=__name__):
self.debugging = False

View File

@@ -8,12 +8,16 @@
#
# ------------------------------------------------------------------
from __future__ import print_function # needed in py2 for print('...', file=sys.stderr)
import cgitb
import os
import sys
import tempfile
import traceback
from apparmor.common import error
#
# Exception handling
#
@@ -27,8 +31,8 @@ def handle_exception(*exc_info):
(ex_cls, ex, tb) = exc_info
if ex_cls.__name__ == 'AppArmorException': # I didn't find a way to get this working with isinstance() :-/
print('')
print(ex.value)
print('', file=sys.stderr)
error(ex.value)
else:
(fd, path) = tempfile.mkstemp(prefix='apparmor-bugreport-', suffix='.txt')
file = os.fdopen(fd, 'w')
@@ -40,13 +44,13 @@ def handle_exception(*exc_info):
file.write('Please consider reporting a bug at https://bugs.launchpad.net/apparmor/\n')
file.write('and attach this file.\n')
print(''.join(traceback.format_exception(*exc_info)))
print('')
print('An unexpected error occoured!')
print('')
print('For details, see %s' % path)
print('Please consider reporting a bug at https://bugs.launchpad.net/apparmor/')
print('and attach this file.')
print(''.join(traceback.format_exception(*exc_info)), file=sys.stderr)
print('', file=sys.stderr)
print('An unexpected error occoured!', file=sys.stderr)
print('', file=sys.stderr)
print('For details, see %s' % path, file=sys.stderr)
print('Please consider reporting a bug at https://bugs.launchpad.net/apparmor/', file=sys.stderr)
print('and attach this file.', file=sys.stderr)
def enable_aa_exception_handler():
'''Setup handle_exception() as exception handler'''

View File

@@ -17,7 +17,7 @@ import re
import sys
import time
import LibAppArmor
from apparmor.common import AppArmorException, open_file_read, DebugLogger
from apparmor.common import AppArmorException, AppArmorBug, open_file_read, DebugLogger
from apparmor.aamode import validate_log_mode, log_str_to_mode, hide_log_mode, AA_MAY_EXEC
@@ -250,10 +250,10 @@ class ReadLog:
if e['operation'] == 'change_hat':
if aamode != 'HINT' and aamode != 'PERMITTING':
return None
profile = e['name']
profile = e['name2']
#hat = None
if '//' in e['name']:
profile, hat = e['name'].split('//')[:2]
if '//' in e['name2']:
profile, hat = e['name2'].split('//')[:2]
if not hat:
hat = profile
@@ -282,21 +282,22 @@ class ReadLog:
'rename_dest', 'unlink', 'rmdir', 'symlink_create', 'link',
'sysctl', 'getattr', 'setattr', 'xattr'] ):
# for some reason, we get file_perm and file_inherit log events without request_mask, see
# https://bugs.launchpad.net/apparmor/+bug/1466812/ and https://bugs.launchpad.net/apparmor/+bug/1509030
if e['operation'] in ['file_perm', 'file_inherit'] and e['request_mask'] is None:
# for some kernel-side reason, we get file-related log events without request_mask, see
# https://bugs.launchpad.net/apparmor/+bug/1466812/, https://bugs.launchpad.net/apparmor/+bug/1509030 and https://bugs.launchpad.net/apparmor/+bug/1540562
# request_mask can also be '', see https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1525119
if not e['request_mask']:
self.debug_logger.debug('UNHANDLED (missing request_mask): %s' % e)
return None
# Map c (create) to a and d (delete) to w (logging is more detailed than the profile language)
# Map c (create) and d (delete) to w (logging is more detailed than the profile language)
rmask = e['request_mask']
rmask = rmask.replace('c', 'a')
rmask = rmask.replace('c', 'w')
rmask = rmask.replace('d', 'w')
if not validate_log_mode(hide_log_mode(rmask)):
raise AppArmorException(_('Log contains unknown mode %s') % rmask)
dmask = e['denied_mask']
dmask = dmask.replace('c', 'a')
dmask = dmask.replace('c', 'w')
dmask = dmask.replace('d', 'w')
if not validate_log_mode(hide_log_mode(dmask)):
raise AppArmorException(_('Log contains unknown mode %s') % dmask)
@@ -385,7 +386,14 @@ class ReadLog:
event = self.parse_log_record(line)
#print(event)
if event:
self.add_event_to_tree(event)
try:
self.add_event_to_tree(event)
except AppArmorException as e:
ex_msg = ('%(msg)s\n\nThis error was caused by the log line:\n%(logline)s' %
{'msg': e.value, 'logline': line})
# when py3 only: Drop the original AppArmorException by passing None as the parent exception
raise AppArmorBug(ex_msg) # py3-only: from None
self.LOG.close()
self.logmark = ''
return self.log

View File

@@ -52,7 +52,11 @@ class BaseRule(object):
def __repr__(self):
classname = self.__class__.__name__
return '<%s> ' % classname + self.get_raw()
try:
raw_content = self.get_raw() # will fail for BaseRule
return '<%s> %s' % (classname, raw_content)
except NotImplementedError:
return '<%s (NotImplementedError - get_clean() not implemented?)>' % classname
@classmethod
def match(cls, raw_rule):
@@ -69,7 +73,7 @@ class BaseRule(object):
@classmethod
def _match(cls, raw_rule):
'''parse raw_rule and return regex match object'''
raise AppArmorBug("'%s' needs to implement _match(), but didn't" % (str(cls)))
raise NotImplementedError("'%s' needs to implement _match(), but didn't" % (str(cls)))
@classmethod
def parse(cls, raw_rule):
@@ -83,12 +87,12 @@ class BaseRule(object):
def _parse(cls, raw_rule):
'''returns a Rule object created from parsing the raw rule.
required to be implemented by subclasses; raise exception if not'''
raise AppArmorBug("'%s' needs to implement _parse(), but didn't" % (str(cls)))
raise NotImplementedError("'%s' needs to implement _parse(), but didn't" % (str(cls)))
# @abstractmethod FIXME - uncomment when python3 only
def get_clean(self, depth=0):
'''return clean rule (with default formatting, and leading whitespace as specified in the depth parameter)'''
raise AppArmorBug("'%s' needs to implement get_clean(), but didn't" % (str(self.__class__)))
raise NotImplementedError("'%s' needs to implement get_clean(), but didn't" % (str(self.__class__)))
def get_raw(self, depth=0):
'''return raw rule (with original formatting, and leading whitespace in the depth parameter)'''
@@ -121,7 +125,7 @@ class BaseRule(object):
# @abstractmethod FIXME - uncomment when python3 only
def is_covered_localvars(self, other_rule):
'''check if the rule-specific parts of other_rule is covered by this rule object'''
raise AppArmorBug("'%s' needs to implement is_covered_localvars(), but didn't" % (str(self)))
raise NotImplementedError("'%s' needs to implement is_covered_localvars(), but didn't" % (str(self)))
def is_equal(self, rule_obj, strict=False):
'''compare if rule_obj == self
@@ -142,7 +146,7 @@ class BaseRule(object):
# @abstractmethod FIXME - uncomment when python3 only
def is_equal_localvars(self, other_rule):
'''compare if rule-specific variables are equal'''
raise AppArmorBug("'%s' needs to implement is_equal_localvars(), but didn't" % (str(self)))
raise NotImplementedError("'%s' needs to implement is_equal_localvars(), but didn't" % (str(self)))
def severity(self, sev_db):
'''return severity of this rule, which can be:
@@ -178,7 +182,7 @@ class BaseRule(object):
def logprof_header_localvars(self):
'''return the headers (human-readable version of the rule) to display in aa-logprof for this rule object
returns {'label1': 'value1', 'label2': 'value2'} '''
raise AppArmorBug("'%s' needs to implement logprof_header(), but didn't" % (str(self)))
raise NotImplementedError("'%s' needs to implement logprof_header(), but didn't" % (str(self)))
def modifiers_str(self):
'''return the allow/deny and audit keyword as string, including whitespace'''
@@ -217,7 +221,10 @@ class BaseRuleset(object):
def __repr__(self):
classname = self.__class__.__name__
return '<%s>\n' % classname + '\n'.join(self.get_raw(1)) + '</%s>' % classname
if self.rules:
return '<%s>\n' % classname + '\n'.join(self.get_raw(1)) + '</%s>' % classname
else:
return '<%s (empty) />' % classname
def add(self, rule):
'''add a rule object'''
@@ -336,7 +343,7 @@ class BaseRuleset(object):
def get_glob_ext(self, path_or_rule):
'''returns the next possible glob with extension (for file rules only).
For all other rule types, raise an exception'''
raise AppArmorBug("get_glob_ext is not available for this rule type!")
raise NotImplementedError("get_glob_ext is not available for this rule type!")
def parse_comment(matches):

View File

@@ -14,7 +14,7 @@
# ----------------------------------------------------------------------
from apparmor.regex import RE_PROFILE_CAP
from apparmor.common import AppArmorBug, AppArmorException
from apparmor.common import AppArmorBug, AppArmorException, type_is_str
from apparmor.rule import BaseRule, BaseRuleset, parse_modifiers
import re
@@ -47,7 +47,7 @@ class CapabilityRule(BaseRule):
self.all_caps = True
self.capability = set()
else:
if type(cap_list) == str:
if type_is_str(cap_list):
self.capability = {cap_list}
elif type(cap_list) == list and len(cap_list) > 0:
self.capability = set(cap_list)

View File

@@ -14,7 +14,7 @@
# ----------------------------------------------------------------------
from apparmor.regex import RE_PROFILE_CHANGE_PROFILE, strip_quotes
from apparmor.common import AppArmorBug, AppArmorException
from apparmor.common import AppArmorBug, AppArmorException, type_is_str
from apparmor.rule import BaseRule, BaseRuleset, parse_modifiers, quote_if_needed
# setup module translations
@@ -48,7 +48,7 @@ class ChangeProfileRule(BaseRule):
self.all_execconds = False
if execcond == ChangeProfileRule.ALL:
self.all_execconds = True
elif type(execcond) == str:
elif type_is_str(execcond):
if not execcond.strip():
raise AppArmorBug('Empty exec condition in change_profile rule')
elif execcond.startswith('/') or execcond.startswith('@'):
@@ -62,7 +62,7 @@ class ChangeProfileRule(BaseRule):
self.all_targetprofiles = False
if targetprofile == ChangeProfileRule.ALL:
self.all_targetprofiles = True
elif type(targetprofile) == str:
elif type_is_str(targetprofile):
if targetprofile.strip():
self.targetprofile = targetprofile
else:

View File

@@ -16,7 +16,7 @@
import re
from apparmor.regex import RE_PROFILE_NETWORK
from apparmor.common import AppArmorBug, AppArmorException
from apparmor.common import AppArmorBug, AppArmorException, type_is_str
from apparmor.rule import BaseRule, BaseRuleset, parse_modifiers
# setup module translations
@@ -24,7 +24,7 @@ from apparmor.translations import init_translation
_ = init_translation()
network_domain_keywords = [ 'unix', 'inet', 'ax25', 'ipx', 'appletalk', 'netrom', 'bridge', 'atmpvc', 'x25', 'inet6',
network_domain_keywords = [ 'unspec', 'unix', 'inet', 'ax25', 'ipx', 'appletalk', 'netrom', 'bridge', 'atmpvc', 'x25', 'inet6',
'rose', 'netbeui', 'security', 'key', 'netlink', 'packet', 'ash', 'econet', 'atmsvc', 'rds', 'sna',
'irda', 'pppox', 'wanpipe', 'llc', 'can', 'tipc', 'bluetooth', 'iucv', 'rxrpc', 'isdn', 'phonet',
'ieee802154', 'caif', 'alg', 'nfc', 'vsock', 'mpls', 'ib' ]
@@ -66,7 +66,7 @@ class NetworkRule(BaseRule):
self.all_domains = False
if domain == NetworkRule.ALL:
self.all_domains = True
elif type(domain) == str:
elif type_is_str(domain):
if domain in network_domain_keywords:
self.domain = domain
else:
@@ -78,7 +78,7 @@ class NetworkRule(BaseRule):
self.all_type_or_protocols = False
if type_or_protocol == NetworkRule.ALL:
self.all_type_or_protocols = True
elif type(type_or_protocol) == str:
elif type_is_str(type_or_protocol):
if type_or_protocol in network_protocol_keywords:
self.type_or_protocol = type_or_protocol
elif type_or_protocol in network_type_keywords:

View File

@@ -16,7 +16,7 @@
import re
from apparmor.regex import RE_PROFILE_RLIMIT, strip_quotes
from apparmor.common import AppArmorBug, AppArmorException
from apparmor.common import AppArmorBug, AppArmorException, type_is_str
from apparmor.rule import BaseRule, BaseRuleset, parse_comment, quote_if_needed
# setup module translations
@@ -57,7 +57,7 @@ class RlimitRule(BaseRule):
if audit or deny or allow_keyword:
raise AppArmorBug('The audit, allow or deny keywords are not allowed in rlimit rules.')
if type(rlimit) == str:
if type_is_str(rlimit):
if rlimit in rlimit_all:
self.rlimit = rlimit
else:
@@ -70,7 +70,7 @@ class RlimitRule(BaseRule):
self.all_values = False
if value == RlimitRule.ALL:
self.all_values = True
elif type(value) == str:
elif type_is_str(value):
if not value.strip():
raise AppArmorBug('Empty value in rlimit rule')

View File

@@ -106,6 +106,13 @@
/usr/bin/killall = icn
/usr/bin/nice = icn
/usr/bin/perl = icn
/usr/bin/python = icn
/usr/bin/python2 = icn
/usr/bin/python2.7 = icn
/usr/bin/python3 = icn
/usr/bin/python3.3 = icn
/usr/bin/python3.4 = icn
/usr/bin/python3.5 = icn
/usr/bin/tr = icn
[required_hats]

View File

@@ -24,8 +24,10 @@ ifdef USE_SYSTEM
LD_LIBRARY_PATH=
PYTHONPATH=
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=..
PYTHONPATH=..:$(PYTHON_DIST_BUILD_PATH)
endif
.PHONY: __libapparmor
@@ -60,10 +62,10 @@ clean:
rm -rf __pycache__/ .coverage htmlcov
check: __libapparmor
export PYTHONPATH=$(PYTHONPATH) ; export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) ; $(foreach test, $(wildcard test-*.py), $(call pyalldo, $(test)))
export PYTHONPATH=$(PYTHONPATH) ; export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) ; $(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); $(COVERAGE_IGNORE_FAILURES_CMD) ; $(foreach test, $(wildcard test-*.py), $(PYTHON) -m coverage run --branch -p $(test); )
export PYTHONPATH=$(PYTHONPATH) ; export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH); $(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

View File

@@ -8,6 +8,8 @@
allow /usr/share/X11/locale/** r,
allow /home/*/** r,
unix (receive) type=dgram,
^foo {
/etc/fstab r,
capability dac_override,

View File

@@ -6,6 +6,8 @@
/usr/bin/a/simple/cleanprof/test/profile {
#include <abstractions/base>
unix (receive) type=dgram,
/home/*/** r,
/home/foo/** w,

View File

@@ -17,7 +17,8 @@ import os
from apparmor.aa import (check_for_apparmor, get_interpreter_and_abstraction, create_new_profile,
get_profile_flags, set_profile_flags, is_skippable_file, is_skippable_dir,
parse_profile_start, parse_profile_data, separate_vars, store_list_var, write_header, serialize_parse_profile_start)
parse_profile_start, parse_profile_data, separate_vars, store_list_var, write_header,
var_transform, serialize_parse_profile_start)
from apparmor.common import AppArmorException, AppArmorBug
class AaTestWithTempdir(AATest):
@@ -74,7 +75,7 @@ class AaTest_check_for_apparmor(AaTestWithTempdir):
class AaTest_create_new_profile(AATest):
tests = [
# file content expected interpreter expected abstraction (besides 'base')
('#!/bin/bash\ntrue', ('/bin/bash', 'abstractions/bash')),
('#!/bin/bash\ntrue', (u'/bin/bash', 'abstractions/bash')),
('foo bar', (None, None)),
]
def _run_test(self, params, expected):
@@ -88,16 +89,16 @@ class AaTest_create_new_profile(AATest):
self.assertEqual(profile[program][program]['allow']['path'][exp_interpreter_path]['audit'], set() )
self.assertEqual(profile[program][program]['allow']['path'][program]['mode'], {'r', '::r'} )
self.assertEqual(profile[program][program]['allow']['path'][program]['audit'], set() )
self.assertEqual(profile[program][program]['allow']['path'].keys(), {exp_interpreter_path, program} )
self.assertEqual(set(profile[program][program]['allow']['path'].keys()), {program, exp_interpreter_path} )
else:
self.assertEqual(profile[program][program]['allow']['path'][program]['mode'], {'r', '::r', 'm', '::m'} )
self.assertEqual(profile[program][program]['allow']['path'][program]['audit'], set() )
self.assertEqual(profile[program][program]['allow']['path'].keys(), {program} )
self.assertEqual(set(profile[program][program]['allow']['path'].keys()), {program} )
if exp_abstraction:
self.assertEqual(profile[program][program]['include'].keys(), {exp_abstraction, 'abstractions/base'})
self.assertEqual(set(profile[program][program]['include'].keys()), {exp_abstraction, 'abstractions/base'})
else:
self.assertEqual(profile[program][program]['include'].keys(), {'abstractions/base'})
self.assertEqual(set(profile[program][program]['include'].keys()), {'abstractions/base'})
class AaTest_get_interpreter_and_abstraction(AATest):
tests = [
@@ -114,6 +115,8 @@ class AaTest_get_interpreter_and_abstraction(AATest):
('#!/usr/bin/python3', ('/usr/bin/python3', 'abstractions/python')),
('#!/usr/bin/python4', ('/usr/bin/python4', None)), # python abstraction is only applied to py2 and py3
('#!/usr/bin/ruby', ('/usr/bin/ruby', 'abstractions/ruby')),
('#!/usr/bin/ruby2.2', ('/usr/bin/ruby2.2', 'abstractions/ruby')),
('#!/usr/bin/ruby1.9.1', ('/usr/bin/ruby1.9.1', 'abstractions/ruby')),
('#!/usr/bin/foobarbaz', ('/usr/bin/foobarbaz', None)), # we don't have an abstraction for "foobarbaz"
('foo', (None, None)), # no hashbang - not a script
]
@@ -484,20 +487,29 @@ class AaTest_separate_vars(AATest):
('' , set() ),
(' ' , set() ),
(' foo bar' , {'foo', 'bar' }),
('foo " ' , {'foo' }), # XXX " is ignored
(' " foo ' , {' "', 'foo' }), # XXX really?
('foo " ' , AppArmorException ),
(' " foo ' , AppArmorException ), # half-quoted
(' foo bar ' , {'foo', 'bar' }),
(' foo bar # comment' , {'foo', 'bar', 'comment' }), # XXX should comments be stripped?
(' foo bar # comment' , {'foo', 'bar', '#', 'comment'}), # XXX should comments be stripped?
('foo' , {'foo' }),
('"foo" "bar baz"' , {'foo', 'bar baz' }),
('foo "bar baz" xy' , {'foo', 'bar baz', 'xy' }),
('foo "bar baz ' , {'foo', 'bar', 'baz' }), # half-quoted
('foo "bar baz ' , AppArmorException ), # half-quoted
(' " foo" bar' , {' foo', 'bar' }),
(' " foo" bar x' , {' foo', 'bar', 'x' }),
('""' , {'' }), # empty value
('"" foo' , {'', 'foo' }), # empty value + 'foo'
('"" foo "bar"' , {'', 'foo', 'bar' }), # empty value + 'foo' + 'bar' (bar has superfluous quotes)
('"bar"' , {'bar' }), # 'bar' with superfluous quotes
]
def _run_test(self, params, expected):
result = separate_vars(params)
self.assertEqual(result, expected)
if expected == AppArmorException:
with self.assertRaises(expected):
separate_vars(params)
else:
result = separate_vars(params)
self.assertEqual(result, expected)
class AaTest_store_list_var(AATest):
@@ -579,6 +591,17 @@ class AaTest_write_header(AATest):
result = write_header(prof_data, depth, name, embedded_hat, write_flags)
self.assertEqual(result, [expected])
class AaTest_var_transform(AATest):
tests = [
(['foo', ''], 'foo ""' ),
(['foo', 'bar'], 'foo bar' ),
([''], '""' ),
(['bar baz', 'foo'], '"bar baz" foo' ),
]
def _run_test(self, params, expected):
self.assertEqual(var_transform(params), expected)
class AaTest_serialize_parse_profile_start(AATest):
def _parse(self, line, profile, hat, prof_data_profile, prof_data_external):
# 'correct' is always True in the code that uses serialize_parse_profile_start() (set some lines above the function call)

View File

@@ -20,34 +20,34 @@ import re
class TestBaserule(AATest):
def test_abstract__parse(self):
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
BaseRule._parse('foo')
def test_abstract__parse_2(self):
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
BaseRule.parse('foo')
def test_abstract__match(self):
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
BaseRule._match('foo')
def test_abstract__match2(self):
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
BaseRule.match('foo')
def test_abstract_get_clean(self):
obj = BaseRule()
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
obj.get_clean()
def test_is_equal_localvars(self):
obj = BaseRule()
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
obj.is_equal_localvars(BaseRule())
def test_is_covered_localvars(self):
obj = BaseRule()
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
obj.is_covered_localvars(None)
def test_parse_modifiers_invalid(self):
@@ -65,7 +65,7 @@ class TestBaserule(AATest):
def test_logprof_header_localvars(self):
obj = BaseRule()
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
obj.logprof_header_localvars()

View File

@@ -635,7 +635,7 @@ class CapabilityGlobTest(AATest):
self.assertEqual(self.ruleset.get_glob('capability net_raw,'), 'capability,')
def test_glob_ext(self):
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
self.ruleset.get_glob_ext('capability net_raw,')
class CapabilityDeleteTest(AATest):

View File

@@ -449,7 +449,7 @@ class ChangeProfileGlobTestAATest(AATest):
# self.assertEqual(self.ruleset.get_glob('change_profile /foo -> /bar,'), 'change_profile -> /bar,')
def test_glob_ext(self):
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
# get_glob_ext is not available for change_profile rules
self.ruleset.get_glob_ext('change_profile /foo -> /bar,')

32
utils/test/test-common.py Normal file
View File

@@ -0,0 +1,32 @@
#! /usr/bin/env python
# ------------------------------------------------------------------
#
# Copyright (C) 2015 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License published by the Free Software Foundation.
#
# ------------------------------------------------------------------
import unittest
from common_test import AATest, setup_all_loops
from apparmor.common import type_is_str
class TestIs_str_type(AATest):
tests = [
('foo', True),
(u'foo', True),
(42, False),
(True, False),
([], False),
]
def _run_test(self, params, expected):
self.assertEqual(type_is_str(params), expected)
setup_all_loops(__name__)
if __name__ == '__main__':
unittest.main(verbosity=2)

View File

@@ -441,7 +441,7 @@ class NetworkGlobTestAATest(AATest):
# self.assertEqual(self.ruleset.get_glob('network inet raw,'), 'network inet,')
def test_glob_ext(self):
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
# get_glob_ext is not available for network rules
self.ruleset.get_glob_ext('network inet raw,')

View File

@@ -125,6 +125,7 @@ exception_not_raised = [
'profile/flags/flags_bad_debug_3.sd',
'profile/flags/flags_bad_debug_4.sd',
'profile/simple_bad_no_close_brace4.sd',
'profile/profile_ns_bad8.sd', # 'profile :ns/t' without terminating ':'
'ptrace/bad_01.sd',
'ptrace/bad_02.sd',
'ptrace/bad_03.sd',
@@ -187,8 +188,6 @@ exception_not_raised = [
'vars/boolean/boolean_bad_6.sd',
'vars/boolean/boolean_bad_7.sd',
'vars/boolean/boolean_bad_8.sd',
'vars/vars_bad_1.sd',
'vars/vars_bad_2.sd',
'vars/vars_bad_3.sd',
'vars/vars_bad_4.sd',
'vars/vars_bad_5.sd',
@@ -198,7 +197,6 @@ exception_not_raised = [
'vars/vars_bad_trailing_comma_2.sd',
'vars/vars_bad_trailing_comma_3.sd',
'vars/vars_bad_trailing_comma_4.sd',
'vars/vars_bad_trailing_garbage_1.sd',
'vars/vars_dbus_bad_01.sd',
'vars/vars_dbus_bad_02.sd',
'vars/vars_dbus_bad_03.sd',

View File

@@ -411,7 +411,7 @@ class RlimitGlobTestAATest(AATest):
# self.assertEqual(self.ruleset.get_glob('rlimit /foo -> /bar,'), 'rlimit -> /bar,')
def test_glob_ext(self):
with self.assertRaises(AppArmorBug):
with self.assertRaises(NotImplementedError):
# get_glob_ext is not available for rlimit rules
self.ruleset.get_glob_ext('set rlimit cpu <= 100,')