2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-09-04 00:05:14 +00:00

Compare commits

..

83 Commits

Author SHA1 Message Date
John Johansen
884adcc58f repare for AppArmor 4.0 alpha 3 release
- update version file

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-09-18 03:51:29 -07:00
John Johansen
f5be84acdc Merge Utils: add support for the 'all,' rule
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1105
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-09-12 00:39:42 +00:00
John Johansen
5d940b2a47 Merge apparmor.vim: add support for the 'all' rule
... and update the bugreporting info in the header

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1106
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-09-12 00:37:44 +00:00
Christian Boltz
583d116871 apparmor.vim: add support for the 'all' rule
... and update the bugreporting info in the header
2023-09-10 18:38:48 +02:00
Christian Boltz
e361644d5a Utils: add support for the 'all,' rule 2023-09-10 18:07:41 +02:00
John Johansen
4e758a838d Merge parser: add support for a generic all rule type
Extend the policy syntax to have a rule that allows specifying all
permissions for all rule types.

  allow all,

This is useful for making blacklist based policy, but can also be
useful when combined with other rule prefixes, eg. to add audit
to all rules.

  audit access all,

Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1103
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-09-07 09:09:13 +00:00
John Johansen
197d00d21a parser: add support for a generic all rule type
Extend the policy syntax to have a rule that allows specifying all
permissions for all rule types.

  allow all,

This is useful for making blacklist based policy, but can also be
useful when combined with other rule prefixes, eg. to add audit
to all rules.

  audit access all,

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-09-07 01:30:15 -07:00
John Johansen
a9c5388f69 Merge parser: refactor network rules and prepare for fine grained network rules
This lays the ground work for fine grained network mediation. The switch to using the rules class brings the advantages of shared infrastructure and is needed for tracking all the different combinations possible with finer control.

In addition range generation and support for large numbers (needed for ipv6) are added. This patchset does not make policy visible changes to network rules.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1104
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-09-07 07:22:21 +00:00
Georgia Garcia
75ca0e7919 parser: track leading zeros required for ipv6 range regex generator
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-09-07 00:13:52 -07:00
Georgia Garcia
fb5f59024c parser: add uppercase hex on regex range generator
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-09-07 00:13:42 -07:00
Georgia Garcia
a71ac76e6d parser: add regex generator for range
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-09-07 00:13:29 -07:00
Georgia Garcia
2be9c431ca parser: add opt_cond in preparation to finer grained network mediation
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-09-07 00:13:15 -07:00
Georgia Garcia
05de4b82e7 parser: implement dedup of network rules
Since network rules don't use the "perms" attribute, it is using the
dedup class in which duplicate rules are removed.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-09-07 00:13:04 -07:00
Georgia Garcia
820f1fb5f2 parser: refactor network to use rule class as its base.
There is one significant difference in the encoding of the network
rules. Before this change, when the parser was encoding a "network,"
rule, it would generate an entry for every family and every
type/protocol. After this patch the parser should generate an entry
for every family, but the type/protocol is changed to .. in the pcre
syntax. There should be no difference in behavior.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-09-07 00:12:51 -07:00
John Johansen
65905b0c55 Merge aa-status: separate error messages from the regular output
using dfprintf for error messages subjects them to the other
output controls and can cause them to be surpressed when they
shouldn't.

Instead use a dedicated error function and add a quiet flag to
allow silencing errors.

Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1102
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-09-07 06:01:08 +00:00
John Johansen
1945ecbf19 aa-status: separate error messages from the regular output
using dfprintf for error messages subjects them to the other
output controls and can cause them to be surpressed when they
shouldn't.

Instead use a dedicated error function and add a quiet flag to
allow silencing errors.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-08-28 15:52:44 -07:00
John Johansen
11976c42e3 Merge binutils/aa_status.c: quiet verbose outputs when --json is specified
By default aa-status outputs with --verbose enabled - if --json is also
specified then aa-status would currently output in its first line "apparmor
module is loaded.":

aa-status --json | head -n1
apparmor module is loaded.

And only after this the actual json output would follow. This then results in
failures to parse this JSON output:

aa-status --json | jq .
parse error: Invalid numeric literal at line 1, column 9

This in turn then breaks tools / tests which expect the output of aa-status
--json to be purely json - e.g:
https://salsa.debian.org/apparmor-team/apparmor-profiles-extra/-/blob/debian/unstable/debian/tests/policy-is-loaded#L12

So ensure dprintf() etc do not output when --json is specified to restrict the
output of aa-status to pure JSON.

Signed-off-by: Alex Murray <alex.murray@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1097
Approved-by: Seth Arnold <seth.arnold@gmail.com>
Merged-by: John Johansen <john@jjmx.net>
2023-08-28 22:07:17 +00:00
John Johansen
327588f019 Merge parser: fix coverity scan 553075
coverity is reporting an overrun of the profile_mode_table

217     		if (merge_profile_mode(mode, rhs.mode) == MODE_CONFLICT)
>>>     CID 322989:    (OVERRUN)
>>>     Overrunning array "profile_mode_table" of 6 8-byte elements at element index 6 (byte offset 55) using index "this->mode" (which evaluates to 6).

this is because it is being indexed by the profile_mode enum which can
go up to a 6th entry. The code tests for MODE_CONFLICT before using
the table so it shouldn't trigger a bug today, but play it safe for
the future and also get rid of the coverity scan error by adding a
"conflict" entry to the mode_table.

Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1098
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
2023-08-28 22:05:45 +00:00
John Johansen
84e22b4cca Merge Docs: apparmor.d.pod document io_uring and userns rules
Documentation for io_uring and userns rules is missing from the
apparmor.d man page. Provide some basic documentation for them.

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/349
Signed-off-by: John Johansen <john.johansen@canonical.com>

Closes #349
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1099
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
2023-08-28 22:05:20 +00:00
John Johansen
248625ae00 Merge apparmor.d.pod: rename SIGNALS to SIGNAL
SIGNAL makes more sense because it's about a single signal.

Besides that, a9494f5523 introduced a (at
that point broken) usage of SIGNAL which becomes valid with this commit.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1100
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-28 22:05:04 +00:00
John Johansen
9ab72ffc7c Merge Install systemd unit on fedora/redhat systems
... instead of trying the old rc.apparmor.redhat initscript, which we no
longer ship since 2019519e34.

Also remove a superfluous level of indirection (rhel4 -> redhat).

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/350

Closes #350
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1101
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-28 22:04:53 +00:00
Christian Boltz
9be09aa909 Install systemd unit on fedora/redhat systems
... instead of trying the old rc.apparmor.redhat initscript, which we no
longer ship since 2019519e34.

Also remove a superfluous level of indirection (rhel4 -> redhat).

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/350
2023-08-28 18:46:20 +02:00
Christian Boltz
2bf35277a0 apparmor.d.pod: rename SIGNALS to SIGNAL
SIGNAL makes more sense because it's about a single signal.

Besides that, a9494f5523 introduced a (at
that point broken) usage of SIGNAL which becomes valid with this commit.
2023-08-28 18:33:03 +02:00
John Johansen
9db134223c Docs: apparmor.d.pod document io_uring and userns rules
Documentation for io_uring and userns rules is missing from the
apparmor.d man page. Provide some basic documentation for them.

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/349
Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-08-27 01:06:01 -07:00
John Johansen
ef56e60e06 parser: fix coverity scan 553075
coverity is reporting an overrun of the profile_mode_table

217     		if (merge_profile_mode(mode, rhs.mode) == MODE_CONFLICT)
>>>     CID 322989:    (OVERRUN)
>>>     Overrunning array "profile_mode_table" of 6 8-byte elements at element index 6 (byte offset 55) using index "this->mode" (which evaluates to 6).

this is because it is being indexed by the profile_mode enum which can
go up to a 6th entry. The code tests for MODE_CONFLICT before using
the table so it shouldn't trigger a bug today, but play it safe for
the future and also get rid of the coverity scan error by adding a
"conflict" entry to the mode_table.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-08-25 23:49:32 -07:00
John Johansen
96965c3da2 Merge Add support for new profile flags
This adds two new profile flags
* `interruptible` which can be used with prompt
* `kill.signal=XXX` which can be used to set the signal used by kill mode or the kill rule prefix

In addition it adds a few cleanups and fixes around profile flag handling

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1096
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
2023-08-26 05:05:42 +00:00
John Johansen
a9494f5523 parser: add kill.signal=XXX flag support
Add a flag that allows setting the signal used to kill the process.
This should not be normally used but can be very useful when
debugging applications, interaction with apparmor.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-08-25 10:16:51 -07:00
John Johansen
57985480ca parser: Fixup flags merge for disconnected_path
When merging flags with a disconnected_path specified, there is no
check for a conflict and we can just throw away (and leak) one of
the paths.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-08-25 07:19:01 -07:00
John Johansen
f10467556c parser: move flag init and merge to flagvals class
We have a basic flagvals class it makes sense to move the parser
code into it, to help make management easier going forward.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-08-25 07:19:01 -07:00
John Johansen
30707be87f parser: add interruptible flag
Allow indicating that prompt upcalls to userspace can be interrupted

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-08-25 07:18:50 -07:00
Alex Murray
f61fd42061 binutils/aa_status.c: quiet verbose outputs when --json is specified
By default aa-status outputs with --verbose enabled - if --json is also
specified then aa-status would currently output in its first line "apparmor
module is loaded.":

aa-status --json | head -n1
apparmor module is loaded.

And only after this the actual json output would follow. This then results in
failures to parse this JSON output:

aa-status --json | jq .
parse error: Invalid numeric literal at line 1, column 9

This in turn then breaks tools / tests which expect the output of aa-status
--json to be purely json - e.g:
https://salsa.debian.org/apparmor-team/apparmor-profiles-extra/-/blob/debian/unstable/debian/tests/policy-is-loaded#L12

So ensure dprintf() etc do not output when --json is specified to restrict the
output of aa-status to pure JSON.

Signed-off-by: Alex Murray <alex.murray@canonical.com>
2023-08-25 09:23:04 +09:30
John Johansen
847ab59e1c Merge collapse_log(): Attach null-* events to correct target profile
ask_exec() and ask_addhat() set
hashlog[aamode][full_profile]['final_name'].

While this was used to get profile and hat split, it was not used as key
for log_dict. This resulted in entries like
log_dict['PERMITTING']['foo//null-/usr/bin/cat']
which are obviously wrong.

Use final_name as log_dict key so that we end up with (assuming child
exec was selected)
log_dict['PERMITTING']['foo///usr/bin/cat']

This fixes a regression introduced in 3.1. Due to other changes in collapse_log() done in master, picking this into 3.1 isn't that easy. I'll submit a separate patch for 3.1.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1091
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-24 01:12:09 +00:00
John Johansen
427b58a4b6 Merge Ignore ´//null-` peers in signal and ptrace events
Ideally we'd update them to the chosen exec target - but until this is
implemented, it doesn't make sense to ask about adding a //null-* peer
to a profile.

I propose this patch for all branches.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1090
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-24 01:11:02 +00:00
John Johansen
aa20721be1 Merge Don't create local/* profile sniplets by default
... and document how to create them if you still want them.

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/337

Closes #337
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1089
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-23 23:26:54 +00:00
Georgia Garcia
5957aa49f5 Merge fix test specifying path on attach disconnected
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1094
Approved-by: John Johansen <john@jjmx.net>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-08-22 12:49:54 +00:00
Georgia Garcia
e133a9fc68 tests: remove superfluous attach_disconnected flag
Merge request https://gitlab.com/apparmor/apparmor/merge_requests/1084
makes it so attach_disconnected.path implies attach_disconnected, so
remove superfluous flag from tests.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-08-22 09:40:17 -03:00
Georgia Garcia
32307601a0 tests: fix test specifying path on attach disconnected
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-08-21 17:36:48 -03:00
Georgia Garcia
5b139521aa tests: replace individual socket permission to socket and put_old/socket
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-08-21 17:33:33 -03:00
John Johansen
2d7bd40606 Merge tests: fix userns setns opening pipe order
setns tests part of the userns could fail if the parent process opened
the child pipe to write it was done before the child opened the pipe
with read permissions.

From the fifo(7) man page:

A process can open a FIFO in nonblocking mode.  In this case, opening
for read‐only succeeds even if no one has opened on the write side yet
and opening for write‐only fails with ENXIO (no such device or
address) unless the other end has already been opened.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1093
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-21 16:13:28 +00:00
Georgia Garcia
24806f6f61 tests: fix userns setns opening pipe order
setns tests part of the userns could fail if the parent process opened
the child pipe to write it was done before the child opened the pipe
with read permissions.

From the fifo(7) man page:

A process can open a FIFO in nonblocking mode.  In this case, opening
for read‐only succeeds even if no one has opened on the write side yet
and opening for write‐only fails with ENXIO (no such device or
address) unless the other end has already been opened.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-08-21 12:04:51 -03:00
Christian Boltz
fbe68f0078 collapse_log(): use final_name
Now that we have `final_name` as shortcut for
`hashlog[aamode][full_profile]['final_name']`, update the code that used
the long version to make it more readable.
2023-08-20 15:50:20 +02:00
Christian Boltz
74265e8ded collapse_log(): Attach null-* events to correct target profile
ask_exec() and ask_addhat() set
hashlog[aamode][full_profile]['final_name'].

While this was used to get profile and hat split, it was not used as key
for log_dict. This resulted in entries like
log_dict['PERMITTING']['foo//null-/usr/bin/cat']
which are obviously wrong.

Use final_name as log_dict key so that we end up with (assuming child
exec was selected)
log_dict['PERMITTING']['foo///usr/bin/cat']
2023-08-20 15:49:59 +02:00
Christian Boltz
41df2ca366 Ignore ´//null-` peers in signal and ptrace events
Ideally we'd update them to the chosen exec target - but until this is
implemented, it doesn't make sense to ask about adding a //null-* peer
to a profile.
2023-08-20 11:53:08 +02:00
Christian Boltz
adf19138d5 Don't create local/* profile sniplets by default
... and document how to create them if you still want them.

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/337
2023-08-20 11:49:10 +02:00
John Johansen
1758b66c9d Merge Increase timeout in test-logprof.py
On (terribly, but real-world) slow buid hosts, running test-logprof.py
fails with a timeout. Increase the timeout so that even those build
hosts get enough time to finish the aa-logprof tests.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1087
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-17 11:04:14 +00:00
Christian Boltz
dd9b7b358f Increase timeout in test-logprof.py
On (terribly, but real-world) slow buid hosts, running test-logprof.py
fails with a timeout. Increase the timeout so that even those build
hosts get enough time to finish the aa-logprof tests.
2023-08-15 20:49:08 +02:00
John Johansen
b45c10d4de Merge Fix compability with Python < 3.9
str.removeprefix() was introduced in Python 3.9. Replace it with
backwards-compatible code.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1085
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-15 13:04:06 +00:00
Christian Boltz
c175e414c8 Fix compability with Python < 3.9
str.removeprefix() was introduced in Python 3.9. Replace it with
backwards-compatible code.
2023-08-15 12:40:39 +02:00
John Johansen
5f6e213d23 Prepare for AppArmor 4.0 alpha 2 release
- update version file

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-08-14 14:36:03 -07:00
John Johansen
d717adfc2f Merge parser: make attach_disconnected.path enable attach_disconnected
Currently you need to use attach_disconnected with
attach_disconnected.path=XXX to be able to attach to a different
location than / whic is ugly and redundant.

Make it so attach_disconnected.path implies attach_disconnected.

Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1084
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: John Johansen <john@jjmx.net>
2023-08-14 21:28:40 +00:00
John Johansen
4a21bd104c parser: make attach_disconnected.path enable attach_disconnected
Currently you need to use attach_disconnected with
attach_disconnected.path=XXX to be able to attach to a different
location than / whic is ugly and redundant.

Make it so attach_disconnected.path implies attach_disconnected.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-08-14 13:28:31 -07:00
John Johansen
f9036d57de Merge document that attach_disconnected.path expexts =PATH
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1083
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-14 20:28:26 +00:00
Christian Boltz
c87dd7985f document that attach_disconnected.path expexts =PATH 2023-08-14 22:22:02 +02:00
John Johansen
8eee4de83e Merge parser: add support for attach_disconnected.path
Add support for specifying the path prefix used when attach disconnected
is specified.

TODO: add regression tests

Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/661
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-14 14:15:25 +00:00
John Johansen
dbb2a1d0bb tests: regression, add basic regression tests
Add a couple basic attach disconnected regression tests

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-08-14 01:42:34 -07:00
John Johansen
b46b2662ff parser: add support for attach_disconnected.path
Add support for specifying the path prefix used when attach disconnected
is specified. The kernel supports prepending a different value than
/ when a path is disconnected. Expose through a profile flag.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-08-14 01:42:28 -07:00
John Johansen
03669ae3df Merge Add aa-logprof test framework
... and a simple test for a single (fake) event for ping.

Notes:
- to let aa-logprof work in the CI environment, we need to skip checking
  for the AppArmor mountpoint. Introduce --no-check-mountpoint for this.
- PYTHONPATH and LD_LIBRARY_PATH need to be explicitely forwarded when
  starting aa-logprof via subprocess.Popen()
- if the test runs with coverage enabled, it will also start aa-logprof
  with coverage (parameters copied from Makefile).

Speaking about coverage - this test adds 4% overall coverage, and 10%
more coverage for apparmor/aa.py.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1082
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-14 07:55:31 +00:00
John Johansen
941118c699 Merge profiles: allow for the default dovecot libexecdir
Though many Linux distros choose to pass _/usr/lib_ as the libexecdir while configuring dovecot, such as [Debian](https://sources.debian.org/src/dovecot/1%3A2.3.20%2Bdfsg1-1/debian/rules/#L132) and [Arch](https://gitlab.archlinux.org/archlinux/packaging/packages/dovecot/-/blob/main/PKGBUILD#L76), others like Alpine Linux and Gentoo don't pass anything as libexecdir, allowing it to default to _/usr/libexec_.

Both appear to be valid. From [FHS 3.0, Chapter 4.7](https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s07.html):
> Some previous versions of this document did not support _/usr/libexec_, despite it being standard practice in a number of environments. To accomodate this restriction, it became common practice to use _/usr/lib_ instead. Either practice is now acceptable, but each application must choose one way or the other to organize itself.

Allow for the default libexec subdir _/usr/libexec/dovecot_ as well as the more common
_/usr/lib/dovecot_.

Signed-off-by: Peter Levine <plevine457@gmail.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1080
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-14 07:54:19 +00:00
Christian Boltz
46debcc493 Add aa-logprof test framework
... and a simple test for a single (fake) event for ping.

Notes:
- to let aa-logprof work in the CI environment, we need to skip checking
  for the AppArmor mountpoint. Introduce --no-check-mountpoint for this.
- PYTHONPATH and LD_LIBRARY_PATH need to be explicitely forwarded when
  starting aa-logprof via subprocess.Popen()
- if the test runs with coverage enabled, it will also start aa-logprof
  with coverage (parameters copied from Makefile).

Speaking about coverage - this test adds 4% overall coverage, and 10%
more coverage for apparmor/aa.py.
2023-08-13 21:02:50 +02:00
Christian Boltz
65de34f462 Merge Add include if exists <tunables/$FILE.d> to all tunables
(except the deprecated tunables/sys)

This allows users to extend variables without editing the main tunables
files.

It also allows to cleanly introduce new tunable files (via
tunables/global.d) and new aliases (via tunables/alias.d).

Note: some files already had `include <tunables/$FILE.d>`. These get
changed to `include if exists`, and the comments for these includes get
unified.

Also extend make check to ensure that all tunables include tunables/$FILE.d

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/347

I propose this patch for 3.0..master.

Closes #347
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1077
Approved-by: timeout
Merged-by: Christian Boltz <apparmor@cboltz.de>
2023-08-13 15:02:45 +00:00
Peter Levine
37ffc6eac8 profiles: allow for the default dovecot libexecdir
Allow for the default libexec subdir, /usr/libexec/dovecot, as well
as the more common /usr/lib/dovecot.

Signed-off-by: Peter Levine <plevine457@gmail.com>
2023-08-03 01:30:42 -04:00
John Johansen
313366fbbc Merge parser: fix encoding of unix permissions for setopt and getopt
The permissions for AA_NET_OPT need to be bounded by mask so we can
make sure it matches when a policy specified only setopt or only
getopt. This was causing failures on the regression tests
unix_socket_pathname, unix_socket_abstract, unix_socket_unnamed and
unix_socket_autobind

Fixes: 44f3be091 ("parser: convert the stored audit from a bit mask to a bool")
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1079
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-08-02 11:13:40 +00:00
Georgia Garcia
64c1eb9cda tests: fix feature test
Fixes: 81f0b84d ("tests: fix feature checking if it's a directory")
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-08-01 16:20:20 -03:00
Georgia Garcia
210ad63cbf parser: fix encoding of unix permissions for setopt and getopt
The permissions for AA_NET_OPT need to be bounded by mask so we can
make sure it matches when a policy specified only setopt or only
getopt. This was causing failures on the regression tests
unix_socket_pathname, unix_socket_abstract, unix_socket_unnamed and
unix_socket_autobind

Fixes: 44f3be091 ("parser: convert the stored audit from a bit mask to a bool")
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-08-01 14:25:41 -03:00
John Johansen
93dff6a806 Merge parser: add support for prompt profile mode
Add support for the prompt profile flag. That allows policy to do an upcall to userspace if supported by the kernel and if a userspace daemon is available.

Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1062
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-07-31 04:10:33 +00:00
John Johansen
e5dace9ffd parser: add support for prompt profile mode
Add support for the prompt profile mode.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-07-30 20:50:03 -07:00
John Johansen
80f7add3b7 Merge Add option to log aa-logprof json input and output
Add a json_log option (default: disabled) to logprof.conf that enables
logging of all aa-logprof and aa-genprof input and output to a
/tmp/aa-jsonlog-* file.

This can be useful for debugging, and maybe also to create tests that do
a full aa-logprof run.

This patch introduces a minor behaviour change if aa-logprof errors out
on startup (for example if the config file is broken or the parser can't
be found):

Before:

```
$ aa-logprof --json
{"dialog": "apparmor-json-version","data": "2.12"}

ERROR: Can't find apparmor_parser at /sbin/apparmor_parser
```

After:

```
$ aa-logprof --json

ERROR: Can't find apparmor_parser at /sbin/apparmor_parser
```

Note that the json version line will not be printed if aa-logprof or
aa-genprof error out that early.

If there are no startup errors, the behaviour will not change.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1078
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-07-31 02:40:23 +00:00
John Johansen
c2b8ca1b28 Merge firefox: fix app name and DBus access
Minor fixes for the firefox profile:

1. The attachment spec was failing to match `/usr/lib/firefox-esr/firefox-esr` on Debian;
2. The `Mounted` method of `org.gtk.vfs.MountTracker` is received, not sent.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1076
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-07-31 02:39:44 +00:00
Christian Boltz
7dc0254b90 Add option to log aa-logprof json input and output
Add a json_log option (default: disabled) to logprof.conf that enables
logging of all aa-logprof and aa-genprof input and output to a
/tmp/aa-jsonlog-* file.

This can be useful for debugging, and maybe also to create tests that do
a full aa-logprof run.

This patch introduces a minor behaviour change if aa-logprof errors out
on startup (for example if the config file is broken or the parser can't
be found):

Before:

```
$ aa-logprof --json
{"dialog": "apparmor-json-version","data": "2.12"}

ERROR: Can't find apparmor_parser at /sbin/apparmor_parser
```

After:

```
$ aa-logprof --json

ERROR: Can't find apparmor_parser at /sbin/apparmor_parser
```

Note that the json version line will not be printed if aa-logprof or
aa-genprof error out that early.

If there are no startup errors, the behaviour will not change.
2023-07-30 21:28:35 +02:00
Christian Boltz
d6bc107940 make check: ensure that all tunables include tunables/$FILE.d
(except the deprecated tunables/sys)
2023-07-30 00:58:21 +02:00
Christian Boltz
5657799dc7 Add include if exists <tunables/$FILE.d> to all tunables
(except the deprecated tunables/sys)

This allows users to extend variables without editing the main tunables
files.

It also allows to cleanly introduce new tunable files (via
tunables/global.d) and new aliases (via tunables/alias.d).

Note: some files already had `include <tunables/$FILE.d>`. These get
changed to `include if exists`, and the comments for these includes get
unified.

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/347
2023-07-30 00:47:34 +02:00
Daniel Richard G
018efdeb9d firefox: fix app name and DBus access 2023-07-25 20:04:26 -04:00
John Johansen
17a521ff50 Merge ask_exec: Stop checking for CMD_nx (named exec)
... because it gets translated to CMD_px or CMD_cx earlier in the
function.

Also add a safety check for unknown answers.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1072
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-07-19 21:17:35 +00:00
John Johansen
853b138537 Merge libapparmor: fix dynamic linkage
It appears that lto1 does not support -dynamic, and that ld no longer
documents -dynamic. Which results in the following build failure
when usine lto1 for link time optimizations

```
[   45s] libtool: link: gcc -shared  -fPIC -DPIC  .libs/grammar.o .libs/libaalogparse.o .libs/kernel.o .libs/scanner.o .libs/private.o .libs/features.o .libs/kernel_interface.o .libs/policy_cache.o .libs/PMurHash.o    -flto-partition=none -O2 -fstack-protector-strong -flto=auto -dynamic -Wl,--version-script=../src/libapparmor.map -flto=auto   -pthread -Wl,-soname -Wl,libapparmor.so.1 -o .libs/libapparmor.so.1.17.0
[   45s] lto1: warning: unrecognized gcc debugging option: y
[   45s] lto1: warning: unrecognized gcc debugging option: n
[   45s] lto1: warning: unrecognized gcc debugging option: m
[   45s] lto1: warning: unrecognized gcc debugging option: i
[   45s] lto1: warning: unrecognized gcc debugging option: c
```

Switch to -Bdynamic which is documented.

Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1071
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: John Johansen <john@jjmx.net>
2023-07-19 21:16:36 +00:00
John Johansen
d6673f95d2 Merge tests: fix feature checking if it's a directory
The test to check if the kernel supports a feature covers two cases:
1. The file/directory indicates a feature is supported.
2. The feature is supported if it's in the contents of the file.

When the intended check is for case 1, and the file does not exist,
then the code checks if it's case 2, but since it was not supposed to
be, we end up grepping a directory, causing an error message. Fix this
by checking if we're grepping a file.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1074
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-07-19 21:16:23 +00:00
John Johansen
b2274a7f5d Merge Revert "policy: pin policy to 4.0 abi for dev"
This reverts commit 460c3d5b59.

The 4.0 ABI was pinned for the development cycle but it can cause
in-tree uses of parser/parser.conf to fail if it cannot find the 4.0
file in /etc/apparmor.d/abi/.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1073
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
2023-07-19 21:14:45 +00:00
Georgia Garcia
81f0b84da5 tests: fix feature checking if it's a directory
The test to check if the kernel supports a feature covers two cases:
1. The file/directory indicates a feature is supported.
2. The feature is supported if it's in the contents of the file.

When the intended check is for case 1, and the file does not exist,
then the code checks if it's case 2, but since it was not supposed to
be, we end up grepping a directory, causing an error message. Fix this
by checking if we're grepping a file.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-07-19 17:46:13 -03:00
Georgia Garcia
10529a6db7 Revert "policy: pin policy to 4.0 abi for dev"
This reverts commit 460c3d5b59.

The 4.0 ABI was pinned for the development cycle but it can cause
in-tree uses of parser/parser.conf to fail if it cannot find the 4.0
file in /etc/apparmor.d/abi/.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2023-07-19 17:37:24 -03:00
Christian Boltz
f1ab009a09 ask_exec: Stop checking for CMD_nx (named exec)
... because it gets translated to CMD_px or CMD_cx earlier in the
function.

Also add a safety check for unknown answers.
2023-07-19 21:41:43 +02:00
John Johansen
94082cc5e4 libapparmor: fix dynamic linkage
It appears that lto1 does not support -dynamic, and that ld no longer
documents -dynamic. Which results in the following build failure
when usine lto1 for link time optimizations

[   45s] libtool: link: gcc -shared  -fPIC -DPIC  .libs/grammar.o .libs/libaalogparse.o .libs/kernel.o .libs/scanner.o .libs/private.o .libs/features.o .libs/kernel_interface.o .libs/policy_cache.o .libs/PMurHash.o    -flto-partition=none -O2 -fstack-protector-strong -flto=auto -dynamic -Wl,--version-script=../src/libapparmor.map -flto=auto   -pthread -Wl,-soname -Wl,libapparmor.so.1 -o .libs/libapparmor.so.1.17.0
[   45s] lto1: warning: unrecognized gcc debugging option: y
[   45s] lto1: warning: unrecognized gcc debugging option: n
[   45s] lto1: warning: unrecognized gcc debugging option: m
[   45s] lto1: warning: unrecognized gcc debugging option: i
[   45s] lto1: warning: unrecognized gcc debugging option: c

Switch to -Bdynamic which is documented.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2023-07-17 21:04:11 -07:00
Steve Beattie
b6bd4f5421 parser: improve errors.py test coverage, convert to unittest.main
Merge the following commits:

- [12cf66ff0] parser/errors.py: check error message + error code for non-existent profiles
- [87896b949] parser/errors.py: convert to unittest.main()

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1070
Signed-off-by: Steve Beattie <steve.beattie@canonical.com>
Approved-by: John Johansen <john@jjmx.net>
2023-07-13 13:43:40 -05:00
Steve Beattie
87896b9496 parser/errors.py: convert to unittest.main()
Do this to simplify test identification, and also support the different
invocation mechanisms of unittest, like running individual tests.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1070
Signed-off-by: Steve Beattie <steve.beattie@canonical.com>
Approved-by: John Johansen <john@jjmx.net>
2023-07-13 13:40:42 -05:00
Steve Beattie
12cf66ff0b parser/errors.py: check error message + error code for non-existent profiles
Add tests for passing the parser a file that doesn't exist, a symlink
to a file that doesn't exist, and a directory that contains that
latter.  Also include tests for different levels of -j passed as an
argument. These tests are based on the fixing commit 1259319508
("parser: Fix parser failing to handle errors when setting up work")

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1070
Signed-off-by: Steve Beattie <steve.beattie@canonical.com>
Approved-by: John Johansen <john@jjmx.net>
2023-07-13 13:38:52 -05:00
157 changed files with 2957 additions and 608 deletions

1
.gitignore vendored
View File

@@ -29,6 +29,7 @@ parser/parser_yacc.h
parser/pod2htm*.tmp
parser/af_rule.o
parser/af_unix.o
parser/all_rule.o
parser/common_optarg.o
parser/dbus.o
parser/default_features.o

View File

@@ -181,6 +181,9 @@ $ make check # depends on the parser having been built first
$ make install
```
Note that the empty local/* profile sniplets no longer get created by default.
If you want them, run `make local` before running `make check`.
[Note that for the parser, binutils, and utils, if you only wish to build/use
some of the locale languages, you can override the default by passing
the LANGS arguments to make; e.g. make all install "LANGS=en_US fr".]

View File

@@ -115,6 +115,7 @@ static void free_processes(struct process *processes, size_t n) {
#define SHOW_PROCESSES 2
static int verbose = 1;
static bool quiet = false;
int opt_show = SHOW_PROFILES | SHOW_PROCESSES;
bool opt_json = false;
bool opt_pretty = false;
@@ -127,15 +128,21 @@ const char *opt_exe = ".*";
const char *profile_statuses[] = {"enforce", "complain", "prompt", "kill", "unconfined"};
const char *process_statuses[] = {"enforce", "complain", "prompt", "kill", "unconfined", "mixed"};
#define dprintf(...) \
#define eprintf(...) \
do { \
if (!quiet) \
fprintf(stderr, __VA_ARGS__); \
} while (0)
#define dprintf(...) \
do { \
if (verbose) \
if (verbose && !opt_json) \
printf(__VA_ARGS__); \
} while (0)
#define dfprintf(...) \
do { \
if (verbose) \
if (verbose && !opt_json) \
fprintf(__VA_ARGS__); \
} while (0)
@@ -149,14 +156,14 @@ static int open_profiles(FILE **fp)
ret = stat("/sys/module/apparmor", &st);
if (ret != 0) {
dfprintf(stderr, "apparmor not present.\n");
eprintf("apparmor not present.\n");
return AA_EXIT_DISABLED;
}
dprintf("apparmor module is loaded.\n");
ret = aa_find_mountpoint(&apparmorfs);
if (ret == -1) {
dfprintf(stderr, "apparmor filesystem is not mounted.\n");
eprintf("apparmor filesystem is not mounted.\n");
return AA_EXIT_NO_CONTROL;
}
@@ -169,9 +176,9 @@ static int open_profiles(FILE **fp)
*fp = fopen(apparmor_profiles, "r");
if (*fp == NULL) {
if (errno == EACCES) {
dfprintf(stderr, "You do not have enough privilege to read the profile set.\n");
eprintf("You do not have enough privilege to read the profile set.\n");
} else {
dfprintf(stderr, "Could not open %s: %s", apparmor_profiles, strerror(errno));
eprintf("Could not open %s: %s", apparmor_profiles, strerror(errno));
}
return AA_EXIT_NO_PERM;
}
@@ -201,7 +208,7 @@ static int get_profiles(FILE *fp, struct profile **profiles, size_t *n) {
char *tmpname = aa_splitcon(line, &status);
if (!tmpname) {
dfprintf(stderr, "Error: failed profile name split of '%s'.\n", line);
eprintf("Error: failed profile name split of '%s'.\n", line);
// skip this entry and keep processing
// else would be AA_EXIT_INTERNAL_ERROR;
continue;
@@ -344,7 +351,7 @@ static int get_processes(struct profile *profiles,
continue;
} else if (rc == -1 ||
asprintf(&exe, "/proc/%s/exe", entry->d_name) == -1) {
fprintf(stderr, "ERROR: Failed to allocate memory\n");
eprintf("ERROR: Failed to allocate memory\n");
ret = AA_EXIT_INTERNAL_ERROR;
goto exit;
} else if (mode) {
@@ -367,7 +374,7 @@ static int get_processes(struct profile *profiles,
// ensure enough space for NUL terminator
real_exe = calloc(PATH_MAX + 1, sizeof(char));
if (real_exe == NULL) {
fprintf(stderr, "ERROR: Failed to allocate memory\n");
eprintf("ERROR: Failed to allocate memory\n");
ret = AA_EXIT_INTERNAL_ERROR;
goto exit;
}
@@ -575,7 +582,7 @@ static int detailed_profiles(FILE *outf, filters_t *filters, bool json,
*/
subfilters.mode = &mode_filter;
if (regcomp(&mode_filter, profile_statuses[i], REG_NOSUB) != 0) {
dfprintf(stderr, "Error: failed to compile sub filter '%s'\n",
eprintf("Error: failed to compile sub filter '%s'\n",
profile_statuses[i]);
return AA_EXIT_INTERNAL_ERROR;
}
@@ -641,7 +648,7 @@ static int detailed_processes(FILE *outf, filters_t *filters, bool json,
*/
subfilters.mode = &mode_filter;
if (regcomp(&mode_filter, process_statuses[i], REG_NOSUB) != 0) {
dfprintf(stderr, "Error: failed to compile sub filter '%s'\n",
eprintf("Error: failed to compile sub filter '%s'\n",
profile_statuses[i]);
return AA_EXIT_INTERNAL_ERROR;
}
@@ -765,6 +772,7 @@ static int print_usage(const char *command, bool error)
" --json displays multiple data points in machine-readable JSON format\n"
" --pretty-json same data as --json, formatted for human consumption as well\n"
" --verbose (default) displays data points about loaded policy set\n"
" --quiet don't output error messages\n"
" -h [(legacy|filter)] this message, or info on the specified option\n"
" --help[=(legacy|filter)] this message, or info on the specified option\n",
command);
@@ -792,6 +800,7 @@ static int print_usage(const char *command, bool error)
#define ARG_EXE 143
#define ARG_PROMPT 144
#define ARG_VERBOSE 'v'
#define ARG_QUIET 'q'
#define ARG_HELP 'h'
static int parse_args(int argc, char **argv)
@@ -809,6 +818,7 @@ static int parse_args(int argc, char **argv)
{"json", no_argument, 0, ARG_JSON},
{"pretty-json", no_argument, 0, ARG_PRETTY},
{"verbose", no_argument, 0, ARG_VERBOSE},
{"quiet", no_argument, 0, ARG_QUIET},
{"help", 2, 0, ARG_HELP},
{"count", no_argument, 0, ARG_COUNT},
{"show", 1, 0, ARG_SHOW},
@@ -830,6 +840,9 @@ static int parse_args(int argc, char **argv)
/* default opt_mode */
/* default opt_show */
break;
case ARG_QUIET:
quiet = true;
break;
case ARG_HELP:
if (!optarg) {
print_usage(argv[0], false);
@@ -838,7 +851,7 @@ static int parse_args(int argc, char **argv)
} else if (strcmp(optarg, "filters") == 0) {
usage_filters();
} else {
dfprintf(stderr, "Error: Invalid --help option '%s'.\n", optarg);
eprintf("Error: Invalid --help option '%s'.\n", optarg);
print_usage(argv[0], true);
break;
}
@@ -906,7 +919,7 @@ static int parse_args(int argc, char **argv)
} else if (strcmp(optarg, "processes") == 0) {
opt_show = SHOW_PROCESSES;
} else {
dfprintf(stderr, "Error: Invalid --show option '%s'.\n", optarg);
eprintf("Error: Invalid --show option '%s'.\n", optarg);
print_usage(argv[0], true);
break;
}
@@ -928,7 +941,7 @@ static int parse_args(int argc, char **argv)
break;
default:
dfprintf(stderr, "Error: Invalid command.\n");
eprintf("Error: Invalid command.\n");
print_usage(argv[0], true);
break;
}
@@ -953,7 +966,7 @@ int main(int argc, char **argv)
if (argc > 1) {
int pos = parse_args(argc, argv);
if (pos < argc) {
dfprintf(stderr, "Error: Unknown options.\n");
eprintf("Error: Unknown options.\n");
print_usage(progname, true);
}
} else {
@@ -965,24 +978,24 @@ int main(int argc, char **argv)
init_filters(&filters, &filter_set);
if (regcomp(filters.mode, opt_mode, REG_NOSUB) != 0) {
dfprintf(stderr, "Error: failed to compile mode filter '%s'\n",
eprintf("Error: failed to compile mode filter '%s'\n",
opt_mode);
return AA_EXIT_INTERNAL_ERROR;
}
if (regcomp(filters.profile, opt_profiles, REG_NOSUB) != 0) {
dfprintf(stderr, "Error: failed to compile profiles filter '%s'\n",
eprintf("Error: failed to compile profiles filter '%s'\n",
opt_profiles);
ret = AA_EXIT_INTERNAL_ERROR;
goto out;
}
if (regcomp(filters.pid, opt_pid, REG_NOSUB) != 0) {
dfprintf(stderr, "Error: failed to compile ps filter '%s'\n",
eprintf("Error: failed to compile ps filter '%s'\n",
opt_pid);
ret = AA_EXIT_INTERNAL_ERROR;
goto out;
}
if (regcomp(filters.exe, opt_exe, REG_NOSUB) != 0) {
dfprintf(stderr, "Error: failed to compile exe filter '%s'\n",
eprintf("Error: failed to compile exe filter '%s'\n",
opt_exe);
ret = AA_EXIT_INTERNAL_ERROR;
goto out;
@@ -997,7 +1010,7 @@ int main(int argc, char **argv)
outf_save = outf;
outf = open_memstream(&buffer, &buffer_size);
if (!outf) {
dfprintf(stderr, "Failed to open memstream: %m\n");
eprintf("Failed to open memstream: %m\n");
return AA_EXIT_INTERNAL_ERROR;
}
}
@@ -1008,7 +1021,7 @@ int main(int argc, char **argv)
*/
ret = get_profiles(fp, &profiles, &nprofiles);
if (ret != 0) {
dfprintf(stderr, "Failed to get profiles: %d....\n", ret);
eprintf("Failed to get profiles: %d....\n", ret);
goto out;
}
@@ -1032,7 +1045,7 @@ int main(int argc, char **argv)
ret = get_processes(profiles, nprofiles, &processes, &nprocesses);
if (ret != 0) {
dfprintf(stderr, "Failed to get processes: %d....\n", ret);
eprintf("Failed to get processes: %d....\n", ret);
} else if (opt_count) {
ret = simple_filtered_process_count(outf, &filters,
processes, nprocesses);
@@ -1058,14 +1071,14 @@ int main(int argc, char **argv)
outf = outf_save;
json = cJSON_Parse(buffer);
if (!json) {
dfprintf(stderr, "Failed to parse json output");
eprintf("Failed to parse json output");
ret = AA_EXIT_INTERNAL_ERROR;
goto out;
}
pretty = cJSON_Print(json);
if (!pretty) {
dfprintf(stderr, "Failed to print pretty json");
eprintf("Failed to print pretty json");
ret = AA_EXIT_INTERNAL_ERROR;
goto out;
}

View File

@@ -1 +1 @@
4.0.0~alpha1
4.0.0~alpha3

View File

@@ -59,7 +59,7 @@ lib_LTLIBRARIES = libapparmor.la
noinst_HEADERS = grammar.h parser.h scanner.h af_protos.h private.h PMurHash.h
libapparmor_la_SOURCES = grammar.y libaalogparse.c kernel.c scanner.c private.c features.c kernel_interface.c policy_cache.c PMurHash.c
libapparmor_la_LDFLAGS = -version-info $(AA_LIB_CURRENT):$(AA_LIB_REVISION):$(AA_LIB_AGE) -XCClinker -dynamic -pthread \
libapparmor_la_LDFLAGS = -version-info $(AA_LIB_CURRENT):$(AA_LIB_REVISION):$(AA_LIB_AGE) -XCClinker -Bdynamic -pthread \
-Wl,--version-script=$(top_srcdir)/src/libapparmor.map
pkgconfigdir = $(libdir)/pkgconfig

View File

@@ -99,15 +99,15 @@ EXTRA_CFLAGS+=-DPACKAGE=\"${NAME}\" -DLOCALEDIR=\"${LOCALEDIR}\"
SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
parser_alias.c common_optarg.c lib.c network.c \
parser_alias.c common_optarg.c lib.c network.cc \
mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \
af_rule.cc af_unix.cc policy_cache.c default_features.c userns.cc \
mqueue.cc io_uring.cc
mqueue.cc io_uring.cc all_rule.cc
STATIC_HDRS = af_rule.h af_unix.h capability.h common_optarg.h dbus.h \
file_cache.h immunix.h lib.h mount.h network.h parser.h \
parser_include.h parser_version.h policy_cache.h policydb.h \
profile.h ptrace.h rule.h signal.h userns.h mqueue.h io_uring.h \
common_flags.h
common_flags.h bignum.h all_rule.h
SPECIAL_HDRS = parser_yacc.h unit_test.h base_cap_names.h
GENERATED_HDRS = af_names.h generated_af_names.h \
@@ -295,7 +295,7 @@ signal.o: signal.cc $(HDRS)
ptrace.o: ptrace.cc $(HDRS)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
network.o: network.c $(HDRS)
network.o: network.cc $(HDRS)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
default_features.o: default_features.c $(HDRS)
@@ -322,6 +322,9 @@ mqueue.o: mqueue.cc $(HDRS)
io_uring.o: io_uring.cc $(HDRS)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
all_rule.o: all_rule.cc $(HDRS)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_version.h: Makefile
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
@mv -f .ver $@
@@ -373,13 +376,8 @@ tests: apparmor_parser ${TESTS}
$(AAREOBJECT): FORCE
$(MAKE) -C $(AAREDIR) CFLAGS="$(EXTRA_CXXFLAGS)"
.PHONY: install-rhel4
install-rhel4: install-redhat
.PHONY: install-redhat
install-redhat:
install -m 755 -d $(DESTDIR)/etc/init.d
install -m 755 rc.apparmor.$(subst install-,,$@) $(DESTDIR)/etc/init.d/apparmor
install-redhat: install-systemd
.PHONY: install-suse
install-suse: install-systemd
@@ -410,9 +408,9 @@ DISTRO=$(shell if [ -f /etc/slackware-version ] ; then \
if [ "$$(rpm --eval '0%{?suse_version}')" != "0" ] ; then \
echo suse ;\
elif [ "$$(rpm --eval '%{_host_vendor}')" = redhat ] ; then \
echo rhel4 ;\
echo redhat ;\
elif [ "$$(rpm --eval '0%{?fedora}')" != "0" ] ; then \
echo rhel4 ;\
echo redhat ;\
else \
echo unknown ;\
fi ;\

View File

@@ -108,6 +108,9 @@ unix_rule::unix_rule(unsigned int type_p, audit_t audit_p, rule_mode_t rule_mode
perms = AA_VALID_NET_PERMS;
audit = audit_p;
rule_mode = rule_mode_p;
/* if this constructor is used, then there's already a
* downgraded network_rule in profile */
downgrade = false;
}
unix_rule::unix_rule(perms_t perms_p, struct cond_entry *conds,
@@ -190,7 +193,7 @@ static void writeu16(std::ostringstream &o, int v)
void unix_rule::downgrade_rule(Profile &prof) {
perms_t mask = (perms_t) -1;
if (!prof.net.allow && !prof.alloc_net_table())
if (!prof.net.allow && !prof.net.alloc_net_table())
yyerror(_("Memory allocation error."));
if (sock_type_n != -1)
mask = 1 << sock_type_n;
@@ -198,6 +201,11 @@ void unix_rule::downgrade_rule(Profile &prof) {
prof.net.allow[AF_UNIX] |= mask;
if (audit == AUDIT_FORCE)
prof.net.audit[AF_UNIX] |= mask;
const char *error;
network_rule *netv8 = new network_rule(AF_UNIX, sock_type_n);
if(!netv8->add_prefix({audit, rule_mode, owner}, error))
yyerror(error);
prof.rule_ents.push_back(netv8);
} else {
/* deny rules have to be dropped because the downgrade makes
* the rule less specific meaning it will make the profile more
@@ -317,7 +325,8 @@ int unix_rule::gen_policy_re(Profile &prof)
* older kernels and be enforced to the best of the old network
* rules ability
*/
downgrade_rule(prof);
if (downgrade)
downgrade_rule(prof);
if (!features_supports_unix) {
if (features_supports_network || features_supports_networkv8) {
/* only warn if we are building against a kernel
@@ -413,7 +422,7 @@ int unix_rule::gen_policy_re(Profile &prof)
tmp << "..";
buf = tmp.str();
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_OPT),
map_perms(mask & AA_NET_OPT),
map_perms(audit == AUDIT_FORCE ? AA_NET_OPT : 0),
parseopts))
goto fail;

View File

@@ -36,6 +36,7 @@ class unix_rule: public af_rule {
public:
char *addr;
char *peer_addr;
bool downgrade = true;
unix_rule(unsigned int type_p, audit_t audit_p, rule_mode_t rule_mode_p);
unix_rule(perms_t perms, struct cond_entry *conds,

130
parser/all_rule.cc Normal file
View File

@@ -0,0 +1,130 @@
/*
* Copyright (c) 2023
* Canonical Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Canonical Ltd.
*/
#include "profile.h"
#include "all_rule.h"
#include "af_unix.h"
#include "dbus.h"
#include "io_uring.h"
#include "mqueue.h"
#include "ptrace.h"
#include "signal.h"
#include "userns.h"
#include "mount.h"
#include "parser.h"
#include <iomanip>
#include <string>
#include <iostream>
#include <sstream>
void all_rule::add_implied_rules(Profile &prof)
{
prefix_rule_t *rule;
const prefixes *prefix = this;
rule = new unix_rule(0, audit, rule_mode);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new dbus_rule(0, NULL, NULL);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new io_uring_rule(0, NULL, NULL);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new mqueue_rule(0, NULL);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new ptrace_rule(0, NULL);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new signal_rule(0, NULL);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new userns_rule(0, NULL);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new mnt_rule(NULL, NULL, NULL, NULL, 0);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new mnt_rule(NULL, NULL, NULL, NULL, AA_DUMMY_REMOUNT);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new mnt_rule(NULL, NULL, NULL, NULL, AA_MAY_UMOUNT);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new mnt_rule(NULL, NULL, NULL, NULL, AA_MAY_PIVOTROOT);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new network_rule(NULL);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
/* rules that have not been converted to use rule.h */
//file
{
const char *error;
struct cod_entry *entry;
char *path = strdup("/{**,}");
int perms = ((AA_BASE_PERMS & ~AA_EXEC_TYPE) |
(AA_MAY_EXEC));
if (rule_mode != RULE_DENY)
perms |= AA_EXEC_INHERIT;
/* duplicate to other permission set */
perms |= perms << AA_OTHER_SHIFT;
if (!path)
yyerror(_("Memory allocation error."));
entry = new_entry(path, perms, NULL);
if (!entry_add_prefix(entry, *prefix, error)) {
yyerror(_("%s"), error);
}
add_entry_to_policy(&prof, entry);
}
// caps
{
if (prefix->owner)
yyerror(_("owner prefix not allowed on capability rules"));
if (rule_mode == RULE_DENY && audit == AUDIT_FORCE) {
prof.caps.deny |= 0xffffffffffffffff;
} else if (rule_mode == RULE_DENY) {
prof.caps.deny |= 0xffffffffffffffff;
prof.caps.quiet |= 0xffffffffffffffff;
} else {
prof.caps.allow |= 0xffffffffffffffff;
if (audit != AUDIT_UNSPECIFIED)
prof.caps.audit |= 0xffffffffffffffff;
}
}
// TODO: rlimit
}

70
parser/all_rule.h Normal file
View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2023
* Canonical Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Canonical Ltd.
*/
#ifndef __AA_ALL_H
#define __AA_ALL_H
#include "rule.h"
#define AA_IO_URING_OVERRIDE_CREDS AA_MAY_APPEND
#define AA_IO_URING_SQPOLL AA_MAY_CREATE
#define AA_VALID_IO_URING_PERMS (AA_IO_URING_OVERRIDE_CREDS | \
AA_IO_URING_SQPOLL)
class all_rule: public prefix_rule_t {
void move_conditionals(struct cond_entry *conds);
public:
char *label;
all_rule(void): prefix_rule_t(RULE_TYPE_ALL) { }
virtual bool valid_prefix(const prefixes &p, const char *&error) {
if (p.owner) {
error = _("owner prefix not allowed on all rules");
return false;
}
return true;
};
int expand_variables(void)
{
return 0;
}
virtual ostream &dump(ostream &os) {
prefix_rule_t::dump(os);
os << "all";
return os;
}
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const
{
return prefix_rule_t::cmp(rhs);
};
virtual void add_implied_rules(Profile &prof);
virtual int gen_policy_re(Profile &prof unused) { return RULE_OK; };
protected:
virtual void warn_once(const char *name unused, const char *msg unused) { };
virtual void warn_once(const char *name unused) { };
};
#endif /* __AA_ALL_H */

View File

@@ -113,9 +113,11 @@ B<XATTR VALUE FILEGLOB> = I<FILEGLOB>
B<PROFILE FLAG CONDS> = [ 'flags=' ] '(' comma or white space separated list of I<PROFILE FLAGS> ')'
B<PROFILE FLAGS> = I<PROFILE MODE> | I<AUDIT_MODE> | 'mediate_deleted' | 'attach_disconnected' | 'chroot_relative' | 'debug'
B<PROFILE FLAGS> = I<PROFILE MODE> | I<AUDIT_MODE> | 'mediate_deleted'
| 'attach_disconnected' | 'attach_disconneced.path='I<ABS PATH> | 'chroot_relative'
| 'debug' | 'interruptible' | 'kill.signal='I<SIGNAL>
B<PROFILE MODE> = 'enforce' | 'complain' | 'kill' | 'unconfined'
B<PROFILE MODE> = 'enforce' | 'complain' | 'kill' | 'unconfined' | 'prompt'
B<AUDIT MODE> = 'audit'
@@ -123,7 +125,7 @@ B<RULES> = [ ( I<LINE RULES> | I<COMMA RULES> ',' | I<BLOCK RULES> )
B<LINE RULES> = ( I<COMMENT> | I<INCLUDE> ) [ '\r' ] '\n'
B<COMMA RULES> = ( I<CAPABILITY RULE> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<UNIX RULE> | I<FILE RULE> | I<LINK RULE> | I<CHANGE_PROFILE RULE> | I<RLIMIT RULE> | I<DBUS RULE> | I<MQUEUE RULE> )
B<COMMA RULES> = ( I<CAPABILITY RULE> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<UNIX RULE> | I<FILE RULE> | I<LINK RULE> | I<CHANGE_PROFILE RULE> | I<RLIMIT RULE> | I<DBUS RULE> | I<MQUEUE RULE> | I<IO_URING RULE> | I<USERNS RULE> | I<ALL RULE>)
B<BLOCK RULES> = ( I<SUBPROFILE> | I<HAT> | I<QUALIFIER BLOCK> )
@@ -190,6 +192,16 @@ B<MQUEUE LABEL> = 'label' '=' '(' '"' I<AARE> '"' | I<AARE> ')'
B<MQUEUE NAME> = I<AARE>
B<USERNS RULE> = [ I<QUALIFIERS> ] 'userns' [ I<USERNS ACCESS PERMISSIONS> ]
B<USERNS ACCESS PERMISSIONS> = ( 'create' )
B<IO_URING RULE> = [ I<QUALIFIERS> ] 'io_uring' [ I<IO_URING ACCESS PERMISSIONS> [ I<IO_URING LABEL> ]
B<IO_URING ACCESS PERMISSIONS> = ( 'sqpoll' | 'override_creds' )
B<IO_URING LABEL> = 'label' '=' '(' '"' I<AARE> '"' | I<AARE> ')'
B<PIVOT ROOT RULE> = [ I<QUALIFIERS> ] pivot_root [ oldroot=I<OLD PUT FILEGLOB> ] [ I<NEW ROOT FILEGLOB> ] [ '-E<gt>' I<PROFILE NAME> ]
B<SOURCE FILEGLOB> = I<FILEGLOB>
@@ -218,9 +230,9 @@ B<SIGNAL ACCESS> = ( 'r' | 'w' | 'rw' | 'read' | 'write' | 'send' | 'receive' )
B<SIGNAL SET> = 'set' '=' '(' I<SIGNAL LIST> ')'
B<SIGNAL LIST> = Comma or space separated list of I<SIGNALS>
B<SIGNAL LIST> = Comma or space separated list of I<SIGNAL>s
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> = ( '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>
@@ -334,6 +346,8 @@ B<EXEC_MODE> = ( 'safe' | 'unsafe' )
B<EXEC COND> = I<FILEGLOB>
B<ALL RULE> = 'all'
=back
All resources and programs need a full path. There may be any number of
@@ -459,6 +473,11 @@ profile replacement. This mode is should not be used under regular
deployment but can be useful during debugging and some system
initialization scenarios.
=item B<prompt> This mode allows task mediation to send an up call to
userspace to ask for a decision when there isn't a rule covering the
permission request. If userspace does not respond then the access
will be denied.
=back
=head4 Audit Mode
@@ -486,6 +505,10 @@ though they are part of the namespace. WARNING this mode is unsafe and
can result in aliasing and access to objects that should not be
allowed. Its intent is a debug and policy development tool.
=item B<attach_disconnected.path>=I<ABS PATH> Like attach_disconnected, but
attach disconnected objects to the supplied path instead of the root of
the namespace.
=item B<chroot_relative> This forces file names to be relative to a
chroot and behave as if the chroot is a mount namespace.
@@ -495,6 +518,11 @@ flags to control what messages will be output. Its effect is kernel
dependent, and it should never appear in policy except when trying
to debug kernel or policy problems.
=item B<interruptible> Enables interrupts for prompt upcall to userspace.
=item B<kill.signal>=I<SIGNAL> This changes the signal that will be
sent by AppArmor when in kill mode or a kill rule has been violated.
=back
=head2 Access Modes
@@ -1122,6 +1150,89 @@ Example AppArmor Message Queue rules:
# Allow create permission for a SYSV queue of label foo
mqueue create label=foo 123,
=head2 User Namespace Rules
User namespaces are part of many sandboxing and containerization
solutions. They provide a way for a non-system root process to be
root within the container. Unfortunately this opens up attack surface
in the kernel and has been part of several exploit chains. As such
AppArmor can be used to restrict the creation of user namespaces to
select processes.
User namespace permission are implied when a rule does not explicitly
state an access list. The rule becomes more restrictive as further
information is specified.
Note: user namespace creation may be restricted so that it is not
available to unprivieged unconfined processes. If this is the case any
process trying to create user namespaces will require a profile that
allows the necessary permissions.
=over 4
=item B<create>
Allow creation of user namespaces.
=back
Example userns rules:
=over 4
# Allow all userns perms
userns,
# Allow creation of a userns
userns create,
=back
=head2 IO_URing Rules
AppArmor supports mediation of the new Linux high speed IO interface.
There is limited mediation at this time to just a few permissions at
the moment.
IO Uring permission are implied when a rule does not explicitly state
an access list. The rule becomes more restrictive as further
information is specified.
Note: io_uring access may be restricted so that it is not available to
unprivileged unconfined processes. If this is the case any process
trying to use io_uring will require a profile that allows the
necessary io_uring permissions.
=over 4
=item B<sqpoll>
All the task confined by the profile to spawn a io_uring polling
thread.
=item B<override_creds>
Grants the task confined by the profile to override (change) its
credentials to the specified label, when executing an io_uring
operation.
=back
Example IO_URING rules:
=over 4
# Allow io_uring operations
io_ring,
# Allow creation of a polling thread
io_uring sqpoll,
# Allow task to override credentials during io_uring operation
io_uring override_creds label=new_creds,
=back
=head2 Pivot Root Rules
AppArmor mediates changing of the root filesystem through the pivot_root(2)
@@ -1495,6 +1606,26 @@ Not all kernels support B<safe> mode and the parser will downgrade rules to
B<unsafe> mode in that situation. If no exec mode is specified, the default is
B<safe> mode in kernels that support it.
=head2 all rule
The all rule is used to add a generic rule for all supported rule types.
This is useful when policy wants to define a black list instead of
white list, but can also be useful to add an access qualifier to all
rules.
Eg. Black list
allow all,
# begin blacklist
deny file,
deny unix,
Eg. Adding audit qualifier
audit access all,
=head2 rlimit rules
AppArmor can set and control the resource limits associated with a

235
parser/bignum.h Normal file
View File

@@ -0,0 +1,235 @@
/*
* Copyright (c) 2023
* Canonical Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#ifndef __AA_BIGNUM_H
#define __AA_BIGNUM_H
#include <iostream>
#include <vector>
#include <sstream>
#include <algorithm>
#include <string>
class bignum
{
public:
std::vector<uint8_t> data;
uint64_t sad = 543;
uint8_t base;
bool negative = false;
bignum () {}
bignum (unsigned long val) {
if (val == 0)
data.push_back(val);
else {
while(val > 0) {
data.push_back(val % 10);
val /= 10;
}
}
base = 10;
}
bignum (const char *val) {
while (*val) {
data.push_back(*val - 48);
val++;
}
std::reverse(data.begin(), data.end());
base = 10;
}
bignum (const uint8_t val[16]) {
size_t i;
bool flag = true;
for (i = 0; i < 16; i++) {
if (flag && (val[i] & 0xF0) >> 4 != 0)
flag = false;
if (!flag)
data.push_back((val[i] & 0xF0) >> 4);
if (flag && (val[i] & 0x0F) != 0)
flag = false;
if (!flag)
data.push_back(val[i] & 0x0F);
}
std::reverse(data.begin(), data.end());
base = 16;
}
bignum operator+(const bignum &brhs) const {
bignum b1 = this->size() < brhs.size() ? *this : brhs;
bignum b2 = this->size() < brhs.size() ? brhs : *this;
bignum result;
result.base = this->base;
uint8_t carryover = 0;
uint8_t sum;
size_t i;
for (i = 0; i < b1.size(); i++) {
sum = b1[i] + b2[i] + carryover;
if (sum > base - 1)
carryover = 1;
else
carryover = 0;
result.data.push_back(sum % base);
}
for (; i < b2.size(); i++) {
sum = b2[i] + carryover;
if (sum > base - 1)
carryover = 1;
else
carryover = 0;
result.data.push_back(sum % base);
}
if (carryover != 0)
result.data.push_back(carryover);
return result;
}
bignum operator-(const bignum &brhs) const {
bignum b1 = this->size() < brhs.size() ? *this : brhs;
bignum b2 = this->size() < brhs.size() ? brhs : *this;
bignum result;
result.negative = *this < brhs;
result.base = this->base;
int8_t borrow = 0;
int8_t sub;
size_t i;
for (i = 0; i < b1.size(); i++) {
sub = b2[i] - b1[i] - borrow;
if (sub < 0) {
sub += base;
borrow = 1;
} else
borrow = 0;
result.data.push_back(sub);
}
for (; i < b2.size(); i++) {
sub = b2[i] - borrow;
if (sub < 0) {
sub += base;
borrow = 1;
} else
borrow = 0;
result.data.push_back(sub);
}
if (borrow) {
int8_t tmp = result.data[result.size() - 1] -= base;
tmp *= -1;
result.data[result.size() - 1] = tmp;
}
while (result.size() > 1 && result.data[result.size() - 1] == 0)
result.data.pop_back();
return result;
}
bool operator>=(const bignum &rhs) const {
return cmp_bignum(this->data, rhs.data) >= 0;
}
bool operator<=(const bignum &rhs) const {
return cmp_bignum(this->data, rhs.data) <= 0;
}
bool operator>(const bignum &rhs) const {
return cmp_bignum(this->data, rhs.data) > 0;
}
bool operator<(const bignum &rhs) const {
return cmp_bignum(this->data, rhs.data) < 0;
}
int operator[](int index) const {
return this->data[index];
}
friend std::ostream &operator<<(std::ostream &os, bignum &bn);
size_t size() const {
return data.size();
}
/*
returns:
- 0, if the lhs and rhs are equal;
- a negative value if lhs is less than rhs;
- a positive value if lhs is greater than rhs.
*/
int cmp_bignum(std::vector<uint8_t> lhs, std::vector<uint8_t> rhs) const
{
if (lhs.size() > rhs.size())
return 1;
else if (lhs.size() < rhs.size())
return -1;
else {
/* assumes the digits are stored in reverse order */
std::reverse(lhs.begin(), lhs.end());
std::reverse(rhs.begin(), rhs.end());
for (size_t i = 0; i < lhs.size(); i++) {
if (lhs[i] > rhs[i])
return 1;
if (lhs[i] < rhs[i])
return -1;
}
}
return 0;
}
static bignum lower_bound_regex(bignum val)
{
/* single digit numbers reduce to 0 */
if (val.size() == 1) {
val.data[0] = 0;
return val;
}
for (auto& j : val.data) {
uint8_t tmp = j;
j = 0;
if (tmp != val.base - 1) {
break;
}
if (&j == &val.data[val.size()-2]) {
val.data[val.size()-1] = 1;
break;
}
}
return val;
}
static bignum upper_bound_regex(bignum val)
{
for (auto& j : val.data) {
uint8_t tmp = j;
j = val.base - 1;
if (tmp != 0) {
break;
}
}
return val;
}
};
inline std::ostream &operator<<(std::ostream &os, bignum &bn)
{
std::stringstream ss;
bignum tmp = bn;
std::reverse(tmp.data.begin(), tmp.data.end());
for (auto i : tmp.data)
ss << std::hex << (int) i;
os << ss.str();
return os;
};
#endif /* __AA_BIGNUM_H */

View File

@@ -12,7 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact or Canonical Ltd.
* along with this program; if not, contact Canonical Ltd.
*/
#include "common_optarg.h"

View File

@@ -12,7 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact or Canonical Ltd.
* along with this program; if not, contact Canonical Ltd.
*/
#ifndef __AA_IO_URING_H

View File

@@ -16,10 +16,6 @@
* Ltd.
*/
#include <stdlib.h>
#include <string.h>
#include <sys/apparmor.h>
#include <iomanip>
#include <string>
#include <sstream>
@@ -28,9 +24,9 @@
#include "lib.h"
#include "parser.h"
#include "profile.h"
#include "parser_yacc.h"
#include "network.h"
#define ALL_TYPES 0x43e
int parse_net_perms(const char *str_mode, perms_t *mode, int fail)
{
@@ -119,7 +115,7 @@ static struct network_tuple network_mappings[] = {
/* FIXME: af_names.h is missing AF_LLC, AF_TIPC */
/* mapped types */
{"inet", AF_INET, "raw", SOCK_RAW,
"tcp", 1 << RAW_TCP},
"tcp", 1 << RAW_TCP},
{"inet", AF_INET, "raw", SOCK_RAW,
"udp", 1 << RAW_UDP},
{"inet", AF_INET, "raw", SOCK_RAW,
@@ -239,21 +235,21 @@ size_t get_af_max() {
return af_max;
}
struct aa_network_entry *new_network_ent(unsigned int family,
unsigned int type,
unsigned int protocol)
{
struct aa_network_entry *new_entry;
new_entry = (struct aa_network_entry *) calloc(1, sizeof(struct aa_network_entry));
if (new_entry) {
new_entry->family = family;
new_entry->type = type;
new_entry->protocol = protocol;
new_entry->next = NULL;
}
return new_entry;
}
const char *net_find_af_name(unsigned int af)
{
size_t i;
if (af < 0 || af > get_af_max())
return NULL;
for (i = 0; i < sizeof(network_mappings) / sizeof(*network_mappings); i++) {
if (network_mappings[i].family == af)
return network_mappings[i].family_name;
}
return NULL;
}
const struct network_tuple *net_find_mapping(const struct network_tuple *map,
const char *family,
@@ -302,95 +298,270 @@ const struct network_tuple *net_find_mapping(const struct network_tuple *map,
return NULL;
}
struct aa_network_entry *network_entry(const char *family, const char *type,
const char *protocol)
void network_rule::move_conditionals(struct cond_entry *conds)
{
struct aa_network_entry *new_entry, *entry = NULL;
const struct network_tuple *mapping = NULL;
struct cond_entry *cond_ent;
while ((mapping = net_find_mapping(mapping, family, type, protocol))) {
new_entry = new_network_ent(mapping->family, mapping->type,
mapping->protocol);
if (!new_entry)
yyerror(_("Memory allocation error."));
new_entry->next = entry;
entry = new_entry;
list_for_each(conds, cond_ent) {
/* for now disallow keyword 'in' (list) */
if (!cond_ent->eq)
yyerror("keyword \"in\" is not allowed in network rules\n");
/* no valid conditionals atm */
yyerror("invalid network rule conditional \"%s\"\n",
cond_ent->name);
}
return entry;
};
#define ALL_TYPES 0x43e
const char *net_find_af_name(unsigned int af)
{
size_t i;
if (af < 0 || af > get_af_max())
return NULL;
for (i = 0; i < sizeof(network_mappings) / sizeof(*network_mappings); i++) {
if (network_mappings[i].family == af)
return network_mappings[i].family_name;
}
return NULL;
}
void __debug_network(unsigned int *array, const char *name)
void network_rule::set_netperm(unsigned int family, unsigned int type)
{
if (type > SOCK_PACKET) {
/* setting mask instead of a bit */
network_perms[family] |= type;
} else
network_perms[family] |= 1 << type;
}
network_rule::network_rule(struct cond_entry *conds):
dedup_perms_rule_t(AA_CLASS_NETV8)
{
size_t family_index;
for (family_index = AF_UNSPEC; family_index < get_af_max(); family_index++) {
network_map[family_index].push_back({ family_index, 0xFFFFFFFF, 0xFFFFFFFF });
set_netperm(family_index, 0xFFFFFFFF);
}
move_conditionals(conds);
free_cond_list(conds);
}
network_rule::network_rule(const char *family, const char *type,
const char *protocol, struct cond_entry *conds):
dedup_perms_rule_t(AA_CLASS_NETV8)
{
const struct network_tuple *mapping = NULL;
while ((mapping = net_find_mapping(mapping, family, type, protocol))) {
network_map[mapping->family].push_back({ mapping->family, mapping->type, mapping->protocol });
set_netperm(mapping->family, mapping->type);
}
if (type == NULL && network_map.empty()) {
while ((mapping = net_find_mapping(mapping, type, family, protocol))) {
network_map[mapping->family].push_back({ mapping->family, mapping->type, mapping->protocol });
set_netperm(mapping->family, mapping->type);
}
}
if (network_map.empty())
yyerror(_("Invalid network entry."));
move_conditionals(conds);
free_cond_list(conds);
}
network_rule::network_rule(unsigned int family, unsigned int type):
dedup_perms_rule_t(AA_CLASS_NETV8)
{
network_map[family].push_back({ family, type, 0xFFFFFFFF });
set_netperm(family, type);
}
ostream &network_rule::dump(ostream &os)
{
class_rule_t::dump(os);
unsigned int count = sizeof(sock_types)/sizeof(sock_types[0]);
unsigned int mask = ~((1 << count) -1);
unsigned int i, j;
int none = 1;
size_t af_max = get_af_max();
for (i = AF_UNSPEC; i < af_max; i++)
if (array[i]) {
none = 0;
break;
}
if (none)
return;
printf("%s: ", name);
unsigned int j;
/* This can only be set by an unqualified network rule */
if (array[AF_UNSPEC]) {
printf("<all>\n");
return;
if (network_map.find(AF_UNSPEC) != network_map.end()) {
os << ",\n";
return os;
}
for (i = 0; i < af_max; i++) {
if (array[i]) {
const char *fam = net_find_af_name(i);
if (fam)
printf("%s ", fam);
else
printf("#%u ", i);
for (const auto& perm : network_perms) {
unsigned int family = perm.first;
unsigned int type = perm.second;
/* All types/protocols */
if (array[i] == 0xffffffff || array[i] == ALL_TYPES)
continue;
const char *family_name = net_find_af_name(family);
if (family_name)
os << " " << family_name;
else
os << " #" << family;
printf("{ ");
/* All types/protocols */
if (type == 0xffffffff || type == ALL_TYPES)
continue;
for (j = 0; j < count; j++) {
const char *type;
if (array[i] & (1 << j)) {
type = sock_types[j].name;
if (type)
printf("%s ", type);
else
printf("#%u ", j);
printf(" {");
for (j = 0; j < count; j++) {
const char *type_name;
if (type & (1 << j)) {
type_name = sock_types[j].name;
if (type_name)
os << " " << type_name;
else
os << " #" << j;
}
}
if (type & mask)
os << " #" << std::hex << (type & mask);
printf(" }");
}
os << ",\n";
return os;
}
int network_rule::expand_variables(void)
{
return 0;
}
void network_rule::warn_once(const char *name)
{
rule_t::warn_once(name, "network rules not enforced");
}
bool network_rule::gen_net_rule(Profile &prof, u16 family, unsigned int type_mask) {
std::ostringstream buffer;
std::string buf;
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_NETV8;
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((family & 0xff00) >> 8);
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (family & 0xff);
if (type_mask > 0xffff) {
buffer << "..";
} else {
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((type_mask & 0xff00) >> 8);
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (type_mask & 0xff);
}
buf = buffer.str();
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(AA_VALID_NET_PERMS),
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(AA_VALID_NET_PERMS) : 0,
parseopts))
return false;
return true;
}
int network_rule::gen_policy_re(Profile &prof)
{
std::ostringstream buffer;
std::string buf;
if (!features_supports_networkv8) {
warn_once(prof.name);
return RULE_NOT_SUPPORTED;
}
for (const auto& perm : network_perms) {
unsigned int family = perm.first;
unsigned int type = perm.second;
if (type > 0xffff) {
if (!gen_net_rule(prof, family, type))
goto fail;
} else {
int t;
/* generate rules for types that are set */
for (t = 0; t < 16; t++) {
if (type & (1 << t)) {
if (!gen_net_rule(prof, family, t))
goto fail;
}
}
if (array[i] & mask)
printf("#%x ", array[i] & mask);
}
printf("} ");
}
return RULE_OK;
fail:
return RULE_ERROR;
}
/* initialize static members */
unsigned int *network_rule::allow = NULL;
unsigned int *network_rule::audit = NULL;
unsigned int *network_rule::deny = NULL;
unsigned int *network_rule::quiet = NULL;
bool network_rule::alloc_net_table()
{
if (allow)
return true;
allow = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
audit = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
deny = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
quiet = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
if (!allow || !audit || !deny || !quiet)
return false;
return true;
}
/* update is required because at the point of the creation of the
* network_rule object, we don't have owner, rule_mode, or audit
* set.
*/
void network_rule::update_compat_net(void)
{
if (!alloc_net_table())
yyerror(_("Memory allocation error."));
for (auto& nm: network_map) {
for (auto& entry : nm.second) {
if (entry.type > SOCK_PACKET) {
/* setting mask instead of a bit */
if (rule_mode == RULE_DENY) {
deny[entry.family] |= entry.type;
if (dedup_perms_rule_t::audit != AUDIT_FORCE)
quiet[entry.family] |= entry.type;
} else {
allow[entry.family] |= entry.type;
if (dedup_perms_rule_t::audit == AUDIT_FORCE)
audit[entry.family] |= entry.type;
}
} else {
if (rule_mode == RULE_DENY) {
deny[entry.family] |= 1 << entry.type;
if (dedup_perms_rule_t::audit != AUDIT_FORCE)
quiet[entry.family] |= 1 << entry.type;
} else {
allow[entry.family] |= 1 << entry.type;
if (dedup_perms_rule_t::audit == AUDIT_FORCE)
audit[entry.family] |= 1 << entry.type;
}
}
}
}
printf("\n");
}
static int cmp_network_map(std::unordered_map<unsigned int, perms_t> lhs,
std::unordered_map<unsigned int, perms_t> rhs)
{
int res;
size_t family_index;
for (family_index = AF_UNSPEC; family_index < get_af_max(); family_index++) {
res = lhs[family_index] - rhs[family_index];
if (res)
return res;
}
return 0;
}
int network_rule::cmp(rule_t const &rhs) const
{
int res = dedup_perms_rule_t::cmp(rhs);
if (res)
return res;
network_rule const &nrhs = rule_cast<network_rule const &>(rhs);
return cmp_network_map(network_perms, nrhs.network_perms);
};

View File

@@ -29,6 +29,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <unordered_map>
#include <vector>
#include "parser.h"
#include "rule.h"
@@ -82,13 +84,10 @@ struct network_tuple {
unsigned int protocol;
};
/* supported AF protocols */
struct aa_network_entry {
unsigned int family;
long unsigned int family;
unsigned int type;
unsigned int protocol;
struct aa_network_entry *next;
};
static inline uint32_t map_perms(uint32_t mask)
@@ -99,45 +98,75 @@ static inline uint32_t map_perms(uint32_t mask)
((mask & (AA_NET_SETOPT | AA_NET_GETOPT)) >> 5); /* 5 + (AA_OTHER_SHIFT - 24) */
};
int parse_net_perms(const char *str_mode, perms_t *perms, int fail);
extern struct aa_network_entry *new_network_ent(unsigned int family,
unsigned int type,
unsigned int protocol);
extern struct aa_network_entry *network_entry(const char *family,
const char *type,
const char *protocol);
extern size_t get_af_max(void);
void __debug_network(unsigned int *array, const char *name);
struct network {
unsigned int *allow; /* array of type masks
* indexed by AF_FAMILY */
unsigned int *audit;
unsigned int *deny;
unsigned int *quiet;
network(void) { allow = audit = deny = quiet = NULL; }
void dump(void) {
if (allow)
__debug_network(allow, "Network");
if (audit)
__debug_network(audit, "Audit Net");
if (deny)
__debug_network(deny, "Deny Net");
if (quiet)
__debug_network(quiet, "Quiet Net");
}
};
size_t get_af_max();
int net_find_type_val(const char *type);
const char *net_find_type_name(int type);
const char *net_find_af_name(unsigned int af);
const struct network_tuple *net_find_mapping(const struct network_tuple *map,
const char *family,
const char *type,
const char *protocol);
class network_rule: public dedup_perms_rule_t {
void move_conditionals(struct cond_entry *conds);
public:
std::unordered_map<unsigned int, std::vector<struct aa_network_entry>> network_map;
std::unordered_map<unsigned int, perms_t> network_perms;
/* empty constructor used only for the profile to access
* static elements to maintain compatibility with
* AA_CLASS_NET */
network_rule(): dedup_perms_rule_t(AA_CLASS_NETV8) { }
network_rule(struct cond_entry *conds);
network_rule(const char *family, const char *type,
const char *protocol, struct cond_entry *conds);
network_rule(unsigned int family, unsigned int type);
virtual ~network_rule()
{
if (allow) {
free(allow);
allow = NULL;
}
if (audit) {
free(audit);
audit = NULL;
}
if (deny) {
free(deny);
deny = NULL;
}
if (quiet) {
free(quiet);
quiet = NULL;
}
};
bool gen_net_rule(Profile &prof, u16 family, unsigned int type_mask);
void set_netperm(unsigned int family, unsigned int type);
void update_compat_net(void);
virtual bool valid_prefix(const prefixes &p, const char *&error) {
if (p.owner) {
error = _("owner prefix not allowed on network rules");
return false;
}
return true;
};
virtual ostream &dump(ostream &os);
virtual int expand_variables(void);
virtual int gen_policy_re(Profile &prof);
virtual bool is_mergeable(void) { return true; }
virtual int cmp(rule_t const &rhs) const;
/* array of type masks indexed by AF_FAMILY */
/* allow, audit, deny and quiet are used for compatibility with AA_CLASS_NET */
static unsigned int *allow;
static unsigned int *audit;
static unsigned int *deny;
static unsigned int *quiet;
bool alloc_net_table(void);
protected:
virtual void warn_once(const char *name) override;
};
#endif /* __AA_NETWORK_H */

View File

@@ -77,7 +77,3 @@
## Pin older policy to the 5.4 kernel abi + out of tree network and af_unix
#policy-features=/etc/apparmor.d/abi/kernel-5.4-outoftree-network
## TO BE reverted - this is only for 4.0 dev cycle
## pin policy that doesn't have an abi to 4.0
policy-features=/etc/apparmor.d/abi/4.0

View File

@@ -37,6 +37,7 @@
#include "libapparmor_re/apparmor_re.h"
#include "libapparmor_re/aare_rules.h"
#include "rule.h"
#include "bignum.h"
#include <string>
@@ -353,6 +354,8 @@ extern int features_supports_userns;
extern int features_supports_posix_mqueue;
extern int features_supports_sysv_mqueue;
extern int features_supports_io_uring;
extern int features_supports_flag_interruptible;
extern int features_supports_flag_signal;
extern int kernel_supports_oob;
extern int conf_verbose;
extern int conf_quiet;
@@ -409,6 +412,7 @@ extern pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
extern int build_list_val_expr(std::string& buffer, struct value_list *list);
extern int convert_entry(std::string& buffer, char *entry);
extern int clear_and_convert_entry(std::string& buffer, char *entry);
extern int convert_range(std::string& buffer, bignum start, bignum end);
extern int process_regex(Profile *prof);
extern int post_process_entry(struct cod_entry *entry);
@@ -457,6 +461,9 @@ extern bool strcomp (const char *lhs, const char *rhs);
extern struct cod_entry *copy_cod_entry(struct cod_entry *cod);
extern void free_cod_entries(struct cod_entry *list);
void debug_cod_entries(struct cod_entry *list);
bool check_x_qualifier(struct cod_entry *entry, const char *&error);
bool entry_add_prefix(struct cod_entry *entry, const prefixes &p, const char *&error);
#define SECONDS_P_MS (1000LL * 1000LL)
long long convert_time_units(long long value, long long base, const char *units);

View File

@@ -82,6 +82,8 @@ int features_supports_userns = 0; /* kernel supports user namespace */
int features_supports_posix_mqueue = 0; /* kernel supports mqueue rules */
int features_supports_sysv_mqueue = 0; /* kernel supports mqueue rules */
int features_supports_io_uring = 0; /* kernel supports io_uring rules */
int features_supports_flag_interruptible = 0;
int features_supports_flag_signal = 0;
int kernel_supports_oob = 0; /* out of band transitions */
int conf_verbose = 0;
int conf_quiet = 0;

View File

@@ -420,6 +420,16 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
}
}
/* added in 4.13, unfortunately there is no features flag */
if (profile->flags.disconnected_path) {
sd_write_string(buf, profile->flags.disconnected_path,
"disconnected");
}
if (profile->flags.signal && features_supports_flag_signal) {
sd_write_name(buf, "kill");
sd_write_uint32(buf, profile->flags.signal);
}
sd_write_struct(buf, "flags");
/* used to be flags.debug, but that's no longer supported */
sd_write_uint32(buf, profile->flags.flags);

View File

@@ -378,7 +378,7 @@ GT >
yyterminate();
}
<INITIAL,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,MQUEUE_MODE>{
<INITIAL,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,MQUEUE_MODE,NETWORK_MODE>{
(peer|xattrs)/{WS}*={WS}*\( {
/* we match to the = in the lexer so that we can switch scanner
* state. By the time the parser see the = it may be too late
@@ -658,6 +658,10 @@ include/{WS} {
yy_push_state(INCLUDE);
}
all/({WS}|[^[:alnum:]_]) {
RETURN_TOKEN(TOK_ALL);
}
#.*\r?\n { /* normal comment */
DUMP_AND_DEBUG("comment(%d): %s\n", current_lineno, yytext);
current_lineno++;

View File

@@ -951,6 +951,12 @@ void set_supported_features()
features_supports_io_uring = features_intersect(kernel_features,
policy_features,
"io_uring");
features_supports_flag_interruptible = features_intersect(kernel_features,
policy_features,
"policy/profile/interruptible");
features_supports_flag_signal = features_intersect(kernel_features,
policy_features,
"policy/profile/kill.signal");
}
static bool do_print_cache_dir(aa_features *features, int dirfd, const char *path)

View File

@@ -129,6 +129,7 @@ static struct keyword_table keyword_table[] = {
{"io_uring", TOK_IO_URING},
{"override_creds", TOK_OVERRIDE_CREDS},
{"sqpoll", TOK_SQPOLL},
{"all", TOK_ALL},
/* terminate */
{NULL, 0}
@@ -1086,6 +1087,47 @@ void debug_cod_entries(struct cod_entry *list)
}
}
bool check_x_qualifier(struct cod_entry *entry, const char *&error)
{
if (entry->perms & AA_EXEC_BITS) {
if ((entry->rule_mode == RULE_DENY) &&
(entry->perms & ALL_AA_EXEC_TYPE)) {
error = _("Invalid perms, in deny rules 'x' must not be preceded by exec qualifier 'i', 'p', or 'u'");
return false;
} else if ((entry->rule_mode != RULE_DENY) &&
!(entry->perms & ALL_AA_EXEC_TYPE)) {
error = _("Invalid perms, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'");
return false;
}
}
return true;
}
// cod_entry version of ->add_prefix here just as file rules aren't converted yet
bool entry_add_prefix(struct cod_entry *entry, const prefixes &p, const char *&error)
{
/* modifiers aren't correctly stored for cod_entries yet so
* we can't conflict on them easily. Leave that until conversion
* to rule_t
*/
/* apply rule mode */
entry->rule_mode = p.rule_mode;
/* apply owner/other */
if (p.owner == 1)
entry->perms &= (AA_USER_PERMS | AA_SHARED_PERMS);
else if (p.owner == 2)
entry->perms &= (AA_OTHER_PERMS | AA_SHARED_PERMS);
/* implied audit modifier */
if (p.audit == AUDIT_FORCE && (entry->rule_mode != RULE_DENY))
entry->audit = AUDIT_FORCE;
else if (p.audit != AUDIT_FORCE && (entry->rule_mode == RULE_DENY))
entry->audit = AUDIT_FORCE;
return check_x_qualifier(entry, error);
}
// these need to move to stl
int ordered_cmp_value_list(value_list *lhs, value_list *rhs)
{

View File

@@ -33,6 +33,7 @@
#include "parser.h"
#include "profile.h"
#include "parser_yacc.h"
#include "network.h"
/* #define DEBUG */
#ifdef DEBUG

View File

@@ -845,6 +845,116 @@ int clear_and_convert_entry(std::string& buffer, char *entry)
return convert_entry(buffer, entry);
}
static std::vector<std::pair<bignum, bignum>> regex_range_generator(bignum start, bignum end)
{
std::vector<std::pair<bignum, bignum>> forward;
std::vector<std::pair<bignum, bignum>> reverse;
bignum next, prev;
while (start <= end) {
next = bignum::upper_bound_regex(start);
if (next > end)
break;
forward.emplace_back(start, next);
start = next + 1;
}
while (!end.negative && end >= start) {
prev = bignum::lower_bound_regex(end);
if (prev < start || prev.negative)
break;
reverse.emplace_back(prev, end);
end = prev - 1;
}
if (!end.negative && start <= end) {
forward.emplace_back(start, end);
}
forward.insert(forward.end(), reverse.rbegin(), reverse.rend());
return forward;
}
static std::string generate_regex_range(bignum start, bignum end)
{
std::ostringstream result;
std::vector<std::pair<bignum, bignum>> regex_range;
int j;
regex_range = regex_range_generator(start, end);
for (auto &i: regex_range) {
bignum sstart = i.first;
bignum send = i.second;
if (sstart.base == 16) {
for (j = (size_t) sstart.size(); j < 32; j++)
result << '0';
}
for (j = sstart.size() - 1; j >= 0; j--) {
result << std::nouppercase;
if (sstart[j] == send[j]) {
if (sstart[j] >= 10)
result << '[';
result << std::hex << sstart[j];
if (sstart[j] >= 10)
result << std::uppercase << std::hex << sstart[j] << ']';
} else {
if (sstart[j] < 10 && send[j] >= 10) {
result << '[';
result << std::hex << sstart[j];
if (sstart[j] < 9) {
result << '-';
result << '9';
}
if (send[j] > 10) {
result << 'a';
result << '-';
}
result << std::hex << send[j];
if (send[j] > 10) {
result << 'A';
result << '-';
}
result << std::uppercase << std::hex << send[j];
result << ']';
} else {
result << '[';
result << std::hex << sstart[j];
result << '-';
result << std::hex << send[j];
if (sstart[j] >= 10) {
result << std::uppercase << std::hex << sstart[j];
result << '-';
result << std::uppercase << std::hex << send[j];
}
result << ']';
}
}
}
if (&i != &regex_range.back())
result << ",";
}
return result.str();
}
int convert_range(std::string& buffer, bignum start, bignum end)
{
pattern_t ptype;
int pos;
std::string regex_range = generate_regex_range(start, end);
if (!regex_range.empty()) {
ptype = convert_aaregex_to_pcre(regex_range.c_str(), 0, glob_default, buffer, &pos);
if (ptype == ePatternInvalid)
return FALSE;
} else {
buffer.append(default_match_pattern);
}
return TRUE;
}
int post_process_policydb_ents(Profile *prof)
{
for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) {
@@ -857,80 +967,6 @@ int post_process_policydb_ents(Profile *prof)
return TRUE;
}
static bool gen_net_rule(Profile *prof, u16 family, unsigned int type_mask,
bool audit, bool deny) {
std::ostringstream buffer;
std::string buf;
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_NETV8;
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((family & 0xff00) >> 8);
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (family & 0xff);
if (type_mask > 0xffff) {
buffer << "..";
} else {
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((type_mask & 0xff00) >> 8);
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (type_mask & 0xff);
}
buf = buffer.str();
if (!prof->policy.rules->add_rule(buf.c_str(), deny, map_perms(AA_VALID_NET_PERMS),
audit ? map_perms(AA_VALID_NET_PERMS) : 0,
parseopts))
return false;
return true;
}
static bool gen_af_rules(Profile *prof, u16 family, unsigned int type_mask,
unsigned int audit_mask, bool deny)
{
if (type_mask > 0xffff && audit_mask > 0xffff) {
/* instead of generating multiple rules wild card type */
return gen_net_rule(prof, family, type_mask, audit_mask, deny);
} else {
int t;
/* generate rules for types that are set */
for (t = 0; t < 16; t++) {
if (type_mask & (1 << t)) {
if (!gen_net_rule(prof, family, t,
audit_mask & (1 << t),
deny))
return false;
}
}
}
return true;
}
bool post_process_policydb_net(Profile *prof)
{
u16 af;
/* no network rules defined so we don't have generate them */
if (!prof->net.allow)
return true;
/* generate rules if the af has something set */
for (af = AF_UNSPEC; af < get_af_max(); af++) {
if (prof->net.allow[af] ||
prof->net.deny[af] ||
prof->net.audit[af] ||
prof->net.quiet[af]) {
if (!gen_af_rules(prof, af, prof->net.allow[af],
prof->net.audit[af],
false))
return false;
if (!gen_af_rules(prof, af, prof->net.deny[af],
prof->net.quiet[af],
true))
return false;
}
}
return true;
}
#define MAKE_STR(X) #X
#define CLASS_STR(X) "\\d" MAKE_STR(X)
#define MAKE_SUB_STR(X) "\\000" MAKE_STR(X)
@@ -959,9 +995,6 @@ int process_profile_policydb(Profile *prof)
if (!post_process_policydb_ents(prof))
goto out;
/* TODO: move to network class */
if (features_supports_networkv8 && !post_process_policydb_net(prof))
goto out;
/* insert entries to show indicate what compiler/policy expects
* to be supported

View File

@@ -285,7 +285,20 @@ static int process_variables_in_name(Profile &prof)
int error = expand_entry_variables(&prof.name);
if (!error && prof.attachment)
error = expand_entry_variables(&prof.attachment);
if (!error && prof.flags.disconnected_path) {
error = expand_entry_variables(&prof.flags.disconnected_path);
if (error)
return error;
filter_slashes(prof.flags.disconnected_path);
// TODO: semantic check should go somewhere else
if (prof.flags.disconnected_path[0] != '/')
yyerror(_("attach_disconnected_path value must begin with a /"));
int n = strlen(prof.flags.disconnected_path);
// removing trailing / */
while (n && prof.flags.disconnected_path[n-1] == '/')
prof.flags.disconnected_path[--n] = 0;
}
return error;
}

View File

@@ -70,8 +70,6 @@ mnt_rule *do_mnt_rule(struct cond_entry *src_conds, char *src,
mnt_rule *do_pivot_rule(struct cond_entry *old, char *root,
char *transition);
static void abi_features(char *filename, bool search);
bool add_prefix(struct cod_entry *entry, const prefixes &p, const char *&error);
bool check_x_qualifier(struct cod_entry *entry, const char *&errror);
%}
@@ -149,6 +147,7 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror);
%token TOK_IO_URING
%token TOK_OVERRIDE_CREDS
%token TOK_SQPOLL
%token TOK_ALL
/* rlimits */
%token TOK_RLIMIT
@@ -187,13 +186,15 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror);
#include "userns.h"
#include "mqueue.h"
#include "io_uring.h"
#include "network.h"
#include "all_rule.h"
}
%union {
char *id;
char *flag_id;
char *mode;
struct aa_network_entry *network_entry;
network_rule *network_entry;
Profile *prof;
struct cod_net_entry *net_entry;
struct cod_entry *user_entry;
@@ -206,6 +207,7 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror);
userns_rule *userns_entry;
mqueue_rule *mqueue_entry;
io_uring_rule *io_uring_entry;
all_rule *all_entry;
prefix_rule_t *prefix_entry;
flagvals flags;
@@ -302,6 +304,7 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror);
%type <fperms> io_uring_perms
%type <fperms> opt_io_uring_perm
%type <io_uring_entry> io_uring_rule
%type <all_entry> all_rule
%%
@@ -575,8 +578,9 @@ valuelist: valuelist TOK_VALUE
}
flags: { /* nothing */
flagvals fv = { 0, MODE_UNSPECIFIED, 0, 0 };
flagvals fv;
fv.init();
$$ = fv;
};
@@ -596,27 +600,7 @@ flags: opt_flags TOK_OPENPAREN flagvals TOK_CLOSEPAREN
flagvals: flagvals flagval
{
if (merge_profile_mode($1.mode, $2.mode) == MODE_CONFLICT)
yyerror(_("Profile flag '%s' conflicts with '%s'"),
profile_mode_table[$1.mode],
profile_mode_table[$2.mode]);
$1.mode = merge_profile_mode($1.mode, $2.mode);
$1.audit = $1.audit || $2.audit;
$1.path = $1.path | $2.path;
if (($1.path & (PATH_CHROOT_REL | PATH_NS_REL)) ==
(PATH_CHROOT_REL | PATH_NS_REL))
yyerror(_("Profile flag chroot_relative conflicts with namespace_relative"));
if (($1.path & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED)) ==
(PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))
yyerror(_("Profile flag mediate_deleted conflicts with delegate_deleted"));
if (($1.path & (PATH_ATTACH | PATH_NO_ATTACH)) ==
(PATH_ATTACH | PATH_NO_ATTACH))
yyerror(_("Profile flag attach_disconnected conflicts with no_attach_disconnected"));
if (($1.path & (PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH)) ==
(PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH))
yyerror(_("Profile flag chroot_attach conflicts with chroot_no_attach"));
$1.merge($2);
$$ = $1;
};
@@ -627,35 +611,9 @@ flagvals: flagval
flagval: TOK_VALUE
{
flagvals fv = { 0, MODE_UNSPECIFIED, 0, 0 };
enum profile_mode mode;
flagvals fv;
if (strcmp($1, "debug") == 0) {
/* DEBUG2 is left for internal compiler use atm */
fv.flags |= FLAG_DEBUG1;
} else if ((mode = str_to_mode($1))) {
fv.mode = mode;
} else if (strcmp($1, "audit") == 0) {
fv.audit = 1;
} else if (strcmp($1, "chroot_relative") == 0) {
fv.path |= PATH_CHROOT_REL;
} else if (strcmp($1, "namespace_relative") == 0) {
fv.path |= PATH_NS_REL;
} else if (strcmp($1, "mediate_deleted") == 0) {
fv.path |= PATH_MEDIATE_DELETED;
} else if (strcmp($1, "delegate_deleted") == 0) {
fv.path |= PATH_DELEGATE_DELETED;
} else if (strcmp($1, "attach_disconnected") == 0) {
fv.path |= PATH_ATTACH;
} else if (strcmp($1, "no_attach_disconnected") == 0) {
fv.path |= PATH_NO_ATTACH;
} else if (strcmp($1, "chroot_attach") == 0) {
fv.path |= PATH_CHROOT_NSATTACH;
} else if (strcmp($1, "chroot_no_attach") == 0) {
fv.path |= PATH_CHROOT_NO_ATTACH;
} else {
yyerror(_("Invalid profile flag: %s."), $1);
}
fv.init($1);
free($1);
$$ = fv;
};
@@ -700,7 +658,7 @@ rules: rules opt_prefix rule
PDEBUG("rules rule: (%s)\n", $3->name);
if (!$3)
yyerror(_("Assert: `rule' returned NULL."));
if (!add_prefix($3, $2, error)) {
if (!entry_add_prefix($3, $2, error)) {
yyerror(_("%s"), error);
}
add_entry_to_policy($1, $3);
@@ -721,7 +679,7 @@ rules: rules opt_prefix block
list_for_each_safe($3->entries, entry, tmp) {
const char *error;
entry->next = NULL;
if (!add_prefix(entry, $2, error)) {
if (!entry_add_prefix(entry, $2, error)) {
yyerror(_("%s"), error);
}
/* transfer rule for now, TODO keep block and just
@@ -738,51 +696,23 @@ rules: rules opt_prefix block
rules: rules opt_prefix network_rule
{
struct aa_network_entry *entry, *tmp;
const char *error;
if (!$3->add_prefix($2, error))
yyerror(error);
/* class members need to be updated after prefix is added */
$3->update_compat_net();
PDEBUG("Matched: network rule\n");
if ($2.owner)
yyerror(_("owner prefix not allowed"));
if (!$3)
yyerror(_("Assert: `network_rule' return invalid protocol."));
if (!$1->alloc_net_table())
yyerror(_("Memory allocation error."));
list_for_each_safe($3, entry, tmp) {
/* map to extended mediation, let rule backend do
* downgrade if needed
*/
if (entry->family == AF_UNIX) {
unix_rule *rule = new unix_rule(entry->type, $2.audit, $2.rule_mode);
auto nm_af_unix = $3->network_map.find(AF_UNIX);
if (nm_af_unix != $3->network_map.end()) {
for (auto& entry : nm_af_unix->second) {
unix_rule *rule = new unix_rule(entry.type,
$2.audit, $2.rule_mode);
if (!rule)
yyerror(_("Memory allocation error."));
$1->rule_ents.push_back(rule);
}
if (entry->type > SOCK_PACKET) {
/* setting mask instead of a bit */
if ($2.rule_mode == RULE_DENY) {
$1->net.deny[entry->family] |= entry->type;
if ($2.audit != AUDIT_FORCE)
$1->net.quiet[entry->family] |= entry->type;
} else {
$1->net.allow[entry->family] |= entry->type;
if ($2.audit == AUDIT_FORCE)
$1->net.audit[entry->family] |= entry->type;
}
} else {
if ($2.rule_mode == RULE_DENY) {
$1->net.deny[entry->family] |= 1 << entry->type;
if ($2.audit != AUDIT_FORCE)
$1->net.quiet[entry->family] |= 1 << entry->type;
} else {
$1->net.allow[entry->family] |= 1 << entry->type;
if ($2.audit == AUDIT_FORCE)
$1->net.audit[entry->family] |= 1 << entry->type;
}
}
free(entry);
}
$1->rule_ents.push_back($3);
$$ = $1;
}
@@ -794,6 +724,7 @@ prefix_rule : mnt_rule { $$ = $1; }
| userns_rule { $$ = $1; }
| mqueue_rule { $$ = $1; }
| io_uring_rule { $$ = $1; }
| all_rule { $$ = $1; }
rules: rules opt_prefix prefix_rule
{
@@ -1152,40 +1083,22 @@ link_rule: TOK_LINK opt_subset_flag id_or_var TOK_ARROW id_or_var TOK_END_OF_RUL
$$ = entry;
};
network_rule: TOK_NETWORK TOK_END_OF_RULE
network_rule: TOK_NETWORK opt_conds TOK_END_OF_RULE
{
size_t family;
struct aa_network_entry *new_entry, *entry = NULL;
for (family = AF_UNSPEC; family < get_af_max(); family++) {
new_entry = new_network_ent(family, 0xffffffff,
0xffffffff);
if (!new_entry)
yyerror(_("Memory allocation error."));
new_entry->next = entry;
entry = new_entry;
}
network_rule *entry = new network_rule($2);
$$ = entry;
}
network_rule: TOK_NETWORK TOK_ID TOK_END_OF_RULE
network_rule: TOK_NETWORK TOK_ID opt_conds TOK_END_OF_RULE
{
struct aa_network_entry *entry;
entry = network_entry($2, NULL, NULL);
if (!entry)
/* test for short circuiting of family */
entry = network_entry(NULL, $2, NULL);
if (!entry)
yyerror(_("Invalid network entry."));
network_rule *entry = new network_rule($2, NULL, NULL, $3);
free($2);
$$ = entry;
}
network_rule: TOK_NETWORK TOK_ID TOK_ID TOK_END_OF_RULE
network_rule: TOK_NETWORK TOK_ID TOK_ID opt_conds TOK_END_OF_RULE
{
struct aa_network_entry *entry;
entry = network_entry($2, $3, NULL);
if (!entry)
yyerror(_("Invalid network entry."));
network_rule *entry = new network_rule($2, $3, NULL, $4);
free($2);
free($3);
$$ = entry;
@@ -1601,6 +1514,14 @@ io_uring_rule: TOK_IO_URING opt_io_uring_perm opt_conds opt_cond_list TOK_END_OF
$$ = ent;
}
all_rule: TOK_ALL TOK_END_OF_RULE
{
all_rule *ent = new all_rule();
if (!ent)
yyerror(_("Memory allocation error."));
$$ = ent;
}
hat_start: TOK_CARET {}
| TOK_HAT {}
@@ -1863,43 +1784,3 @@ static void abi_features(char *filename, bool search)
};
bool check_x_qualifier(struct cod_entry *entry, const char *&error)
{
if (entry->perms & AA_EXEC_BITS) {
if ((entry->rule_mode == RULE_DENY) &&
(entry->perms & ALL_AA_EXEC_TYPE)) {
error = _("Invalid perms, in deny rules 'x' must not be preceded by exec qualifier 'i', 'p', or 'u'");
return false;
} else if ((entry->rule_mode != RULE_DENY) &&
!(entry->perms & ALL_AA_EXEC_TYPE)) {
error = _("Invalid perms, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'");
return false;
}
}
return true;
}
// cod_entry version of ->add_prefix here just as file rules aren't converted yet
bool add_prefix(struct cod_entry *entry, const prefixes &p, const char *&error)
{
/* modifiers aren't correctly stored for cod_entries yet so
* we can't conflict on them easily. Leave that until conversion
* to rule_t
*/
/* apply rule mode */
entry->rule_mode = p.rule_mode;
/* apply owner/other */
if (p.owner == 1)
entry->perms &= (AA_USER_PERMS | AA_SHARED_PERMS);
else if (p.owner == 2)
entry->perms &= (AA_OTHER_PERMS | AA_SHARED_PERMS);
/* implied audit modifier */
if (p.audit == AUDIT_FORCE && (entry->rule_mode != RULE_DENY))
entry->audit = AUDIT_FORCE;
else if (p.audit != AUDIT_FORCE && (entry->rule_mode == RULE_DENY))
entry->audit = AUDIT_FORCE;
return check_x_qualifier(entry, error);
}

View File

@@ -27,6 +27,8 @@ const char *profile_mode_table[] = {
"complain",
"kill",
"unconfined",
"prompt",
"conflict" /* should not ever be displayed */
};
bool deref_profileptr_lt::operator()(Profile * const &lhs, Profile * const &rhs) const
@@ -70,20 +72,6 @@ void ProfileList::dump_profile_names(bool children)
}
}
bool Profile::alloc_net_table()
{
if (net.allow)
return true;
net.allow = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
net.audit = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
net.deny = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
net.quiet = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
if (!net.allow || !net.audit || !net.deny || !net.quiet)
return false;
return true;
}
Profile::~Profile()
{
hat_table.clear();
@@ -106,19 +94,13 @@ Profile::~Profile()
free(name);
if (attachment)
free(attachment);
if (flags.disconnected_path)
free(flags.disconnected_path);
if (ns)
free(ns);
for (int i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++)
if (exec_table[i])
free(exec_table[i]);
if (net.allow)
free(net.allow);
if (net.audit)
free(net.audit);
if (net.deny)
free(net.deny);
if (net.quiet)
free(net.quiet);
}
static bool comp (rule_t *lhs, rule_t *rhs)
@@ -344,6 +326,19 @@ static int profile_add_hat_rules(Profile *prof)
void Profile::post_parse_profile(void)
{
/* semantic check stuff that can't be done in parse, like flags */
if (flags.flags & FLAG_INTERRUPTIBLE) {
if (!features_supports_flag_interruptible) {
warn_once(name, "flag interruptible not supported. Ignoring");
/* TODO: don't clear in parse data, only at encode */
flags.flags &= ~FLAG_INTERRUPTIBLE;
}
}
if (flags.signal) {
if (!features_supports_flag_signal) {
warn_once(name, "kill.signal not supported. Ignoring");
}
}
post_process_file_entries(this);
post_process_rule_entries(this);
}
@@ -352,6 +347,12 @@ void Profile::add_implied_rules(void)
{
int error;
for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); i++) {
if ((*i)->skip())
continue;
(*i)->add_implied_rules(*this);
}
error = profile_add_hat_rules(this);
if (error) {
PERROR(_("ERROR adding hat access rule for profile %s\n"),
@@ -360,3 +361,9 @@ void Profile::add_implied_rules(void)
}
}
/* do we want to warn once/profile or just once per compile?? */
void Profile::warn_once(const char *name, const char *msg)
{
common_warn_once(name, msg, &warned_name);
}

View File

@@ -23,6 +23,7 @@
#include "rule.h"
#include "libapparmor_re/aare_rules.h"
#include "network.h"
#include "signal.h"
class Profile;
@@ -62,9 +63,10 @@ enum profile_mode {
MODE_COMPLAIN = 2,
MODE_KILL = 3,
MODE_UNCONFINED = 4,
MODE_CONFLICT = 5 /* greater than MODE_LAST */
MODE_PROMPT = 5,
MODE_CONFLICT = 6 /* greater than MODE_LAST */
};
#define MODE_LAST MODE_UNCONFINED
#define MODE_LAST MODE_PROMPT
static inline enum profile_mode operator++(enum profile_mode &mode)
{
@@ -113,13 +115,71 @@ static inline enum profile_mode str_to_mode(const char *str)
#define FLAG_HAT 1
#define FLAG_DEBUG1 2
#define FLAG_DEBUG2 4
#define FLAG_INTERRUPTIBLE 8
/* sigh, used in parse union so needs trivial constructors. */
class flagvals {
public:
int flags;
enum profile_mode mode;
int audit;
int path;
char *disconnected_path;
int signal;
// stupid not constructor constructors
void init(void)
{
flags = 0;
mode = MODE_UNSPECIFIED;
audit = 0;
path = 0;
disconnected_path = NULL;
signal = 0;
}
void init(const char *str)
{
init();
enum profile_mode pmode = str_to_mode(str);
if (strcmp(str, "debug") == 0) {
/* DEBUG2 is left for internal compiler use atm */
flags |= FLAG_DEBUG1;
} else if (pmode) {
mode = pmode;
} else if (strcmp(str, "audit") == 0) {
audit = 1;
} else if (strcmp(str, "chroot_relative") == 0) {
path |= PATH_CHROOT_REL;
} else if (strcmp(str, "namespace_relative") == 0) {
path |= PATH_NS_REL;
} else if (strcmp(str, "mediate_deleted") == 0) {
path |= PATH_MEDIATE_DELETED;
} else if (strcmp(str, "delegate_deleted") == 0) {
path |= PATH_DELEGATE_DELETED;
} else if (strcmp(str, "attach_disconnected") == 0) {
path |= PATH_ATTACH;
} else if (strcmp(str, "no_attach_disconnected") == 0) {
path |= PATH_NO_ATTACH;
} else if (strcmp(str, "chroot_attach") == 0) {
path |= PATH_CHROOT_NSATTACH;
} else if (strcmp(str, "chroot_no_attach") == 0) {
path |= PATH_CHROOT_NO_ATTACH;
} else if (strncmp(str, "attach_disconnected.path=", 25) == 0) {
/* TODO: make this a proper parse */
path |= PATH_ATTACH;
disconnected_path = strdup(str + 25);
} else if (strncmp(str, "kill.signal=", 12) == 0) {
/* TODO: make this a proper parse */
signal = find_signal_mapping(str + 12);
if (signal == -1)
yyerror("unknown signal specified for kill.signal=\'%s\'\n", str + 12);
} else if (strcmp(str, "interruptible") == 0) {
flags |= FLAG_INTERRUPTIBLE;
} else {
yyerror(_("Invalid profile flag: %s."), str);
}
}
ostream &dump(ostream &os)
{
@@ -131,6 +191,10 @@ public:
if (flags & FLAG_HAT)
os << ", Hat";
if (disconnected_path)
os << ", attach_disconnected.path=" << disconnected_path;
if (signal)
os << ", kill.signal=" << signal;
os << "\n";
return os;
@@ -144,6 +208,58 @@ public:
#endif
}
/* warning for now disconnected_path is just passed on (not copied),
* or leaked on error. It is not freed here, It is freed when the
* profile destroys it self.
*/
void merge(const flagvals &rhs)
{
if (merge_profile_mode(mode, rhs.mode) == MODE_CONFLICT)
yyerror(_("Profile flag '%s' conflicts with '%s'"),
profile_mode_table[mode],
profile_mode_table[rhs.mode]);
mode = merge_profile_mode(mode, rhs.mode);
audit = audit || rhs.audit;
path = path | rhs.path;
if ((path & (PATH_CHROOT_REL | PATH_NS_REL)) ==
(PATH_CHROOT_REL | PATH_NS_REL))
yyerror(_("Profile flag chroot_relative conflicts with namespace_relative"));
if ((path & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED)) ==
(PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))
yyerror(_("Profile flag mediate_deleted conflicts with delegate_deleted"));
if ((path & (PATH_ATTACH | PATH_NO_ATTACH)) ==
(PATH_ATTACH | PATH_NO_ATTACH))
yyerror(_("Profile flag attach_disconnected conflicts with no_attach_disconnected"));
if ((path & (PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH)) ==
(PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH))
yyerror(_("Profile flag chroot_attach conflicts with chroot_no_attach"));
if (rhs.disconnected_path) {
if (disconnected_path) {
if (strcmp(disconnected_path, rhs.disconnected_path) != 0) {
yyerror(_("Profile flag attach_disconnected set to conflicting values: '%s' and '%s'"), disconnected_path, rhs.disconnected_path);
}
// same ignore rhs.disconnect_path
} else {
disconnected_path = rhs.disconnected_path;
}
}
if (rhs.signal) {
if (signal) {
if (signal != rhs.signal) {
yyerror(_("Profile flag kill.signal set to conflicting values: '%d' and '%d'"), signal, rhs.signal);
}
// same so do nothing
} else {
signal = rhs.signal;
}
}
/* if we move to dupping disconnected_path will need to have
* an assignment and copy constructor and a destructor
*/
}
};
struct capabilities {
@@ -195,7 +311,7 @@ public:
flagvals flags;
struct capabilities caps;
struct network net;
network_rule net;
struct aa_rlimits rlimits;
@@ -221,7 +337,7 @@ public:
parent = NULL;
flags = { 0, MODE_UNSPECIFIED, 0, 0 };
flags.init();
rlimits = {0, {}};
std::fill(exec_table, exec_table + AA_EXEC_COUNT, (char *)NULL);
@@ -268,7 +384,6 @@ public:
flags.dump(cerr);
caps.dump();
net.dump();
if (entries)
debug_cod_entries(entries);
@@ -281,8 +396,6 @@ public:
hat_table.dump();
}
bool alloc_net_table();
std::string hname(void)
{
if (!parent)
@@ -315,6 +428,10 @@ public:
void post_parse_profile(void);
void add_implied_rules(void);
protected:
const char *warned_name = NULL;
virtual void warn_once(const char *name, const char *msg);
};

View File

@@ -35,8 +35,9 @@ class Profile;
#define RULE_TYPE_RULE 0
#define RULE_TYPE_PREFIX 1
#define RULE_TYPE_PERMS 2
#define RULE_TYPE_ALL 3
// RULE_TYPE_CLASS needs to be last because various class follow it
#define RULE_TYPE_CLASS 3
#define RULE_TYPE_CLASS 4
// rule_cast should only be used after a comparison of rule_type to ensure
// that it is valid. Change to dynamic_cast for debugging
@@ -289,6 +290,10 @@ public:
return true;
}
virtual bool add_prefix(const prefixes &p) {
const char *err;
return add_prefix(p, err);
}
int cmp(prefixes const &rhs) const {
return prefixes::cmp(rhs);

View File

@@ -121,7 +121,7 @@ int parse_signal_perms(const char *str_perms, perms_t *perms, int fail)
return parse_X_perms("signal", AA_VALID_SIGNAL_PERMS, str_perms, perms, fail);
}
static int find_signal_mapping(const char *sig)
int find_signal_mapping(const char *sig)
{
if (strncmp("rtmin+", sig, 6) == 0) {
char *end;

View File

@@ -31,6 +31,7 @@
typedef set<int> Signals;
int find_signal_mapping(const char *sig);
int parse_signal_perms(const char *str_perms, perms_t *perms, int fail);
class signal_rule: public perms_rule_t {

View File

@@ -13,10 +13,13 @@
#
# ------------------------------------------------------------------
import os
import subprocess
import sys
import unittest
from argparse import ArgumentParser
from shutil import rmtree
from tempfile import mkdtemp
import testlib
@@ -28,6 +31,15 @@ class AAErrorTests(testlib.AATestTemplate):
self.maxDiff = None
self.cmd_prefix = [config.parser, '--config-file=./parser.conf', '-S', '-I', 'errors']
self.tmpdir = os.path.realpath(mkdtemp(prefix='test-aa-parser-errors-'))
self.profile_dir = os.path.join(self.tmpdir, 'profile')
os.mkdir(self.profile_dir)
def tearDown(self):
if os.path.exists(self.tmpdir):
rmtree(self.tmpdir)
def _run_test(self, profile, message='', is_error=True):
cmd = self.cmd_prefix + [profile]
@@ -90,28 +102,65 @@ class AAErrorTests(testlib.AATestTemplate):
is_error=False
)
def test_non_existant_profile(self):
test_profile = os.path.join(self.profile_dir, "does-not-exist.sd")
self._run_test(
test_profile,
"File {} not found, skipping...\n".format(test_profile),
)
# We can run this test with multiple different arguments
def _test_non_existant_symlink_target(self):
"""Helper Function to test the parser on a symlink with a non-existent target"""
test_profile = os.path.join(self.profile_dir, "non-existant-target.sd")
os.symlink('does-not-exist.sd', test_profile)
self._run_test(
test_profile,
"File {} not found, skipping...\n".format(test_profile),
)
def test_non_existant_symlink_target(self):
'''Basic symlink test that goes nowhere'''
self._test_non_existant_symlink_target()
def test_non_existant_symlink_target_j0(self):
'''Basic symlink test that goes nowhere with 0 jobs'''
self.cmd_prefix.append('-j0')
self._test_non_existant_symlink_target()
def test_non_existant_symlink_target_j1(self):
'''Basic symlink test that goes nowhere with 1 job arg'''
self.cmd_prefix.append('-j1')
self._test_non_existant_symlink_target()
def test_non_existant_symlink_target_j8(self):
'''Basic symlink test that goes nowhere with 8 job arg'''
self.cmd_prefix.append('-j8')
self._test_non_existant_symlink_target()
def test_non_existant_symlink_target_jauto(self):
'''Basic symlink test that goes nowhere with auto job arg'''
self.cmd_prefix.append('-jauto')
self._test_non_existant_symlink_target()
def test_non_existant_symlink_target_in_directory(self):
'''Symlink test passing a directory to the parser'''
test_profile = os.path.join(self.profile_dir, "non-existant-target.sd")
os.symlink('does-not-exist.sd', test_profile)
self._run_test(
self.profile_dir,
"There was an error while loading profiles from {}\n".format(self.profile_dir),
)
def main():
global config
p = ArgumentParser()
p.add_argument('-p', '--parser', default=testlib.DEFAULT_PARSER, action="store", dest='parser',
help="Specify path of apparmor parser to use [default = %(default)s]")
p.add_argument('-v', '--verbose', action="store_true", dest="verbose")
config = p.parse_args()
verbosity = 2 if config.verbose else 1
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.TestLoader().loadTestsFromTestCase(AAErrorTests))
try:
result = unittest.TextTestRunner(verbosity=verbosity).run(test_suite)
except Exception:
rc = 1
else:
rc = 0 if result.wasSuccessful() else 1
return rc
config, args = p.parse_known_args()
unittest.main(argv=sys.argv[:1] + args)
if __name__ == "__main__":
sys.exit(main())
main()

View File

@@ -0,0 +1,8 @@
#
#=Description basic ptrace all rule
#=EXRESULT FAIL
#
/usr/bin/foo {
all read readby trace tracedby ,
}

View File

@@ -0,0 +1,8 @@
#
#=Description basic ptrace all rule
#=EXRESULT FAIL
#
/usr/bin/foo {
owner all,
}

View File

@@ -0,0 +1,8 @@
#
#=Description basic all rule
#=EXRESULT PASS
#
/usr/bin/foo {
all,
}

View File

@@ -0,0 +1,8 @@
#
#=Description basic all rule
#=EXRESULT PASS
#
/usr/bin/foo {
audit all,
}

View File

@@ -0,0 +1,8 @@
#
#=Description basic all rule
#=EXRESULT PASS
#
/usr/bin/foo {
allow all,
}

View File

@@ -0,0 +1,8 @@
#
#=Description basic all rule
#=EXRESULT PASS
#
/usr/bin/foo {
deny all,
}

View File

@@ -0,0 +1,8 @@
#
#=Description basic all rule
#=EXRESULT PASS
#
/usr/bin/foo {
audit deny all,
}

View File

@@ -0,0 +1,8 @@
#
#=Description basic all rule
#=EXRESULT PASS
#
/usr/bin/foo {
audit allow all,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, prompt) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(complain, prompt) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill, prompt) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(prompt, enforce) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(prompt, complain) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(prompt, kill) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(prompt, unconfined) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, kill, prompt) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(complain, kill, prompt) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, complain, kill, unconfined, prompt) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, kill, interruptible) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(complain, kill, interruptible) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, complain, kill, unconfined, interruptible) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure bad signal value
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill.signal=0) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure bad signal value
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill.signal=foo) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure bad signal value
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill.signal=hup.) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,13 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(attach_disconnected.path=foo) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,13 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(attach_disconnected.path=foo/) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,16 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
@{var}=bar
/does/not/exist flags=(attach_disconnected.path=@{var}) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,16 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
@{var}=bar/
/does/not/exist flags=(attach_disconnected.path=@{var}) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,16 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
@{var}=bar/
/does/not/exist flags=(attach_disconnected.path=@{var}/) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(prompt) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(prompt audit) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(interruptible) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(interruptible audit) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION ensure flag does not conflict with other mdes, and flags
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, interruptible) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION ensure flag does not conflict with other mdes, and flags
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(complain, interruptible) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION ensure flag does not conflict with other mdes, and flags
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill, interruptible) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION ensure flag does not conflict with other mdes, and flags
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(interruptible, enforce) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION ensure flag does not conflict with other mdes, and flags
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(interruptible, complain) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION ensure flag does not conflict with other mdes, and flags
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(interruptible, kill) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION ensure flag does not conflict with other mdes, and flags
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(interruptible, unconfined) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION ensure flag does not conflict with other mdes, and flags
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, interruptible) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION ensure flag does not conflict with other mdes, and flags
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(prompt, interruptible) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(prompt, kill.signal=hup) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure signal.kill works with different flags and signals
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, kill.signal=kill) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure kill.signal works with different flags and signals
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill.signal=int, unconfined) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure kill.signal works with different modes and signals
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill.signal=quit, kill) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure kill.signal works with different modes and signals
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill.signal=hup, complain) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure kill.signal works with different modes and signals
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill.signal=ill, enforce) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure kill.signal works with different modes and signals
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill, kill.signal=trap) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure kill.signal works with different modes and signals
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(complain, kill.signal=bus) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure kill.signal works with different flags and signals
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, kill.signal=usr1) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,12 @@
#
#=DESCRIPTION Ensure kill.signals works with different flags and signals
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill.signal=stop audit) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,12 @@
#
#=DESCRIPTION Ensure kill.signal works with different flags and signals
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill.signal=emt) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,13 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(attach_disconnected.path=/foo) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,13 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(attach_disconnected.path=/foo/) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,13 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(attach_disconnected.path=/foo//) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,13 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(attach_disconnected.path=/foo/bar) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,16 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
@{var}=/bar
/does/not/exist flags=(attach_disconnected.path=/foo/@{var}) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,16 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
@{var}=/bar
/does/not/exist flags=(attach_disconnected.path=/foo/@{var}/) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,16 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
@{var}=/bar
/does/not/exist flags=(attach_disconnected.path=@{var}/foo) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -0,0 +1,16 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
@{var}=/bar
/does/not/exist flags=(attach_disconnected.path=@{var}) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@@ -20,7 +20,7 @@
# Makefile for LSM-based AppArmor profiles
NAME=apparmor-profiles
all: local docs
all: docs
COMMONDIR=../common/
include $(COMMONDIR)/Make.rules
@@ -86,7 +86,7 @@ local:
done
.PHONY: install
install: local
install:
install -m 755 -d ${PROFILES_DEST}
install -m 755 -d ${PROFILES_DEST}/disable
for dir in ${SUBDIRS} ; do \
@@ -119,10 +119,10 @@ CHECK_PROFILES=$(filter-out ${IGNORE_FILES} ${SUBDIRS}, $(wildcard ${PROFILES_SO
CHECK_ABSTRACTIONS=$(shell find ${ABSTRACTIONS_SOURCE} -type f -print)
.PHONY: check
check: check-parser check-logprof check-abstractions.d check-extras
check: check-parser check-logprof check-abstractions.d check-tunables.d check-extras
.PHONY: check-parser
check-parser: test-dependencies local
check-parser: test-dependencies
@echo "*** Checking profiles from ${PROFILES_SOURCE} and ${EXTRAS_SOURCE} against apparmor_parser"
$(Q)for profile in ${CHECK_PROFILES} ; do \
[ -n "${VERBOSE}" ] && echo "Testing $${profile}" ; \
@@ -138,7 +138,7 @@ check-parser: test-dependencies local
done
.PHONY: check-logprof
check-logprof: test-dependencies local
check-logprof: test-dependencies
@echo "*** Checking profiles from ${PROFILES_SOURCE} against logprof"
$(Q)${LOGPROF} -d ${PROFILES_SOURCE} -f /dev/null || exit 1
@@ -152,6 +152,15 @@ check-abstractions.d:
grep -q "^ include if exists <abstractions/$${file}.d>$$" $$file || { echo "$$file does not contain 'include if exists <abstractions/$${file}.d>'"; exit 1; } ; \
done
.PHONY: check-tunables.d
check-tunables.d:
@echo "*** Checking if all tunables (with a few exceptions) contain include if exists <tunables/*.d>"
$(Q)cd apparmor.d/tunables && for file in * ; do \
test -d "$$file" && continue ; \
test "$$file" = 'sys' && continue ; \
grep -q "^include if exists <tunables/$${file}.d>$$" $$file || { echo "$$file does not contain 'include if exists <tunables/$${file}.d>'"; exit 1; } ; \
done
.PHONY: check-extras
check-extras:
@echo "*** Checking if all extra profiles contain include if exists <local/*>"

View File

@@ -13,9 +13,12 @@
#
# For example, if the shipped /etc/apparmor.d/usr.sbin.smbd profile has:
# include <local/usr.sbin.smbd>
# or
# include if exists <local/usr.sbin.smbd>
#
# then an administrator can adjust /etc/apparmor.d/local/usr.sbin.smbd to
# contain any additional paths to be allowed, such as:
# then an administrator can adjust /etc/apparmor.d/local/usr.sbin.smbd
# (create the file if it doesn't exist yet) to contain any additional paths
# to be allowed, such as:
#
# /var/exports/** lrwk,
#

View File

@@ -14,3 +14,6 @@
#
# Or if mysql databases are stored in /home:
# alias /var/lib/mysql/ -> /home/mysql/,
# Also, include files in tunables/alias.d for site-specific adjustments
include if exists <tunables/alias.d>

View File

@@ -9,3 +9,6 @@
include <tunables/securityfs>
@{apparmorfs}=@{securityfs}/apparmor/
# Also, include files in tunables/apparmorfs.d for site-specific adjustments
include if exists <tunables/apparmorfs.d>

View File

@@ -18,3 +18,5 @@
@{DOVECOT_MAILSTORE}=@{HOME}/Maildir/ @{HOME}/mail/ @{HOME}/Mail/ /var/vmail/ /var/mail/ /var/spool/mail/
# Also, include files in tunables/dovecot.d for site-specific adjustments
include if exists <tunables/dovecot.d>

View File

@@ -24,6 +24,5 @@
# For rules that only allows read access, use @{etc_ro}.
@{etc_rw}=/etc/
# Also, include files in tunables/etc.d/ for site-specific adjustments to
# @{etc_ro} and @{etc_rw}.
# Also, include files in tunables/etc.d for site-specific adjustments
include if exists <tunables/etc.d>

Some files were not shown because too many files have changed in this diff Show More