2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-09-02 07:15:18 +00:00

Compare commits

..

145 Commits

Author SHA1 Message Date
John Johansen
8d9a061a45 Prepare for 4.1.0~beta3 release
- bump version

Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 02:47:26 -08:00
John Johansen
94ea0f00b1 Merge parser: convert uint to unsigned int
As reported in https://gitlab.com/apparmor/apparmor/-/merge_requests/1475
uint requires the inclusion of sys/types.h for use in musl libc.
Including that would be fine but since it is only used for the
cast for the owner type comparison, just convert to use a more
standard type.

Reported-by: @fossd <fossdd@pwned.life>
Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1478
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit cd8b75abc0)
2025-01-09 02:46:06 -08:00
John Johansen
99e919c288 parser: convert uint to unsigned int
As reported in https://gitlab.com/apparmor/apparmor/-/merge_requests/1475
uint requires the inclusion of sys/types.h for use in musl libc.
Including that would be fine but since it is only used for the
cast for the owner type comparison, just convert to use a more
standard type.

Reported-by: @fossd <fossdd@pwned.life>
Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit ff03702fde)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 02:46:06 -08:00
John Johansen
d805b5c3f8 Merge cupsd: Add /etc/paperspecs and convert to @etc_ro/rw
I had this message in my log

```
Dez 30 08:14:46 kernel: audit: type=1400 audit(1735542886.787:307): apparmor="DENIED" operation="open" class="file" profile="/usr/sbin/cupsd" name="/etc/paperspecs" pid=317509 comm="cupsd" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
```

If the second commit is bad, I can drop it.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1472
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit e5a960a685)
2025-01-09 02:26:24 -08:00
Jörg Sommer
2aa7fe4659 cupsd: convert profile to @etc_ro/rw
While cups itself writes to /etc the others require only read-only access
and might therefore live in /usr/etc.

(cherry picked from commit c3af6228fd)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 02:26:24 -08:00
Jörg Sommer
c456101ebb cupsd: Add /etc/paperspecs read access
Cups uses libpaper which accesses /etc/paperspecs.

ce42216e2e/lib/libpaper.c.in.in (L419)
(cherry picked from commit 97d7fa3f5f)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 02:26:24 -08:00
John Johansen
9875ba19ef Merge Allow write access to /run/user/*/dconf/user
Gtk applications like Firefox request write access to the file
`/run/user/1000/dconf/user`. The code in `dconf_shm_open` opens the file
with `O_RDWR | O_CREAT`.

4057f8c84f/shm/dconf-shm.c (L68)

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1471
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 0eca26c6c2)
2025-01-09 02:26:07 -08:00
Jörg Sommer
ab15e29654 Allow write access to /run/user/*/dconf/user
Gtk applications like Firefox request write access to the file
`/run/user/1000/dconf/user`. The code in `dconf_shm_open` opens the file
with `O_RDWR | O_CREAT`.

4057f8c84f/shm/dconf-shm.c (L68)
(cherry picked from commit 318fb30446)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 02:26:07 -08:00
John Johansen
320a2a5155 Merge parser: fix priority for file rules.
Fix priority for file rules, and the ability to dump the dfa at different stages, and update and fix the equality tests.

This in particular adds the ability to better debug the equality tests. Instead of just piping the parser output into the hash it creates a tmp dir and drops the binary files there so they can be manually examined. It adds new options particularly the -r option making so the tests will exit on first failure to make it easier to isolate and examine a failure.

Eg.
```
./equality.sh -r -d -v
Equality Tests:
................................................................................................................................................................................................................................
Binary inequality 'priority=-1'x'priority=-1' change_hat rules automatically inserted
FAIL: Hash values match
parser: ./../apparmor_parser -QKSq --features-file=./features_files/features.all
known-good (ee4f926922ecd341f1389a79dd155879) == profile-under-test (ee4f926922ecd341f1389a79dd155879) for the following profiles:
known-good         /t { priority=-1 owner /proc/[0-9]*/attr/{apparmor/,}current a, ^test { priority=-1 owner /proc/[0-9]*/attr/{apparmor/,}current a, /f r, }}
profile-under-test /t { priority=-1 owner /proc/[0-9]*/attr/{apparmor/,}current w, ^test { priority=-1 owner /proc/[0-9]*/attr/{apparmor/,}current w, /f r, }}

  files retained in "/tmp/eq.3240859-deHu10/"
```

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1455
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 40e9b2a961)
2025-01-09 01:44:18 -08:00
John Johansen
00dc6794f5 parser: equality tests: convert to using sha256sum for the hashes
There is a general industry wide effort to move off of md5 and even
sha1 (see recent kernel changes). While in this particular use case it
doesn't make a difference (besides slightly lowering the chance of a
collision) switch to sha256sum to make sure our code doesn't depend on
tools that are deprecated and there is an effort to remove.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 027b508da8)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
958a77a2db parser: equality tests: fix r carve out tests
Similar to the deny x permission tests, the tests that test carving
out r permissions need to be updated to be conditional on what
priority is being used on the rule.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit bf7b80c478)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
b4aa2cfde4 parser: equality tests: update deny x perm carve out test
With priority rules, deny does not carve out permissions from the
higher priority rule. Technically it doesn't from lower priority either
as it completely overrides them, but that case already results in
an inequality so does not cause the tests to fail.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 25f16b239d)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
86273b746a parser: equality tests: fix cx specified profile transition
cx rules using a specified profile transition, may be emulated by
using px and a hierarchical profile name. That is

  cx -> b

may be transformed into

  px -> profile//b

which will generate an xtable entry of

  profile//b

which means the previous patch using

  pivot_root -> b,

to reliably add b to the xtable will not cover this case.

transition to using two pivot_root rules to provide the xtable entries
  pivot_root /a -> b,
  pivot_root /c -> /t//b,

the paths /a and /c are irrelavent as long as they don't have an
overlap with the generic globbing expression in the test, Two table
entries will be generated. We guarantee no overlap by converting the

  /** to /f**

Also the xtable reserving rules are moved to the end of the profile so
the table order can be reliably created. A follow on MR around xtable
improvements should add reliability to xtable order.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 369029dc07)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
6a26d1f58c parser: equality tests: fix equality failure due to xtable
exec rules that specify an specific target profile generate an entry
in the xtable. The test entries containing " -> b" are an example of
this.

Currently the parser allocates the xtable entry before priorities are
applied in the backend, or minimization is done. Further more the
parser does not ref count the xtable entry to know what it is no
longer referenced.

The equality tests generate rules that are designed to completely
override and remove a lower priority rule, and remove it. Eg.

  /t { priority=1 /* ux, /f px -> b, }

and then compares the generated profile to the functionaly equivalent
profile eg.

  /t { priority=1 /* ux, }

To verify the overridden rule has been completely removed.
Unfortunately the compilation is not removing the unused xtable entry
for the specified transition, causing the equality comparison to fail.

Ideally the parser should be fixed so unused xtable entries are removed,
but that should be done in a different MR, and have its own test.

To fix the current tests, and another rule that adds an xtable entry
to the same target that can not be overriden by the x rule using
pivot_root. The parser will dedup the xtable entry resulting in the
known and test profile both having the same xtable. So the test will
pass and meet the original goal of verifying the x rule being overriden
and eliminated.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 84650beb2f)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
17d3545d07 parser: equality tests: rework and add debug features
Failed equality tests can be hard to debug. The profiles aren't always
enough to figure out what is going on. Add several options that will
help in debugging, and developing new tests.

Add switches and arg parsing.

Add the ability to run tests individually

Add a -r flag to allow retaining the test and output
similar to the regression tests, so the exact output from the
tests can be examined.

Add a -d flag to dump dfa build information.

Allow overriding the parser, features, and description for a given
test run.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit cca842b897)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
640c3dde26 parser: equality tests: wrap test run in function
In preparation for some additional abilities wrap the current tests in
a function.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 05ddc61246)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
380a5c8a72 parser: equality tests: consitently dump error output to stderr
printf of failure/error info should be going to stderr. Unfortunately
the test has a mix of 2>&1 and 1>&2. Having a mix is just wrong, we
could standardize on either but since the info is error info 1>&2
seems to be the better choice.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 31e60baab2)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
f26f577742 parser: equality tests: fix failing overlapping x rule tests
The test was passing because the file priority was always zero bug
resulting in the priority rule always being correctly combined
with the specific match x rule, instead of overriding it.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 57c57f198c)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
2700e58755 parser: equality tests: fix change_hat priority test
The test was passing because the file priority always being zero bug,
the supplied rule always had the same priority as the implied
rule. Resulting in binary_equality always passing even though the
specified priority should have resulted in a failure.

Fix this by checking if the priorities are equal to the implied
rule other wise it should result in an inequality.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 4b410b67f1)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
427a895288 parser: equality tests: output parser, config and features info
When there is a failure output the exact call info used to invoke the
parser. To facilitate manually recreating the test.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit d275dfdd42)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
dc0a9dc599 parser: equality tests: convert xequality tests to equality
With the file priority fix the xequality (expected equal but known
failure) tests are now passing. So convert them to regular equality
tests.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit fcee32a37e)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
74219b34dc parser: add some new dfa dump options.
The dfa goes through several stages during the build. Allow dumping it
at the various stages instead of only at the end.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 5d2a38e816)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
5aaa45e4ce parser: fix priority for file rules.
File rules could drop priority info when rule matched a rule
that was the same except for having different priority. For now
fix this by treating them as a different rule.

The priority was also be dropped when add_prefix was used to
add the priority during the parse resulting in file rules always
getting a default priority of 0.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 9d5b86bc9d)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:44:18 -08:00
John Johansen
0c02c8afe1 Merge Allow python cache under the @{HOME}/.cache/ dir
Starting with Python 3.8, you can use the PYTHONPYCACHEPREFIX environment
variable to define a cache directory for Python [1]. I think most people would set
this dir to @{HOME}/.cache/python/ , so the python abstraction should allow
writing to this location.

[1]: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPYCACHEPREFIX

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1467
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 8c799f4eec)
2025-01-09 01:43:42 -08:00
Mikhail Morfikov
70ed8d6f38 Allow python cache under the @{HOME}/.cache/ dir
Starting with Python 3.8, you can use the PYTHONPYCACHEPREFIX environment
variable to define a cache directory for Python [1]. I think most people would set
this dir to @{HOME}/.cache/python/ , so the python abstraction should allow
writing to this location.

[1]: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPYCACHEPREFIX

(cherry picked from commit 03b5a29b05)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:43:42 -08:00
John Johansen
5751614928 Merge regression tests: make loop device size more generous
Depending on the system, copying echo to the loop device fails because the echo binary is too large.
Especially on systems that have echo be just a symlink to coreutils (e.g. busybox) (as opposed to echo being its own binary) 16k is just not enough.
2M seems fine on my system, but this might need yet a higher value depending on what coreutils other people actually run.

The crash in question:
```
cp: error writing '/tmp/sdtest.3937422-31490-Bxvi6g/mount_target/echo': No space left on device
Fatal Error (file_unbindable_mount): Unexpected shell error. Run with -x to debug
rm: cannot remove '/tmp/sdtest.3937422-31490-Bxvi6g/mount_target': Device or resource busy
```

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1469
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 8e431ebcd9)
2025-01-09 01:43:11 -08:00
Grimmauld
73842b54f7 regression tests: make loop device size more generous
Depending on the system, copying echo to the loop device fails because the echo binary is too large.
Especially on systems that have echo be just a symlink to coreutils (e.g. busybox) 16k is just not enough.
2M seems fine on my system, but this might need yet a higher value depending on what coreutils other people actually run.
The actual loop device needs to be larger to properly fit the allocated file size. Testing shows 4M is sufficient, but this is basically arbitrary.

(cherry picked from commit 1cc2a3bd86)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:43:11 -08:00
John Johansen
54f1cf8dca Merge Write a regression test for mediating file access in private mounts
This test, as is, emits an execname warning which is due to a bug in the `prologue.inc` infrastructure (see !1450 for a fix to this issue).

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1448
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit ba60bfff85)
2025-01-09 01:42:41 -08:00
Ryan Lee
2de3b84de2 Shellcheck fix pass over file_unbindable_mount test
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit fa58d3611a)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:42:41 -08:00
Ryan Lee
9fc848be81 Add file_unbindable_mount to regression task.yaml
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit c768a7dc79)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:42:41 -08:00
Ryan Lee
fefbf514f7 Add file_unbindable_mount to regression test Makefile
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit 049b35dff0)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:42:41 -08:00
Ryan Lee
ae0c588acb Write a regression test for mediating file access in unbindable mounts
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit f249c6d58f)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:42:41 -08:00
John Johansen
0af8c5e26f Merge aa-status: fix json generation
- previously, aa-status --json --show profiles would return non-standard json
- adding the --pretty flag would crash completely
- closes #470

Things done:
- removed trailing ", " in json generation
- generate json seperator (", ") for each new json field
  (profiles/processes) after the header if json is enabled

Tested on NixOS and apparmor 4.0.3 base, but should work on any version the patch applies on.

Closes #470
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1451
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit c489631770)
2025-01-09 01:42:18 -08:00
Grimmauld
f4deae6759 aa-status: fix json output with --count flag
(cherry picked from commit 9967ba9873)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:42:18 -08:00
Grimmauld
0691cfcf3c aa-status: fix json generation
- previously, aa-status --json --show profiles would return non-standard json
- adding the --pretty flag would crash completely
- closes #470

Things done:
- removed trailing ", " in json generation
- generate json seperator (", ") for each new json field
  (profiles/processes) after the header if json is enabled

Tested on NixOS and apparmor 4.0.3 base, but should work on any version the patch applies on.

(cherry picked from commit 4f006a660c)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:42:18 -08:00
John Johansen
760ddaeb80 Merge fixes on the testing infrastructure
This MR is meant to resolve warnings such as "Warning: execname '/home/username/Documents/apparmor/tests/regression/apparmor/file_unbindable_mount': no such file or directory" when running tests like the one in the current version of !1448.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1450
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 59957aa1d8)
2025-01-09 01:42:03 -08:00
Georgia Garcia
4e46df38cf tests: fix profile name when wrapper is specified
When settest was called with two parameters, one for the test name and
the other for the test wrapper/binary, the profile created with
genprofile would show the test name, causing an error if the file
didn't exist.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit b4adff2ce0)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:42:03 -08:00
Georgia Garcia
e9858c0c43 tests: add option to append a profile to a profile already generated
Some of the tests using the --stdin option of mkprofile.pl are adding
more than one profile at a time. Whenever a profile is created in the
test, its name is added to the file profile.names so the test
infrastructure can tell if the profile is loaded or removed when
appropriately. The issue is that the name of the second profile
created by --stdin is not added, so these checks are not applied.

This patch adds the option of appending a second profile (not rules).
The option --append was used instead of a short -A because the short
options are arguments of mkprofile.pl, which --append is not.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit 0307619ed9)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:42:03 -08:00
Georgia Garcia
0e59b99623 tests: remove outdated restriction on image name specification
Due to how the tests were implemented in the past, permissions could
be passed along with the image name, and the permission part would be
discarded. The issue is that permissions are usually separated by ':',
but namespaces also contain ':', which would cause a conflict.

Since permissions are no longer passed as part of the image name,
remove that description so profile names in namespaces can be
supported.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit 9cc40e2dca)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:42:03 -08:00
John Johansen
9a2f0ff702 Merge profiles: transmission-gtk needs attach_disconnected
From LP: #2085377, when using ip netns to torrent traffic through a
VPN, attach_disconnected is needed by the policy because ip netns sets
up a mount namespace.

Fixes: https://bugs.launchpad.net/bugs/2085377
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1395
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 50f260df51)
2025-01-09 01:41:36 -08:00
Georgia Garcia
c153a6916f profiles: transmission-gtk needs attach_disconnected
From LP: #2085377, when using ip netns to torrent traffic through a
VPN, attach_disconnected is needed by the policy because ip netns sets
up a mount namespace.

Fixes: https://bugs.launchpad.net/bugs/2085377
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit f9edc7d4c1)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:41:35 -08:00
John Johansen
2316ad42d4 Merge Allow make-* flags with remount operations
While the mount syscall documentation disallows this, the kernel silently
ignores make-* flags when doing a remount, and real applications were
passing this conflicting set of flags. Because changing the kernel to
reject this combination would break userspace, we should allow them
instead.

For an example: see https://bugs.launchpad.net/apparmor/+bug/2091424.

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1466
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 3ed5adb665)
2025-01-09 01:41:22 -08:00
Ryan Lee
e46ca918a2 Add a regression test for allowing rprivate with conflicting options
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit 83270fcf68)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:41:22 -08:00
Ryan Lee
610d383de2 Allow make-* flags with remount operations
While the mount syscall documentation disallows this, the kernel silently
ignores make-* flags when doing a remount, and real applications were
passing this conflicting set of flags. Because changing the kernel to
reject this combination would break userspace, we should allow them
instead.

For an example: see https://bugs.launchpad.net/apparmor/+bug/2091424.

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit 52babe8054)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:41:22 -08:00
John Johansen
5ae6f202f8 Merge Add separator between mount flags in dump_flags
The previous code would concatenate all of them together without spacing.
While dump_flags and the corresponding operator<< function aren't currently used,
this will help for when dump_flags is used to debug parser problems.

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1465
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>

(cherry picked from commit 67ee5f8b39)
2025-01-09 01:40:41 -08:00
Ryan Lee
d96d69a60c Add separator between mount flags in dump_flags
The previous code would concatenate all of them together without spacing.
While dump_flags and the corresponding operator<< function aren't currently used,
this will help for when dump_flags is used to debug parser problems.

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit 96718ea4d1)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:40:41 -08:00
John Johansen
164526d16a Merge Update fs type comment in swap regression test
As per https://gitlab.com/apparmor/apparmor/-/merge_requests/1463#note_2259888640: this really should have been a part of !1463, except that cboltz only pointed this out after the MR was already merged. Better late than never, nevertheless.

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1464
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>

(cherry picked from commit f2c398405b)
2025-01-09 01:39:20 -08:00
Ryan Lee
5267a7eb14 Update fs type comment in swap regression test
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit 5cd3362a81)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:39:20 -08:00
John Johansen
fd24c230c9 Merge Fix swap regression test on btrfs
As per !1462 it turns out that the swap regression test on btrfs also needs special casing in order to work properly. This is an analogous patch to check for btrfs.

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1463
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>

(cherry picked from commit 6d7b5df947)
2025-01-09 01:39:00 -08:00
Ryan Lee
14933dc768 Fix swap regression test on btrfs
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit 90c7af69c5)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:39:00 -08:00
John Johansen
9bf91bbe40 Merge fix swap test on zfs file system
Swap on ZFS is *weird*. Getting it working needs some special casing, see e.g. https://askubuntu.com/questions/1198903/can-not-use-swap-file-on-zfs-files-with-holes

Currently, the swap regression test fails on my system (with /tmp in zfs):
```bash
tests/regression/apparmor ❯ ./swap.sh
Error: swap failed. Test 'SWAPON (unconfined)' was expected to 'pass'. Reason for failure 'FAIL: swapon /tmp/sdtest.872368-19048-kN4FN2/swapfile failed - Invalid argument'
Error: swap failed. Test 'SWAPOFF (unconfined)' was expected to 'pass'. Reason for failure 'FAIL: swapoff /tmp/sdtest.872368-19048-kN4FN2/swapfile failed - Invalid argument'
swapon: /tmp/sdtest.872368-19048-kN4FN2/swapfile: skipping - it appears to have holes.
Fatal Error (swap): Unexpected shell error. Run with -x to debug
```

However, just doing a file mount does make the test work on zfs, similar to how it is done with tmpfs. This means we don't need any special-casing for zfs beyond what is already there for working around (similar) tmpfs limitations.

Also, while researching this, it is possible a similar patch is needed for btrfs, but i currently don't have an easy way to test that.
This is non-breaking for anyone *not* using zfs, and it is currently broken with zfs anyways.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1462
Approved-by: Ryan Lee <rlee287@yahoo.com>
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>

(cherry picked from commit e8f1ac4791)
2025-01-09 01:38:43 -08:00
Grimmauld
8597b04aac fix swap test on zfs file system
(cherry picked from commit 9a1b538298)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:38:43 -08:00
John Johansen
28537ff8ec Merge limit buildpath.py setuptools version check to the relevant bits
previously, this check would fail if the setuptools version would contain non-integers.
On my system, that is the case: `setuptools.__version__` is `'75.1.0.post0'`
I believe it is entirely fair to just check the relevant bits and refuse  to continue if those can not be checked properly.
Having some extra slug on the version should not immediately cause issues (e.g. the `post0` here, or slugs like `beta`, `alpha` and the likes).
Probably only very few systems are running setuptools with weird version info, but supporting this is a simple one-line change i figured i might as well MR.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1460
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>

(cherry picked from commit b3de4ef022)
2025-01-09 01:38:28 -08:00
Grimmauld
f90a041921 limit buildpath.py setuptools version check to the relevant bits
previously, this check would fail if the setuptools version would contain non-integers.
On my system, that is the case: `setuptools.__version__` is `'75.1.0.post0'`
I believe it is entirely fair to just check the relevant bits and refuse  to continue if those can not be checked properly.
But haviong something extra on the version should not immediately cause issues (e.g. the `post0` here, or slugs like `beta`, `alpha` and the likes).
Probably only very few systems are running setuptools with weird version info, but supporting this doesn't cost much, i believe.

(cherry picked from commit 3302ae98e4)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:38:27 -08:00
John Johansen
ae0d1aafda Merge postfix-smtp profile fix
Allow locking for /var/spool/postfix/pid/unix.relay.

Example log entry: `type=AVC msg=audit(1733851239.685:8882): apparmor="DENIED" operation="file_lock" profile="postfix-smtp" name="/var/spool/postfix/pid/unix.relay" pid=14222 comm="smtp" requested_mask="k" denied_mask="k" fsuid=91 ouid=0FSUID="postfix" OUID="root"`

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1459
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>

(cherry picked from commit 8a6eb170e1)
2025-01-09 01:37:32 -08:00
pyllyukko
403b3cad10 postfix-smtp profile fix
Allow locking for /var/spool/postfix/pid/unix.relay.

(cherry picked from commit 76dcf46d4f)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:37:32 -08:00
John Johansen
851f6013f6 Merge Use MS_SYNCHRONOUS instead of MS_SYNC
MS_SYNC is a flag for msync(2) while MS_SYNCHRONOUS is a flag for mount(2).
The header used to define MS_SYNC but IMO this is confusing since that's an
unrelated flag.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1458
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 60f1b55ab5)
2025-01-09 01:36:14 -08:00
Zygmunt Krynicki
0838496c32 Use MS_SYNCHRONOUS instead of MS_SYNC
MS_SYNC is a flag for msync(2) while MS_SYNCHRONOUS is a flag for mount(2).
The header used to define MS_SYNC but IMO this is confusing since that's an
unrelated flag.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit d164e877f5)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:36:14 -08:00
John Johansen
191f01b749 Merge Allow spread to use locally-provided kernel
By placing a bzImage into the top level of the AppArmor git repository one can
instruct spread and image-garden to use that image instead of booting
traditionally with an EFI / full disk image pair.

In addition, make error handling in qemu more robust, so failures are both
surfaced and do not cause endless attempts to allocate.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1452
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 239ae21b69)
2025-01-09 01:35:52 -08:00
Zygmunt Krynicki
788d29aacb Allow spread to use locally-provided kernel
By placing a bzImage into the top level of the AppArmor git repository one can
instruct spread and image-garden to use that image instead of booting
traditionally with an EFI / full disk image pair.

In addition, make error handling in qemu more robust, so failures are both
surfaced and do not cause endless attempts to allocate.

Please update image-garden to at least 5a00ead9964df6463e19432ae50e7760fc6da755

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit 7031b5aeee)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:35:52 -08:00
John Johansen
1b70d1e9c2 Merge tests: add regression tests for snapd mount-control
The test adds a very small and simple smoke test that shows that a mount rule
with both fstype and options allows mounts to be performed on a real running
kernel.

The test is structured in a way that should make it easy to extend with new
variants (flags, fstype) in the future.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1445
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>

(cherry picked from commit 11d121409d)
2025-01-09 01:35:27 -08:00
Zygmunt Krynicki
00a5c07db5 tests: add regression tests for snapd mount-control
The test adds a very small and simple smoke test that shows that a mount rule
with both fstype and options allows mounts to be performed on a real running
kernel.

The test is structured in a way that should make it easy to extend with new
variants (flags, fstype) in the future.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit 1f60021979)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:35:27 -08:00
John Johansen
6876448a24 Merge Allow running tests with spread
Spread is a full-system, or integration test suite runner initially developed
to test snapd. Over time it has spread to other projects where it provides a
structured way to organize, run and debug complex full-system interactions.
Spread is documented on https://github.com/canonical/spread and is used in
production since late 2016.

Spread has a notion of backends which are responsible for allocating and
discarding test machines. For the purpose of running AppArmor regression tests,
I've combined spread with my own tool, image garden. The tool provides
off-the-shelf images, constructed on-the-fly from freely available images, and
makes them easily available to spread.

The reason for doing it this way is so that using non-free cloud systems is not
required and anyone can repeat the test process locally, on their own computer.
Vanilla spread is somewhat limited to x86-64 systems but the way I've used it
here makes it equally possible to test x86_64 *and* aarch64 systems. I've done
most of the development on an ARM single-board-computer running on my desk.

Spread requires a top-level spread.yaml file and a collection of task.yaml
files that describe individual tasks (for us, those are just tests). Tasks have
no implied dependency except that to reach a given task, spread will run all
the _prepare_ statements leading to that task, starting from the project, test
suite and then task. With proper care one can then run a specific individual
test with a one-line command, for example:

```
spread -v garden:ubuntu-cloud-24.04:tests/regression/apparmor:at_secure
```

This will prepare a fresh ubuntu-cloud-24.04 system (matching the CPU
architecture of the host), copy the project tree into the test machine, install
all the build dependencies, build all the parts of apparmor and then run one
specific variant of the regression test, namely the at_secure program.
Importantly the same test can also run on, say debian-cloud-13 (Debian Trixie),
but also, if you have a Google cloud account, on Google Compute Engine or in
one of the other backends either built into spread or available as a fork of
spread or as a helper for ad-hoc backend. Spread can also create more than one
worker per system and distribute the tests to all of the available instances.
In no way are we locking ourselves out of the ability to run our test suite on
our target of choice.

Spread has other useful switches, such as:
- `-reuse` for keeping machines around until discarded with -discard
- `-resend` for re-sending updated copy of the project (useful for -reuse)
- `-debug` for starting an interactive shell on any failure
- `-shell` for starting an interactive shell instead of the `execute` phase

This first patch contains just the spread elements, assuming that both spread
and image-garden are externally installed. A GitLab continuous integration
installing everything required and running a subset of tests will follow
shortly.

I've expanded the initial selection of systems to allow running all the tests
on several versions of Ubuntu, Debian and openSUSE, mainly as a sanity check
but also to showcase how practical spread is at covering real-world systems.

A number of tests are currently failing:

    - garden:debian-cloud-12:tests/regression/apparmor:attach_disconnected
    - garden:debian-cloud-12:tests/regression/apparmor:deleted
    - garden:debian-cloud-12:tests/regression/apparmor:unix_fd_server
    - garden:debian-cloud-12:tests/regression/apparmor:unix_socket_pathname
    - garden:debian-cloud-13:tests/regression/apparmor:attach_disconnected
    - garden:debian-cloud-13:tests/regression/apparmor:deleted
    - garden:debian-cloud-13:tests/regression/apparmor:unix_fd_server
    - garden:debian-cloud-13:tests/regression/apparmor:unix_socket_pathname
    - garden:opensuse-cloud-15.6:tests/regression/apparmor:attach_disconnected
    - garden:opensuse-cloud-15.6:tests/regression/apparmor:deleted
    - garden:opensuse-cloud-15.6:tests/regression/apparmor:e2e
    - garden:opensuse-cloud-15.6:tests/regression/apparmor:unix_fd_server
    - garden:opensuse-cloud-15.6:tests/regression/apparmor:unix_socket_pathname
    - garden:opensuse-cloud-15.6:tests/regression/apparmor:xattrs_profile

In addition, only on openSUSE, I've skipped the entire test suite of the utils
directory, as it requires python3 ttk themes, which I cannot find in packaged
form.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1432
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>

(cherry picked from commit d9304c7653)
2025-01-09 01:31:39 -08:00
Zygmunt Krynicki
297cd44aff Document spread tests in README.md
Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit d27377a62f)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:31:39 -08:00
Zygmunt Krynicki
32da740f1b Third iteration of spread support
- Tests defined in utils/test are now described by a task.yaml in the same
  directory and can run concurrently across many machines.
- Tests for utils/ are now executed on openSUSE Tumbleweed since ttk themes is
  no longer a hard dependency in master.
- Tests no longer run on openSUSE Leap 15.6 due to the age of default
  Python (3.6) and gcc/g++. The tight integration with SWIG which does
  not seem to support other Python versions very well. Perl hard-codes
  old GCC for extension modules. The upcoming openSUSE Leap 16 should be
  a viable target. In the meantime we can still test everything through
  rolling-release Tumbleweed.
- Formatting of YAML files is now more uniform, at four spaces per tab.
- The run-spread.sh script is now in the root of the tree. The script allows
  running all spread tests sequentially on one system, while collecting logs
  and artifacts for convenient analysis after the fact.
- All systems are adjusted to run _four_ workers in parallel with _two_ virtual
  cores each and equipped with 1.5GB of virtual memory. This aims to best
  utilize the capacity of a typical CI worker with two to four cores and about
  8GB of available memory.
- Failing tests are marked as such, so that as a whole the entire spread suite
  can pass and be useful at catching regressions.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit 1df91e2c8c)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:31:39 -08:00
Zygmunt Krynicki
b24f0bbfa8 Second iteration of spread support
Compared to v1 the following improvements have been made:

- The cost of installing packages have been shifted from each startup to image
  preparation phase, thanks to the integration of custom cloud-init profiles
  into image-garden. This has dramatic impact on iteration time while also
  entirely removing requirement to be online to run once a prepared image is
  available.

- Support for running on Google Compute Engine has been removed since it would
  not be able to use cloud-init the same way would currently only complicate
  setup.

- The number of workers have been tuned for local iteration, aiming for
  comfortable work with 16GB of memory on the host. Once CI/CD pipeline
  support is introduced I will add a dedicated entry so that resources are
  utilized well both locally and when running in CI.

- The set of regression tests listed in tests/regression/apparmor/task.yaml is
  now cross-checked so introduction of a new test to the makefile there is
  automatically flagged and causes spread to fail with a clear message.

- The task tests/unit/utils has been improved to generate profiles. Thanks to
  Christian Boltz for explaining this relationship between tests.

- A number of comments have been improved and cleaned up for readability,
  accuracy and sometimes better grammar.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit c95ac4d350)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:31:39 -08:00
Zygmunt Krynicki
5a4ddbeaeb Allow running tests with spread
Spread is a full-system, or integration test suite runner initially developed
to test snapd. Over time it has spread to other projects where it provides a
structured way to organize, run and debug complex full-system interactions.
Spread is documented on https://github.com/canonical/spread and is used in
production since late 2016.

Spread has a notion of backends which are responsible for allocating and
discarding test machines. For the purpose of running AppArmor regression tests,
I've combined spread with my own tool, image garden. The tool provides
off-the-shelf images, constructed on-the-fly from freely available images, and
makes them easily available to spread.

The reason for doing it this way is so that using non-free cloud systems is not
required and anyone can repeat the test process locally, on their own computer.
Vanilla spread is somewhat limited to x86-64 systems but the way I've used it
here makes it equally possible to test x86_64 *and* aarch64 systems. I've done
most of the development on an ARM single-board-computer running on my desk.

Spread requires a top-level spread.yaml file and a collection of task.yaml
files that describe individual tasks (for us, those are just tests). Tasks have
no implied dependency except that to reach a given task, spread will run all
the _prepare_ statements leading to that task, starting from the project, test
suite and then task. With proper care one can then run a specific individual
test with a one-line command, for example:

```
spread -v garden:ubuntu-cloud-24.04:tests/regression/apparmor:at_secure
```

This will prepare a fresh ubuntu-cloud-24.04 system (matching the CPU
architecture of the host), copy the project tree into the test machine, install
all the build dependencies, build all the parts of apparmor and then run one
specific variant of the regression test, namely the at_secure program.
Importantly the same test can also run on, say debian-cloud-13 (Debian Trixie),
but also, if you have a Google cloud account, on Google Compute Engine or in
one of the other backends either built into spread or available as a fork of
spread or as a helper for ad-hoc backend. Spread can also create more than one
worker per system and distribute the tests to all of the available instances.
In no way are we locking ourselves out of the ability to run our test suite on
our target of choice.

Spread has other useful switches, such as:
- `-reuse` for keeping machines around until discarded with -discard
- `-resend` for re-sending updated copy of the project (useful for -reuse)
- `-debug` for starting an interactive shell on any failure
- `-shell` for starting an interactive shell instead of the `execute` phase

This first patch contains just the spread elements, assuming that both spread
and image-garden are externally installed. A GitLab continuous integration
installing everything required and running a subset of tests will follow
shortly.

I've expanded the initial selection of systems to allow running all the tests
on several versions of Ubuntu, Debian and openSUSE, mainly as a sanity check
but also to showcase how practical spread is at covering real-world systems.

A number of systems and tests are currently failing:

- garden:debian-cloud-12:tests/regression/apparmor:attach_disconnected
- garden:debian-cloud-12:tests/regression/apparmor:deleted
- garden:debian-cloud-12:tests/regression/apparmor:unix_fd_server
- garden:debian-cloud-12:tests/regression/apparmor:unix_socket_pathname
- garden:debian-cloud-13:tests/regression/apparmor:attach_disconnected
- garden:debian-cloud-13:tests/regression/apparmor:deleted
- garden:debian-cloud-13:tests/regression/apparmor:unix_fd_server
- garden:debian-cloud-13:tests/regression/apparmor:unix_socket_pathname
- garden:opensuse-cloud-15.6:tests/regression/apparmor:attach_disconnected
- garden:opensuse-cloud-15.6:tests/regression/apparmor:deleted
- garden:opensuse-cloud-15.6:tests/regression/apparmor:e2e
- garden:opensuse-cloud-15.6:tests/regression/apparmor:unix_fd_server
- garden:opensuse-cloud-15.6:tests/regression/apparmor:unix_socket_pathname
- garden:opensuse-cloud-15.6:tests/regression/apparmor:xattrs_profile
- garden:opensuse-cloud-tumbleweed:tests/regression/apparmor:attach_disconnected
- garden:opensuse-cloud-tumbleweed:tests/regression/apparmor:deleted
- garden:opensuse-cloud-tumbleweed:tests/regression/apparmor:unix_fd_server
- garden:opensuse-cloud-tumbleweed:tests/regression/apparmor:unix_socket_pathname
- garden:ubuntu-cloud-22.04:tests/regression/apparmor:attach_disconnected

In addition, only on openSUSE, I've skipped the entire test suite of the utils
directory, as it requires python3 ttk themes, which I cannot find in packaged
form.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit cc04181578)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:31:39 -08:00
Zygmunt Krynicki
fd253d1c31 Allow running exactly one test in utils/test
The new check-one-test-% pattern rule allows running individual test scripts.
This allows them to be tested in parallel across many Make worker threads or
across many distinct machines with spread.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit 9588b06e0f)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:31:39 -08:00
John Johansen
505faeff10 Merge Add explicit test for parser priority-based carveouts
Tests #466 but is marked as expected fail due to that bug not being resolved.

Depends on !1441 which adds the xfail infrastructure to the parser equality testing framework, and should be rebased on top of master once that MR is merged.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1443
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit e1d8bf1888)
2025-01-09 01:30:12 -08:00
Ryan Lee
3d5346b48e parser equality tests: print both profiles upon test failure
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit b925d8acff)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:30:12 -08:00
Ryan Lee
1d9e28df35 Add explicit test for parser priority-based carveouts
These are marked as expected fail due to a bug in the parser's priority
handling.

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit 7b5f4c0d6f)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:30:12 -08:00
John Johansen
9995e36347 Merge parser: equality tests: add the ability have tests that are a known problem
currently the equality tests require the tests to PASS as known equality
or inequality. Add the ability to add tests that are a known problem
and are expected to fail the equality, or inequality test.

This is done by using

   verify_binary_xequality
   verify_binary_xinequality

This allows new tests to be added to document a known issue, without
having to develop the fix for the issue. The use of this facility
is expected to be temporary, so any test marked as xequality or
xinequality will be noisy but not fail the other tests until they
are fixed, at which point they will cause the tests to fail to
force them to be updated to the correct equality or inequality
test.

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

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1441
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 53e322b755)
2025-01-09 01:27:50 -08:00
John Johansen
08aeeedc69 parser: equality tests: add the ability have tests that are a known problem
currently the equality tests require the tests to PASS as known equality
or inequality. Add the ability to add tests that are a known problem
and are expected to fail the equality, or inequality test.

This is done by using

   verify_binary_xequality
   verify_binary_xinequality

This allows new tests to be added to document a known issue, without
having to develop the fix for the issue. The use of this facility
is expected to be temporary, so any test marked as xequality or
xinequality will be noisy but not fail the other tests until they
are fixed, at which point they will cause the tests to fail to
force them to be updated to the correct equality or inequality
test.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit b81ea65c1c)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:27:50 -08:00
John Johansen
d426129baf Merge profiles: update bwrap profile
Update the bwrap profile so that it will attach to application profiles
if present.

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

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1435
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 662a26d133)
2025-01-09 01:26:51 -08:00
John Johansen
cda7af8561 profiles: update bwrap profile
Update the bwrap profile so that it will attach to application profiles
if present.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 1979af7710)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:26:51 -08:00
John Johansen
f5844dc267 Merge regression tests: Add test to check for DAC permissions to the testsuite
The regression test suite uses root with capabilities restricted in
several tests. This can cause the test suite to fail in weird and
confusing ways.

Add a test to check for DAC permissiosns from / to the testsuite
and abort running the tests with an error message if DAC permissions
are going to cause the test suite to fail.

Currently the test is pretty basic, but is better than nothing.

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

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1411
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit c5b17d85ea)
2025-01-09 01:21:11 -08:00
John Johansen
52b83aeac4 regression tests: Add test to check for DAC permissions to the testsuite
The regression test suite uses root with capabilities restricted in
several tests. This can cause the test suite to fail in weird and
confusing ways.

Add a test to check for DAC permissiosns from / to the testsuite
and abort running the tests with an error message if DAC permissions
are going to cause the test suite to fail.

Currently the test is pretty basic, but is better than nothing.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 82e4b4ba00)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:21:11 -08:00
John Johansen
89fd37abbf Merge Assorted fixes for test suite portability
I've been working on improved end-to-end testing of AppArmor on a number
of popular Linux distributions. My first run contains Debian, Ubuntu and openSUSE.

This branch contains three small fixes that, mainly, allow running more tests on
openSUSE Tumbleweed.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1431
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>

(cherry picked from commit 6f5cdb7b44)
2025-01-09 01:20:44 -08:00
Zygmunt Krynicki
ffff25e21b On openSUSE 15.6 make fails to find awk
Using this version of make:
```
GNU Make 4.2.1
Built for x86_64-suse-linux-gnu
```
I'm not entirely sure why but the alternative syntax I've used works correctly.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit 4caf0aff81)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:20:44 -08:00
Zygmunt Krynicki
0e7e509ba8 Use larger loop device in mult_mount.sh test
This fixes the test to pass on openSUSE Tumbleweed, where the small size
prevented alloction of an inode for the `lost+found` directory:

```
garden:opensuse-cloud-tumbleweed .../tests/regression/apparmor# mkfs.ext2 -F -m 0 -N 10 /tmp/sdtest.32929-21402-6x826m/image.ext3
mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done
Creating filesystem with 512 1k blocks and 8 inodes

Allocating group tables: done
Writing inode tables: done
ext2fs_mkdir: Could not allocate inode in ext2 filesystem while creating /lost+found
```

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit 32ee85cef8)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:20:44 -08:00
Zygmunt Krynicki
4722ff8e65 Quote trailing backslash in test case
This fixes an error with Python 3.11:

```
test/test-parser-simple-tests.py:420:21: E502 the backslash is redundant between brackets
```

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit 92fcdcab9e)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:20:44 -08:00
Zygmunt Krynicki
d88c6d3bca Use command -v rather than which
Which is technically not POSIX and command -v works everywhere. This fixes
building and running the test suite on openSUSE Tumbleweed.

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit 4b0adc63f5)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:20:44 -08:00
Zygmunt Krynicki
37ea52db0c parser: quote BISON_MAJOR in case it is empty
On a test system without bison installed, make setup fails with:

  /bin/sh: 1: bison: not found
  /bin/sh: 1: test: -ge: unexpected operator

Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
(cherry picked from commit f58fe9cd52)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:20:44 -08:00
John Johansen
da9c59ab09 Merge regression tests: check for setfattr binary used by xattrs_profile
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1412
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 0828ab67b2)
2025-01-09 01:20:26 -08:00
Ryan Lee
8fde25d828 regression tests: check for setfattr binary used by xattrs_profile
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit b39a535cb9)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-09 01:20:26 -08:00
John Johansen
5aa7d046db Merge Write basic file complain-mode regression tests
The test "Complain mode profile (file exec cx permission entry)" currently will only pass on a Ubuntu Oracular system due to a kernel bugfix patch that has not yet been upstreamed or backported.

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1415
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 926929da16)
2025-01-08 23:06:13 -08:00
Ryan Lee
9c3ac976ec Write basic file complain-mode regression tests
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit cb110eaf98)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 23:06:13 -08:00
John Johansen
f433acb219 Merge Dovecot profile: Allow reading of /proc/sys/kernel/core_pattern
See <https://dovecot.org/bugreport.html>

(the link describes how Dovecot requires access to `core_pattern`)

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1331
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>

(cherry picked from commit 2b45586fa9)
2025-01-08 23:01:29 -08:00
pyllyukko
5ee3c03101 Dovecot profile: Allow reading of /proc/sys/kernel/core_pattern
See <https://dovecot.org/bugreport.html>

(cherry picked from commit 0a5a9c465f)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 23:01:29 -08:00
John Johansen
6fdc08a5a5 Merge tests: fix incorrect setfattr call in xattrs_profile
The file was quoted with the following space, making the test broken.

Signed-off-by: Zygmunt Krynicki <me@zygoon.pl>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1429
Approved-by: Ryan Lee <rlee287@yahoo.com>
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>

(cherry picked from commit a2d52fedb2)
2025-01-08 23:01:06 -08:00
Zygmunt Krynicki
e931449ffc tests: fix incorrect setfattr call in xattrs_profile
The file was quoted with the following space, making the test broken.

Signed-off-by: Zygmunt Krynicki <me@zygoon.pl>
(cherry picked from commit 8c16fb2700)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 23:01:06 -08:00
John Johansen
203b4994e9 Merge Quote some variables in regression test suite to allow for spaces
This is not a complete fix for the spaces issue, but it is the next simple step that can be taken before the more difficult work of finding the remaining bugs in each shell script.

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1424
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>

(cherry picked from commit e27b0ad2b6)
2025-01-08 23:00:44 -08:00
Ryan Lee
7b53763f92 Quote some variables in regression test suite to allow for spaces
Signed-off-by: Ryan Lee <ryan.lee@canonical.com>
(cherry picked from commit e7ec01f075)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 23:00:44 -08:00
John Johansen
ddb33d348c Merge parser: fix expr MatchFlag dump
Match Flags convert output to hex but don't restore after outputting
the flag resulting in following numbers being hex encoded. This
results in dumps that can be confusing eg.

rule: \d2  ->  \x2 priority=1001 (0x4/0)< 0x4>

rule: \d7  ->  \a priority=3e9 (0x4/0)< 0x4>

rule: \d10  ->  \n priority=3e9 (0x4/0)< 0x4>

rule: \d9  ->  \t priority=3e9 (0x4/0)< 0x4>

rule: \d14  ->  \xe priority=1001 (0x4/0)< 0x4>

where priority=3e9 is the hex encoded priority 1001.

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

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1419
Approved-by: Maxime Bélair <maxime.belair@canonical.com>
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 2e77129e15)
2025-01-08 22:59:44 -08:00
John Johansen
8274bff547 parser: fix expr MatchFlag dump
Match Flags convert output to hex but don't restore after outputting
the flag resulting in following numbers being hex encoded. This
results in dumps that can be confusing eg.

rule: \d2  ->  \x2 priority=1001 (0x4/0)< 0x4>

rule: \d7  ->  \a priority=3e9 (0x4/0)< 0x4>

rule: \d10  ->  \n priority=3e9 (0x4/0)< 0x4>

rule: \d9  ->  \t priority=3e9 (0x4/0)< 0x4>

rule: \d14  ->  \xe priority=1001 (0x4/0)< 0x4>

where priority=3e9 is the hex encoded priority 1001.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit a31343c5f7)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 22:59:44 -08:00
John Johansen
b17750163b Merge Partial fix for regression tests if parent directory contains spaces
Most `tests/regression/apparmor/*.sh` scripts contain

    . $bin/prologue.inc

This will explode if one of the parent directories contains a space.

Minimized reproducer:

```
# cat test.sh
pwd=`dirname $0`
pwd=`cd $pwd ; /bin/pwd`
bin=$pwd
echo "pwd: $bin"
. $bin/prologue.inc
# ./test.sh
pwd: /tmp/foo bar
./test.sh: line 9: /tmp/foo: No such file or directory
```

Notice that test.sh tries to source `/tmp/foo` instead of `/tmp/foo bar/prologue.inc`.

The fix is to quote the prologue.inc path:

    . "$bin/prologue.inc"

While on it, also fix other uses of $bin - directly and indirectly - by quoting them.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1418
Approved-by: Ryan Lee <rlee287@yahoo.com>
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit a422d2ea17)
2025-01-08 22:58:38 -08:00
Christian Boltz
9c229d1452 Quote indirect uses of $bin and ${bin}
... to avoid issues with spaces in a parent directory's name.

"Indirect uses" means usage of $bin via another variable, for example
`foo=$bin/whatever`

(cherry picked from commit 55db4af979)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 22:58:38 -08:00
Christian Boltz
702f2863a4 Quote all uses of $bin and ${bin}
... to avoid issues with spaces in a parent directory's name.

(cherry picked from commit 22cf88b7c7)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 22:58:38 -08:00
Christian Boltz
989bf0b3ed Fix sourcing prologue.inc if parent directory contains spaces
Most `tests/regression/apparmor/*.sh` scripts contain

    . $bin/prologue.inc

This will explode if one of the parent directories contains a space.

Minimized reproducer:

```
pwd=`dirname $0`
pwd=`cd $pwd ; /bin/pwd`
bin=$pwd
echo "pwd: $bin"
. $bin/prologue.inc
pwd: /tmp/foo bar
./test.sh: line 9: /tmp/foo: No such file or directory
```

Notice that test.sh tries to source `/tmp/foo` instead of `/tmp/foo bar/prologue.inc`.

The fix - as done in this commit - is to quote the prologue.inc path:

    . "$bin/prologue.inc"

(cherry picked from commit e1972eb22f)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 22:58:38 -08:00
John Johansen
9b1d0ea3d8 Merge parser: improve libapparmor_re build and dump info
Fix libapparmor_re/Makefile so it works correctly with rebuilds and
improve state machine dump information, to aid with debugging of
permission handling during the compile.

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

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1410
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 015b41aeb4)
2025-01-08 12:24:33 -08:00
John Johansen
a577d92c7b parser: add the abilitiy to dump the permissions table
Instead of encoding permissions in the accept and accept2 tables
extended perms uses a permissions table and accept becomes an index
into the table.

Add the ability to dump the permissions table so that it can be
compared and debugged.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 45964d34e7)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 12:22:58 -08:00
John Johansen
a16aff8e20 parser: add the accept2 table entry to the chfa dump
The chfa dump is missing information about the accept2 entry. The
accept2 information is necessary to help with debugging state machine
builds as accept2 is used to store quiet and audit information in the
old format or conditional information in the extended perms format.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 00dedf10ad)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 12:22:45 -08:00
John Johansen
4099bf6574 parser: fix and cleanup libapparmor_re/Makefile
The Makefile is missing some of its .h depenedncies causing compiles
to either fail or worse result in bad builds when rebuilding in an
already built tree.

Move the header dependencies into a variable and use it for each
target. While some targets don't need every include as a dependency
and this will result in unnecessary rebuilds in some cases, it makes
the Makefile cleaner, easier to maintain and makes sure a dependency
isn't accidentally missed.

Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 7cc7f47424)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 12:22:28 -08:00
John Johansen
a102e9dc55 Merge parser: fix mapping of AA_CONT_MATCH for policydb compat entries
The mapping of AA_CONT_MATCH was being dropped resulting in the
tcp tests failing because they would only match up to the first conditional
match check in the layout.

Bug: https://gitlab.com/apparmor/apparmor/-/issues/462
Fixes: e29f5ce5f ("parser: if extended perms are supported by the kernel build a permstable")
Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1409
Approved-by: Ryan Lee <rlee287@yahoo.com>
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit f24fc4841f)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 11:44:22 -08:00
John Johansen
7c1eff3867 Prepare for 4.1.0~beta2 release
- bump version

Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
John Johansen
d69d4d3ddf Merge parser: bug fix do not change auditing information when applying deny
The parser recently changed how/where deny information is applied.
commit 1fa45b7c1 ("parser: dfa minimization prepare for extended
permissions") removed the implicit filtering of explicit denies during
the minimization pass. The implicit clear allowed the explicit
information to be carried into the minimization pass and merged with
implicit denies. The end result being a minimized dfa with the explicit
deny information available to be applied post minimization, and
then dropped later at permission encoding in the accept entries.

Extended permission however enable carrying explicit deny information
into the kernel to fix certain bugs like complain mode not being
able to distinguish between implicit and explicit deny rules (ie.
deny rules get ignored in complain mode). However keeping explicit
deny information when unnecessary result in a larger state machine
than necessary and slower compiles.

commit 179c1c1ba ("parser: fix minimization check for filtering_deny")
Moved the explicit apply_and_clear_deny() pass to before minimization
to restore mnimization's ability to create a minimized dfa with
explicit and implicit deny information merged but this also cleared
the explicit deny information that used to be carried through
minimization. This meant that when the deny information was applied
post minimization it resulted in the audit and quiet information
being cleared.

This resulted in the query_label tests failing as they are checking
for the expected audit infomation in the permissions.

Fixes: 179c1c1ba ("parser: fix minimization check for filtering_deny")
Bug: https://gitlab.com/apparmor/apparmor/-/issues/461
Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1408
Approved-by: Ryan Lee <rlee287@yahoo.com>
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit eb365b374d)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
John Johansen
5c04b791d2 Merge aa-mergeprof: prevent backtrace if file not found
If a user specifies a non-existing file to merge into the profiles
(`aa-mergeprof /file/not/found`), this results in a backtrace showing an
AppArmorBug because that file unsurprisingly doesn't end up in the
active_profiles filelist.

Handle this more gracefully by adding a read_error_fatal parameter to
read_profile() that, if set, forwards the exception. With that,
aa-mergeprof doesn't try to list the profiles in this non-existing file.

Note that all other callers of read_profile() continue to ignore read
errors, because aborting just because a single file in /etc/apparmor.d/
(for example a broken symlink) isn't readable would be a bad idea.

This bug was introduced in 4e09f315c3, therefore I propose this patch for 3.0..master

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1403
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 5ebbe788ea)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
John Johansen
be8d85603e Merge php-fpm: widen allowed socket paths
It is common for packaged PHP applications to ship a PHP-FPM
configuration using a scheme of "$app.sock" or or "$app.socket" instead
of using a generic FPM socket.

Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1406
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit bfa9147182)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
Georgia Garcia
450813869a profiles: update dconf abstraction to use @{etc_ro}
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit cbe8d295a5)
Signed-off-by: John Johansen <john.johansen@canonical.com>
(cherry picked from commit 740d7ddae14b69568eaede1b05fe560ae53762df)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
John Johansen
65c41b7fac Merge Misc small fixes to resolve some compiler warnings in regression test suite
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1407
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 14f54f3df2)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
Georgia Garcia
d19e5e8990 Merge Improvements to Postfix profiles
* Support /usr/libexec/postfix/ path
* Added abstractions/{nameservice,postfix-common} to postfix-postscreen
* Added postfix-tlsproxy, postscreen & spawn to postfix-master
    * Added missing postfix-tlsproxy profile
* Added postscreen cache map (see <https://www.postfix.org/postconf.5.html#postscreen_cache_map>)
* Added /{var/spool/postfix/,}pid/pass.smtpd to postfix-smtpd

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1330
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit f7b5d0e783)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
Giampaolo Fresi Roglia
c11ad3e675 abstractions/nameservice: tighten libnss_libvirt file access
(cherry picked from commit 5be4295b5a)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
Georgia Garcia
de2bb16ad6 Merge Support name resolution via libnss-libvirt
Add support for hostname resolution via libnss-libvirt. This change has been tested against the latest oracular version 10.6.0-1ubuntu3.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1362
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit a522e11129)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
Georg Pfuetzenreuter
4f1d2ac549 zgrep: deny passwd access
Bash will try to read the passwd database to find the shell of a user if
$SHELL is not set. This causes zgrep to trigger

```
apparmor="DENIED" operation="open" class="file" profile="zgrep" name="/etc/nsswitch.conf" comm="zgrep" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
apparmor="DENIED" operation="open" class="file" profile="zgrep" name="/etc/passwd" comm="zgrep" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
```

if called in a sanitized environment. As the functionality of zgrep is
not impacted by a limited Bash environment, add deny rules to avoid the
potentially misleading AVC messages.

Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
(cherry picked from commit 48483f2ff8)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
Christian Boltz
cc9f0ed538 ProfileStorage: store correct name
Instead of always storing the name of the main profile, store the child
profile/hat name if we are in a child profile or hat.

As a result, we always get the correct "profile xy" header even for
child profiles when dumping the ProfileStorage object.

Also extend the tests to check that the name gets stored correctly.

(cherry picked from commit cb943e4efc)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
Steve Beattie
1b8afda407 .gitignore: add mod_apparmor and pam_apparmor files
... that are generated during `make`

I propose this patch for 3.x..master.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1374
Approved-by: Ryan Lee <rlee287@yahoo.com>
Approved-by: Steve Beattie <steve+gitlab@nxnw.org>
Merged-by: Steve Beattie <steve+gitlab@nxnw.org>
(cherry picked from commit 3478558904)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
Christian Boltz
9aae96356e Merge apparmor.vim: Add missing units for rlimit cpu and rttime
... and allow whitespace between the number and the unit.

I propose this patch for 3.x, 4.0 and master.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1336
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Christian Boltz <apparmor@cboltz.de>
(cherry picked from commit 247bdd5deb)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
John Johansen
b5db2361f3 Merge profiles: add support for ArchLinux php-legacy package to php-fpm
ArchLinux ships a secondary PHP package called php-legacy with different
paths. As of now, the php-fpm profile will cover this binary but
inadequately restrict it.

Fixes: #454

Closes #454
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1401
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 3d1a3493af)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
John Johansen
5db4a1e7ca Merge abstractions/nameservice: include nameservice-strict
... and drop all rules it contains from abstractions/nameservice.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1373
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 4fe3e30abc)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2025-01-08 10:58:20 -08:00
Christian Boltz
230a975916 Merge smbd: allow capability chown
This is neeed for "inherit owner = yes" in smb.conf.

From man smb.conf:

    inherit owner (S)

    The ownership of new files and directories is normally governed by
    effective uid of the connected user. This option allows the Samba
    administrator to specify that the ownership for new files and
    directories should be controlled by the ownership of the parent
    directory.

Fixes: https://bugzilla.suse.com/show_bug.cgi?id=1234327

I propose this fix for 3.x, 4.x and master.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1456
Approved-by: Ryan Lee <rlee287@yahoo.com>
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>


(cherry picked from commit a315d89a2b)

d3050285 smbd: allow capability chown

Co-authored-by: John Johansen <john@jjmx.net>
2024-12-10 12:50:24 +00:00
Christian Boltz
aa3592a57e Merge postfix-showq profile fix
Allow reading queue ID files from /var/spool/postfix/hold/.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1454
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>


(cherry picked from commit dfe771602d)

3c2aae3a postfix-showq profile fix

Co-authored-by: Christian Boltz <apparmor@cboltz.de>
2024-12-09 18:56:41 +00:00
Christian Boltz
a9aef6f37b Merge python 3.13 fixes/workarounds
Fixes/workarounds for python 3.13 support.

fail.py: handle missing cgitb - workaround for https://gitlab.com/apparmor/apparmor/-/issues/447

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1439
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>


(cherry picked from commit 5fb91616e3)

434e34bb fail.py: handle missing cgitb

Co-authored-by: Christian Boltz <apparmor@cboltz.de>
2024-12-05 17:36:32 +00:00
Georgia Garcia
c71c486313 Merge Remove match statements in utils for older Python compatibility
Somehow the use of new match statements slipped by review despite our commitment to supporting older Python versions. Replace them with an unfortunately-needed if-elif chain.

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1440
Approved-by: Christian Boltz <apparmor@cboltz.de>
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit 36ae21e3fa)
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
2024-12-03 10:00:25 -03:00
Christian Boltz
4213dcc586 Merge aa-remove-unknown: fix readability check [upstreaming]
I am upstreaming this patch that is part of the nix package of apparmor for close to a year now.
This fixes the issue at https://github.com/NixOS/nixpkgs/issues/273164 for more distros than just NixOS.
The original merge Request on the nix side patching this was https://github.com/NixOS/nixpkgs/pull/285915.
However, people had issues with gitlab, so this never hit apparmor upstream until now. This does however also mean this patch has seen production and seems to work quite well.

## Original reasoning/message of the patch author:

This check is intended for ensuring that the profiles file can actually
be opened.  The *actual* check is performed by the shell, not the read
utility, which won't even be executed if the input redirection (and
hence the test) fails.

If the test succeeds, though, using `read` here might actually
jeopardize the test result if there are no profiles loaded and the file
is empty.

This commit fixes that case by simply using `true` instead of `read`.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1438
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>


(cherry picked from commit 93c7035148)

b4aa00de aa-remove-unknown: fix readability check

Co-authored-by: Christian Boltz <apparmor@cboltz.de>
2024-12-01 16:11:25 +00:00
Christian Boltz
ff7f0ff0ea Merge test-logprof: Increase timeout once more
Builds for risc64 are much slower than on other architectures (4-5
seconds with qemu-user or on Litchi Pi 4A).

Since the timeout is only meant as a safety net, increase it generously,
and hopefully for the last time.

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

I propose this patch for 4.0 and master.

Closes #463
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1417
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>


(cherry picked from commit 4c32ad8fb7)

508ace45 test-logprof: Increase timeout once more

Co-authored-by: John Johansen <john@jjmx.net>
2024-11-10 16:34:48 +00:00
John Johansen
9da0f6d3db Merge zgrep: allow reading /etc/nsswitch.conf and /etc/passwd
Seen on various VMs, my guess is that bash wants to translate a uid to a
username.

Log events (slightly shortened)

apparmor="DENIED" operation="open" class="file" profile="zgrep" name="/etc/nsswitch.conf" comm="zgrep" requested_mask="r" denied_mask="r" fsuid=0 ouid=0

apparmor="DENIED" operation="open" class="file" profile="zgrep" name="/etc/passwd" comm="zgrep" requested_mask="r" denied_mask="r" fsuid=0 ouid=0

I propose this patch for 3.0..master

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1357
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit ab16377838)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-30 02:29:38 -07:00
John Johansen
0c1c186267 Merge Fix memory leak in aare_rules UniquePermsCache
When the find fails but the insertion also fails, we leak the new node
that we generated. Delete the new node in this case to avoid leaking
memory.

The question remains, however, as to whether we should implement `operator==` in addition to `operator<` so that they are consistent with each other and `find` works correctly.

Signed-off-by: Ryan Lee <ryan.lee@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1399
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 99261bad11)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-30 02:26:54 -07:00
Steve Beattie
bfd2a0e014 profiles: transmission-daemon needs attach_disconnected
Systemd's PrivateTmp= in transmission service is causing mount namespaces to be used leading to disconnected paths

[395201.414562] audit: type=1400 audit(1727277774.392:573): apparmor="ALLOWED" operation="sendmsg" class="file" info="Failed name lookup - disconnected path" error=-13 profile="transmission-daemon" name="run/systemd/notify" pid=193060 comm="transmission-da" requested_mask="w" denied_mask="w" fsuid=114 ouid=0

Fixes: https://bugs.launchpad.net/bugs/2083548
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1355
Approved-by: Ryan Lee <rlee287@yahoo.com>
Merged-by: Steve Beattie <steve+gitlab@nxnw.org>
(cherry picked from commit 4d3b094d9e)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-30 02:24:50 -07:00
Georgia Garcia
c87fb0a8c1 Merge abstraction: add nameservice-strict.
As I have read multiple MR mentioning the `nameservice-strict`. Therefore, I thought it would make sense to directly import it here.

To give some context, this abstraction is probably the most commonly included abstraction (after `base`). In `apparmor.d`, it is used by over 700 profiles (only counting direct import). Therefore, adding new rules can have an important impact over a lot of profiles.

Note: the abstraction is a direct import from https://gitlab.com/roddhjav/apparmor.d. The license is the same, I obviously kept Morfikov copyright line. However, I am not sure either or not the SPDX identifier can be used here.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1368
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Approved-by: Christian Boltz <apparmor@cboltz.de>
Approved-by: Ryan Lee <rlee287@yahoo.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit 68376e7fee)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-30 02:19:06 -07:00
Georgia Garcia
05debdb2d8 Merge Support name resolution via libnss-libvirt
Add support for hostname resolution via libnss-libvirt. This change has been tested against the latest oracular version 10.6.0-1ubuntu3.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1362
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit a522e11129)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-30 02:18:32 -07:00
Christian Boltz
8b06f61bea Merge ping: allow reading /proc/sys/net/ipv6/conf/all/disable_ipv6
Fixes: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1082190

I propose this patch for 3.0..master.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1340
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Christian Boltz <apparmor@cboltz.de>
(cherry picked from commit 4b6df10fe3)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-30 02:16:50 -07:00
Christian Boltz
34a706f566 Merge abstractions/mesa: allow ~/.cache/mesa_shader_cache_db/
... which is used by Mesa 24.2.2

Reported by darix.

Fixes: https://bugs.launchpad.net/bugs/2081692

I propose this addition for 3.x, 4.0 and master

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1333
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: Christian Boltz <apparmor@cboltz.de>
(cherry picked from commit 62ff290c02)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-30 02:16:30 -07:00
Georgia Garcia
a31cbd07aa profiles: enable php-fpm in /usr/bin and /usr/sbin
To enable the profile in distros that merge sbin into bin.

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/421
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit 2083994513)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-30 02:09:01 -07:00
Akihiro Suda
c4c020cdc0 profiles: slirp4netns: allow pivot_root
`pivot_root` is required for running `slirp4netns --enable-sandbox` inside LXD.
- https://github.com/rootless-containers/slirp4netns/issues/348
- https://github.com/rootless-containers/slirp4netns/blob/v1.3.1/sandbox.c#L101-L234

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
(cherry picked from commit bf5db67284)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-30 02:08:47 -07:00
John Johansen
3c00ed7c85 Merge parser: fix minimization check for filtering deny
commit 1fa45b7c1 ("parser: dfa minimization prepare for extended
permissions") removed implicit filtering of explicit denies in the
minimization pass (the information was ignored in building the set of
final accept states).

The filtering of explicit denies reduces the size of the produced
dfa. Since we need to be smarter about when explicit denies are
kept (eg. during complain mode), and most dfas are limited to 65k
states we currently need to filter explicit deny perms by default.

To compensate commit 2737cb2c2 ("parser: minimization - remove
unnecessary second minimization pass") moved the
apply_and_clear_deny() to before minimization. However its check to
apply removal denials before minimization is broken. Remove minimization
triggering apply_and_clear_deny() and just set the FILTER_DENY flag
by default, until we have better selection of rules/conditions where
explicit deny information should be carried through to the backend.

Fixes: 2737cb2c2 ("parser: minimization - remove unnecessary second minimization pass")
Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1397
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit e9d6e0ba14)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-28 04:58:49 -07:00
John Johansen
bf8fd8cfac Merge parser: fix integer overflow bug in rule priority comparisons
There is an integer overflow when comparing priorities when cmp is
used because it uses subtraction to find lessthan, equal, and greater
than in one operation.

But INT_MAX and INT_MIN are being used by priorities and this results
in INT_MAX - INT_MIN and INT_MIN - INT_MAX which are both overflows
causing an incorrect comparison result and selection of the wrong
rule permission.

Closes: https://gitlab.com/apparmor/apparmor/-/issues/452
Fixes: e3fca60d1 ("parser: add the ability to specify a priority prefix to rules")
Signed-off-by: John Johansen <john.johansen@canonical.com>

Closes #452
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1396
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit a5da9d5b5d)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-28 04:58:35 -07:00
John Johansen
2ca7d30590 Merge utils: catch TypeError exception for binary logs
When a log like system.journal is passed on to aa-genprof, for
example, the user receives a TypeError exception: in method
'parse_record', argument 1 of type 'char *'

This patch catches that exception and displays a more meaningful
message.

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/436
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

Closes #436
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1354
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit cb0f84e101)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-28 04:57:50 -07:00
John Johansen
5a39ae82fe Merge Fix ABI break for aa_log_record
Commit 3c825eb001 adds a field called `execpath` to the `aa_log_record` struct. This field was added in the middle of the struct instead of the end, causing an ABI break in libapparmor without a corresponding major version number bump.
Bug report: https://bugs.launchpad.net/apparmor/+bug/2083435
This is fixed by simply moving execpath at the end of the struct.

Signed-off-by: Maxime Bélair <maxime.belair@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1345
Approved-by: Ryan Lee <rlee287@yahoo.com>
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 07fe0e9a1b)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-28 04:56:24 -07:00
John Johansen
c7ce54bcbf Merge aa-notify: Simplify user interfaces and update man page
aa-notify: Simplify user interfaces and update man page

In notifications, Clicking on "allow" now directly adds the rule without
intermediate window, leading to a smoother UX.
Aligning man page with notify.conf.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1313
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit b5af7d5492)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-28 04:55:20 -07:00
John Johansen
2318ba598c Merge utils: fixes when handling owner file rules
Fixes: https://gitlab.com/apparmor/apparmor/-/issues/429
Fixes: https://gitlab.com/apparmor/apparmor/-/issues/430

Closes #429 and #430
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1320
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 1940b1b7cd)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-28 04:55:00 -07:00
John Johansen
3d403fe2a7 Merge parser: add port range support on network policy
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1321
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit ebeb89cbce)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-28 04:53:20 -07:00
John Johansen
9074b20a95 Merge utils: ignore peer when parsing logs for non-peer access modes
utils: ignore peer when parsing logs for non-peer access modes

Some access modes (create, setopt, getopt, bind, shutdown, listen,
getattr, setattr) cannot be used with a peer in network rules.

Due to how auditing is implemented in the kernel, the peer information
might be available in the log (as faddr= but not daddr=), which causes
a failure in log parsing.

When parsing the log, check if that's the case and ignore the peer,
avoiding the exception on the NetworkRule constructor.

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

Reported-by: Evan Caville <evan.caville@canonical.com>

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

Closes #427
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1314
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit ab5f180b08)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-28 04:52:21 -07:00
John Johansen
7949339b93 Merge parser: fix rule priority destroying rule permissions for some classes
io_uring and userns mediation are encoding permissions on the class
byte. This is a mistake that should never have been allowed.

With the addition of rule priorities the class byte mediates rule,
that ensure the kernel can determine a class is being mediated is
given the highest priority possible, to ensure class mediation can not
be removed by a deny rule. See
  61b7568e1 ("parser: bug fix mediates_X stub rules.")
for details.

Unfortunately this breaks rule classes that encode permissions on the
class byte, because those rules will always have a lower priority and
the class mediates rule will always be selected over them resulting in
only the class mediates permission being on the rule class state.

Fix this by adding the mediaties class rules for these rule classes
with the lowest priority possible. This means that any rule mediating
the class will wipe out the mediates class rule. So add a new mediates
class rule at the same priority, as the rule being added.

This is a naive implementation and does result in more mediates rules
being added than necessary. The rule class could keep track of the
highest priority rule that had been added, and use that to reduce the
number of mediates rules it adds for the class.

Technically we could also get away with not adding the rules for allow
rules, as the kernel doesn't actually check the encoded permission but
whether the class state is not the trap state. But it is required with
deny rules to ensure the deny rule doesn't result in permissions being
removed from the class, resulting in the kernel thinking it is
unmediated. We also want to ensure that mediation is encoded for other
rule types like prompt, and in the future the kernel could check the
permission so we do want to guarantee that the class state has the
MAY_READ permission on it.

Note: there is another set of classes (file, mqueue, dbus, ...) which
encodes a default rule permission as

  class .* <perm>

this encoding is unfortunate in that it will also add the permission
to the class byte, but also sets up following states with the permission.
thankfully, this accespt anything, including nothing generally isn't
valid in the nothing case (eg. a file without any absolute name). For
this set of classes, the high priority mediates rule just ensures
that the null match case does not have permission.

Fixes: 61b7568e1 parser: bug fix mediates_X stub rules.
Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1307
Approved-by: Georgia Garcia <georgia.garcia@canonical.com>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit b6e9df3495)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-28 04:51:48 -07:00
Georgia Garcia
80b6e4ddff Merge libapparmor: make af_protos.h consistent in different archs
af_protos.h is a generated table of the protocols created by looking
for definitions of IPPROTO_* in netinet/in.h. Depending on the
architecture, the order of the table may change when using -dM in the
compiler during the extraction of the defines.

This causes an issue because there is more than one IPPROTO defined
by the value 0: IPPROTO_IP and IPPROTO_HOPOPTS which is a header
extension used by IPv6. So if IPPROTO_HOPOPTS was first in the table,
then protocol=0 in the audit logs would be translated to hopopts.

This caused a failure in arm 32bit:

Output doesn't match expected data:
--- ./test_multi/testcase_unix_01.out	2024-08-15 01:47:53.000000000 +0000
+++ ./test_multi/out/testcase_unix_01.out	2024-08-15 23:42:10.187416392 +0000
@@ -12,7 +12,7 @@
 Peer Addr: @test_abstract_socket
 Network family: unix
 Socket type: stream
-Protocol: ip
+Protocol: hopopts
 Class: net
 Epoch: 1711454639
 Audit subid: 322

By the time protocol is resolved in grammar.y, we don't have have
access to the net family to check if it's inet6. Instead of making
protocol dependent on the net family, make the order of the
af_protos.h table consistent between architectures using -dD.

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

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1309
Approved-by: John Johansen <john@jjmx.net>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit 0ec0e2b035)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-28 04:51:23 -07:00
John Johansen
d824adcf93 Merge utils: change os.mkdir to self.mkpath to create intermediary dirs
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1306
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 4c8a27457e)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-10-28 04:50:24 -07:00
160 changed files with 2741 additions and 4855 deletions

16
.gitignore vendored
View File

@@ -1,4 +1,4 @@
apparmor-*
apparmor-
cscope.*
binutils/aa-enabled
binutils/aa-enabled.1
@@ -7,7 +7,6 @@ binutils/aa-exec.1
binutils/aa-features-abi
binutils/aa-features-abi.1
binutils/aa-load
binutils/aa-load.8
binutils/aa-status
binutils/aa-status.8
binutils/cJSON.o
@@ -121,18 +120,6 @@ libraries/libapparmor/src/tst_aalogmisc
libraries/libapparmor/src/tst_aalogmisc.log
libraries/libapparmor/src/tst_aalogmisc.o
libraries/libapparmor/src/tst_aalogmisc.trs
libraries/libapparmor/src/tst_aalogparse_cpp
libraries/libapparmor/src/tst_aalogparse_cpp.log
libraries/libapparmor/src/tst_aalogparse_cpp.o
libraries/libapparmor/src/tst_aalogparse_cpp.trs
libraries/libapparmor/src/tst_aalogparse_reentrancy
libraries/libapparmor/src/tst_aalogparse_reentrancy.log
libraries/libapparmor/src/tst_aalogparse_reentrancy.o
libraries/libapparmor/src/tst_aalogparse_reentrancy.trs
libraries/libapparmor/src/tst_aalogparse_oldname
libraries/libapparmor/src/tst_aalogparse_oldname.log
libraries/libapparmor/src/tst_aalogparse_oldname.o
libraries/libapparmor/src/tst_aalogparse_oldname.trs
libraries/libapparmor/src/tst_features
libraries/libapparmor/src/tst_features.log
libraries/libapparmor/src/tst_features.o
@@ -203,6 +190,7 @@ utils/apparmor/*.pyc
utils/apparmor/rule/*.pyc
utils/apparmor.egg-info/
utils/build/
!utils/emacs/apparmor-mode.el
utils/htmlcov/
utils/test/common_test.pyc
utils/test/.coverage

View File

@@ -4,40 +4,25 @@ image: ubuntu:latest
# XXX - add a deploy stage to publish man pages, docs, and coverage
# reports
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH
stages:
- build
- test
.ubuntu-common:
.ubuntu-before_script:
before_script:
# Install build-dependencies by loading the package list from the ubuntu/debian cloud-init profile.
- printf '\e[0K%s:%s:%s[collapsed=true]\r\e[0K%s\n' section_start "$(date +%s)" install_deps "Installing dependencies..."
- export DEBIAN_FRONTEND=noninteractive
- apt-get update -qq
- apt-get install --yes yq make lsb-release
- |
printf 'include .image-garden.mk\n$(info $(UBUNTU_CLOUD_INIT_USER_DATA_TEMPLATE))\n.PHONY: nothing\nnothing:\n' \
| make -f - nothing \
| yq '.packages | .[]' \
| xargs apt-get install --yes --no-install-recommends
- printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" install_deps
after_script:
# Inspect the kernel and lsb-release.
- apt-get install --no-install-recommends -y gcc perl liblocale-gettext-perl linux-libc-dev lsb-release make
- lsb_release -a
- uname -a
.install-c-build-deps: &install-c-build-deps
- apt-get install --no-install-recommends -y build-essential apache2-dev autoconf autoconf-archive automake bison dejagnu flex libpam-dev libtool pkg-config python3-all-dev python3-setuptools ruby-dev swig zlib1g-dev
build-all:
stage: build
extends:
- .ubuntu-common
script:
# Run the spread prepare section to build everything.
- yq -r '.prepare' <spread.yaml | SPREAD_PATH=. bash -xeu
- .ubuntu-before_script
artifacts:
name: ${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
expire_in: 30 days
@@ -50,33 +35,39 @@ build-all:
- changehat/mod_apparmor/
- changehat/pam_apparmor/
- profiles/
script:
- *install-c-build-deps
- cd libraries/libapparmor && ./autogen.sh && ./configure --with-perl --with-python --prefix=/usr && make && cd ../.. || { cat config.log ; exit 1 ; }
- make -C parser
- make -C binutils
- make -C utils
- make -C changehat/mod_apparmor
- make -C changehat/pam_apparmor
- make -C profiles
test-libapparmor:
stage: test
needs: ["build-all"]
extends:
- .ubuntu-common
- .ubuntu-before_script
script:
# This is to touch the built files in the test stage to avoid needless rebuilding
- make -C libraries/libapparmor --touch
- *install-c-build-deps
- make -C libraries/libapparmor check
test-parser:
stage: test
needs: ["build-all"]
extends:
- .ubuntu-common
- .ubuntu-before_script
script:
# This is to touch the built files in the test stage to avoid needless rebuilding
- make -C parser --touch
- make -C parser -j $(nproc) tst_binaries
- *install-c-build-deps
- make -C parser check
test-binutils:
stage: test
needs: ["build-all"]
extends:
- .ubuntu-common
- .ubuntu-before_script
script:
- make -C binutils check
@@ -84,15 +75,9 @@ test-utils:
stage: test
needs: ["build-all"]
extends:
- .ubuntu-common
- .ubuntu-before_script
script:
# This is to touch the built files in the test stage to avoid needless rebuilding
- make -C utils --touch
# TODO: move those to cloud-init list?
- printf '\e[0K%s:%s:%s[collapsed=true]\r\e[0K%s\n' section_start "$(date +%s)" install_extra_deps "Installing additional dependencies..."
- apt-get install --no-install-recommends -y libc6-dev libjs-jquery libjs-jquery-throttle-debounce libjs-jquery-isonscreen libjs-jquery-tablesorter flake8 python3-coverage python3-notify2 python3-psutil python3-setuptools python3-tk python3-ttkthemes python3-gi
- printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" install_extra_deps
# See apparmor/apparmor#221
- make -C parser/tst gen_dbus
@@ -108,50 +93,31 @@ test-mod-apparmor:
stage: test
needs: ["build-all"]
extends:
- .ubuntu-common
- .ubuntu-before_script
script:
# This is to touch the built files in the test stage to avoid needless rebuilding
- make -C changehat/mod_apparmor --touch
- make -C changehat/mod_apparmor check
test-profiles:
stage: test
needs: ["build-all"]
extends:
- .ubuntu-common
- .ubuntu-before_script
script:
# This is to touch the built files in the test stage to avoid needless rebuilding
- make -C profiles --touch
- make -C profiles check-parser
- make -C profiles check-abstractions.d
- make -C profiles check-local
# Build the regression tests (don't run them because that needs kernel access)
test-build-regression:
stage: test
needs: ["build-all"]
extends:
- .ubuntu-common
script:
# Additional dependencies required by regression tests
- printf '\e[0K%s:%s:%s[collapsed=true]\r\e[0K%s\n' section_start "$(date +%s)" install_extra_deps "Installing additional dependencies..."
- apt-get install --no-install-recommends -y attr fuse-overlayfs libdbus-1-dev liburing-dev
- printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" install_extra_deps
- make -C tests/regression/apparmor -j $(nproc)
shellcheck:
stage: test
needs: []
extends:
- .ubuntu-common
- .ubuntu-before_script
script:
- printf '\e[0K%s:%s:%s[collapsed=true]\r\e[0K%s\n' section_start "$(date +%s)" install_extra_deps "Installing additional dependencies..."
- apt-get install --no-install-recommends -y python3-minimal file shellcheck xmlstarlet
- printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" install_extra_deps
- shellcheck --version
- "./tests/bin/shellcheck-tree --format=checkstyle
| xmlstarlet tr tests/checkstyle2junit.xslt
> shellcheck.xml"
- apt-get install --no-install-recommends -y python3-minimal file shellcheck xmlstarlet
- shellcheck --version
- './tests/bin/shellcheck-tree --format=checkstyle
| xmlstarlet tr tests/checkstyle2junit.xslt
> shellcheck.xml'
artifacts:
when: always
reports:
@@ -173,26 +139,29 @@ variables:
SAST_EXCLUDED_ANALYZERS: "eslint,flawfinder,semgrep,spotbugs"
SAST_BANDIT_EXCLUDED_PATHS: "*/tst/*, */test/*"
.send-to-coverity: &send-to-coverity
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form file=@$(ls apparmor-*-cov-int.tar.gz) --form version="$(git describe --tags)"
--form description="$(git describe --tags) / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
coverity:
stage: .post
extends:
- .ubuntu-common
- .ubuntu-before_script
only:
refs:
- master
script:
- printf '\e[0K%s:%s:%s[collapsed=true]\r\e[0K%s\n' section_start "$(date +%s)" install_extra_deps "Installing additional dependencies..."
- apt-get install --no-install-recommends -y curl git texlive-latex-recommended
- printf '\e[0K%s:%s:%s\r\e[0K\n' section_end "$(date +%s)" install_extra_deps
- curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64
--form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN
- tar xfz /tmp/cov-analysis-linux64.tgz
- COV_VERSION=$(ls -dt cov-analysis-linux64-* | head -1)
- PATH=$PATH:$(pwd)/$COV_VERSION/bin
- make coverity
- curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME
--form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL
--form file=@$(ls apparmor-*-cov-int.tar.gz) --form version="$(git describe --tags)"
--form description="$(git describe --tags) / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID"
- apt-get install --no-install-recommends -y curl git texlive-latex-recommended
- *install-c-build-deps
- curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64
--form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN
- tar xfz /tmp/cov-analysis-linux64.tgz
- COV_VERSION=$(ls -dt cov-analysis-linux64-* | head -1)
- PATH=$PATH:$(pwd)/$COV_VERSION/bin
- make coverity
- *send-to-coverity
artifacts:
paths:
- "apparmor-*.tar.gz"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PROJECT_PATH == "apparmor/apparmor"

View File

@@ -8,7 +8,6 @@
define DEBIAN_CLOUD_INIT_USER_DATA_TEMPLATE
$(CLOUD_INIT_USER_DATA_TEMPLATE)
packages:
- apache2-dev
- attr
- autoconf
- autoconf-archive
@@ -16,14 +15,10 @@ packages:
- bison
- build-essential
- dejagnu
- dosfstools
- flake8
- flex
- fuse-overlayfs
- gdb
- gettext
- libdbus-1-dev
- libpam0g-dev
- libtool
- liburing-dev
- pkg-config
@@ -35,7 +30,6 @@ packages:
- python3-tk
- python3-ttkthemes
- swig
- toybox
endef
# Ubuntu shares cloud-init profile with Debian.
@@ -44,10 +38,7 @@ UBUNTU_CLOUD_INIT_USER_DATA_TEMPLATE=$(DEBIAN_CLOUD_INIT_USER_DATA_TEMPLATE)
# This is the cloud-init user-data profile for openSUSE Tumbleweed.
define OPENSUSE_tumbleweed_CLOUD_INIT_USER_DATA_TEMPLATE
$(CLOUD_INIT_USER_DATA_TEMPLATE)
- sed -i -e 's/security=selinux/security=apparmor/g' /etc/default/grub
- update-bootloader
packages:
- apache2-devel
- attr
- autoconf
- autoconf-archive
@@ -55,20 +46,15 @@ packages:
- bison
- dbus-1-devel
- dejagnu
- dosfstools
- flex
- fuse-overlayfs
- gcc
- gcc-c++
- gdb
- gettext
- gobject-introspection
- libtool
- liburing2-devel
- make
- pam-devel
- pkg-config
- python3-devel
- python3-flake8
- python3-notify2
- python3-psutil
@@ -76,35 +62,7 @@ packages:
- python3-setuptools
- python3-tk
- python311
- python3-devel
- python311-devel
- swig
endef
define FEDORA_CLOUD_INIT_USER_DATA_TEMPLATE
$(CLOUD_INIT_USER_DATA_TEMPLATE)
packages:
- attr
- autoconf
- autoconf-archive
- automake
- bison
- dbus-devel
- dejagnu
- dosfstools
- flex
- gdb
- gettext
- httpd-devel
- libstdc++-static
- libtool
- liburing-devel
- pam-devel
- perl
- pkg-config
- python3-devel
- python3-flake8
- python3-gobject-base
- python3-notify2
- python3-tkinter
- swig
endef

View File

@@ -59,7 +59,7 @@ coverity: snapshot
mv $(COVERITY_DIR)/build-log.txt $(COVERITY_DIR)/build-log-python-$(subst /,.,$(dir)).txt ;)
cov-build --dir $(COVERITY_DIR) -- sh -c \
"$(foreach dir, $(filter-out utils profiles tests, $(DIRS)), \
$(MAKE) -j $$(nproc) -C $(SNAPSHOT_NAME)/$(dir);) "
$(MAKE) -C $(SNAPSHOT_NAME)/$(dir);) "
tar -cvzf $(SNAPSHOT_NAME)-$(COVERITY_DIR).tar.gz $(COVERITY_DIR)
.PHONY: export_dir

View File

@@ -111,21 +111,13 @@ $ export PYTHON_VERSION=3
$ export PYTHON_VERSIONS=python3
```
Note that, in general, the build steps can be run in parallel, while the test
steps do not gain much speedup from being run in parallel. This is because the
test steps spawn a handful of long-lived test runner processes that mostly
run their tests sequentially and do not use `make`'s jobserver. Moreover,
process spawning overhead constitutes a significant part of test runtime, so
reworking the test harnesses to add parallelism (which would be a major undertaking
for the harnesses that do not have it already) would not produce much of a speedup.
### libapparmor:
```
$ cd ./libraries/libapparmor
$ sh ./autogen.sh
$ sh ./configure --prefix=/usr --with-perl --with-python # see below
$ make -j $(nproc)
$ make
$ make check
$ make install
```
@@ -138,7 +130,7 @@ generate Ruby bindings to libapparmor.]
```
$ cd binutils
$ make -j $(nproc)
$ make
$ make check
$ make install
```
@@ -147,8 +139,7 @@ $ make install
```
$ cd parser
$ make -j $(nproc) # depends on libapparmor having been built first
$ make -j $(nproc) tst_binaries # a build step of make check that can be parallelized
$ make # depends on libapparmor having been built first
$ make check
$ make install
```
@@ -158,7 +149,7 @@ $ make install
```
$ cd utils
$ make -j $(nproc)
$ make
$ make check PYFLAKES=/usr/bin/pyflakes3
$ make install
```
@@ -167,7 +158,7 @@ $ make install
```
$ cd changehat/mod_apparmor
$ make -j $(nproc) # depends on libapparmor having been built first
$ make # depends on libapparmor having been built first
$ make install
```
@@ -176,7 +167,7 @@ $ make install
```
$ cd changehat/pam_apparmor
$ make -j $(nproc) # depends on libapparmor having been built first
$ make # depends on libapparmor having been built first
$ make install
```
@@ -243,7 +234,7 @@ To run:
### Regression tests - using apparmor userspace installed on host
```
$ cd tests/regression/apparmor (requires root)
$ make -j $(nproc) USE_SYSTEM=1
$ make USE_SYSTEM=1
$ sudo make tests USE_SYSTEM=1
$ sudo bash open.sh -r # runs and saves the last testcase from open.sh
```
@@ -256,7 +247,7 @@ $ sudo bash open.sh -r # runs and saves the last testcase from open.sh
```
$ cd tests/regression/apparmor (requires root)
$ make -j $(nproc)
$ make
$ sudo make tests
$ sudo bash open.sh -r # runs and saves the last testcase from open.sh
```
@@ -390,7 +381,6 @@ The aa-notify tool's Python dependencies can be satisfied by installing the
following packages (Debian package names, other distros may vary):
* python3-notify2
* python3-psutil
* python3-sqlite (part of the python3.NN-stdlib package)
* python3-tk
* python3-ttkthemes
* python3-gi

View File

@@ -21,7 +21,7 @@ DESTDIR=/
BINDIR=${DESTDIR}/usr/bin
SBINDIR=${DESTDIR}/usr/sbin
LOCALEDIR=/usr/share/locale
MANPAGES=aa-enabled.1 aa-exec.1 aa-features-abi.1 aa-load.8 aa-status.8
MANPAGES=aa-enabled.1 aa-exec.1 aa-features-abi.1 aa-status.8
WARNINGS = -Wall
CPP_WARNINGS =

View File

@@ -1,77 +0,0 @@
# This publication is intellectual property of Canonical Ltd. Its contents
# can be duplicated, either in part or in whole, provided that a copyright
# label is visibly located on each copy.
#
# All information found in this book has been compiled with utmost
# attention to detail. However, this does not guarantee complete accuracy.
# Neither Canonical Ltd, the authors, nor the translators shall be held
# liable for possible errors or the consequences thereof.
#
# Many of the software and hardware descriptions cited in this book
# are registered trademarks. All trade names are subject to copyright
# restrictions and may be registered trade marks. Canonical Ltd
# essentially adheres to the manufacturer's spelling.
#
# Names of products and trademarks appearing in this book (with or without
# specific notation) are likewise subject to trademark and trade protection
# laws and may thus fall under copyright restrictions.
#
=pod
=head1 NAME
aa-load - load precompiled AppArmor policy from cache location(s)
=head1 SYNOPSIS
B<aa-load> [options] (cache file|cache dir|cache base dir)+
=head1 DESCRIPTION
B<aa-load> loads precompiled AppArmor policy from the specified locations.
=head1 OPTIONS
B<aa-load> accepts the following arguments:
=over 4
=item -f, --force
Force B<aa-load> to load a policy even if its abi does not match the kernel abi.
=item -d, --debug
Display debug messages.
=item -v, --verbose
Display progress and error messages.
=item -n, --dry-run
Do not actually load the specified policy/policies into the kernel.
=item -h, --help
Display a brief usage guide.
=back
=head1 EXIT STATUS
Upon exiting, B<aa-load> returns 0 upon success and 1 upon an error loading
the precompiled policy.
=head1 BUGS
If you find any bugs, please report them at
L<https://gitlab.com/apparmor/apparmor/-/issues>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), and L<https://wiki.apparmor.net>.
=cut

View File

@@ -309,8 +309,9 @@ static int load_arg(char *arg)
static void print_usage(const char *command)
{
printf("Usage: %s [OPTIONS] (cache file|cache dir|cache base dir)+\n"
"Load precompiled AppArmor policy from cache location(s)\n\n"
printf("Usage: %s [OPTIONS] (cache file|cache dir|cache base dir)]*\n"
"Load Precompiled AppArmor policy from a cache location or \n"
"locations.\n\n"
"Options:\n"
" -f, --force load policy even if abi does not match the kernel\n"
" -d, --debug display debug messages\n"

View File

@@ -20,8 +20,6 @@
#include <ctype.h>
#include <dirent.h>
#include <regex.h>
#include <libintl.h>
#define _(s) gettext(s)
#include <sys/apparmor.h>
#include <sys/apparmor_private.h>
@@ -133,7 +131,7 @@ const char *process_statuses[] = {"enforce", "complain", "prompt", "kill", "unco
#define eprintf(...) \
do { \
if (!quiet) \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, __VA_ARGS__); \
} while (0)
#define dprintf(...) \
@@ -158,14 +156,14 @@ static int open_profiles(FILE **fp)
ret = stat("/sys/module/apparmor", &st);
if (ret != 0) {
eprintf(_("apparmor not present.\n"));
eprintf("apparmor not present.\n");
return AA_EXIT_DISABLED;
}
dprintf(_("apparmor module is loaded.\n"));
dprintf("apparmor module is loaded.\n");
ret = aa_find_mountpoint(&apparmorfs);
if (ret == -1) {
eprintf(_("apparmor filesystem is not mounted.\n"));
eprintf("apparmor filesystem is not mounted.\n");
return AA_EXIT_NO_CONTROL;
}
@@ -178,9 +176,9 @@ static int open_profiles(FILE **fp)
*fp = fopen(apparmor_profiles, "r");
if (*fp == NULL) {
if (errno == EACCES) {
eprintf(_("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 {
eprintf(_("Could not open %s: %s"), apparmor_profiles, strerror(errno));
eprintf("Could not open %s: %s", apparmor_profiles, strerror(errno));
}
return AA_EXIT_NO_PERM;
}
@@ -353,7 +351,7 @@ static int get_processes(struct profile *profiles,
continue;
} else if (rc == -1 ||
asprintf(&exe, "/proc/%s/exe", entry->d_name) == -1) {
eprintf(_("ERROR: Failed to allocate memory\n"));
eprintf("ERROR: Failed to allocate memory\n");
ret = AA_EXIT_INTERNAL_ERROR;
goto exit;
} else if (mode) {
@@ -376,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) {
eprintf(_("ERROR: Failed to allocate memory\n"));
eprintf("ERROR: Failed to allocate memory\n");
ret = AA_EXIT_INTERNAL_ERROR;
goto exit;
}
@@ -600,7 +598,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) {
eprintf(_("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;
}
@@ -666,7 +664,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) {
eprintf(_("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;
}
@@ -728,7 +726,7 @@ exit:
static int print_legacy(const char *command)
{
printf(_("Usage: %s [OPTIONS]\n"
printf("Usage: %s [OPTIONS]\n"
"Legacy options and their equivalent command\n"
" --profiled --count --profiles\n"
" --enforced --count --profiles --mode=enforced\n"
@@ -736,8 +734,8 @@ static int print_legacy(const char *command)
" --kill --count --profiles --mode=kill\n"
" --prompt --count --profiles --mode=prompt\n"
" --special-unconfined --count --profiles --mode=unconfined\n"
" --process-mixed --count --ps --mode=mixed\n"),
command);
" --process-mixed --count --ps --mode=mixed\n",
command);
exit(0);
return 0;
@@ -747,7 +745,7 @@ static int usage_filters(void)
{
long unsigned int i;
printf(_("Usage of filters\n"
printf("Usage of filters\n"
"Filters are used to reduce the output of information to only\n"
"those entries that will match the filter. Filters use posix\n"
"regular expression syntax. The possible values for exes that\n"
@@ -757,7 +755,7 @@ static int usage_filters(void)
" --filter.profiles: regular expression to match displayed profile names\n"
" --filter.pid: regular expression to match displayed processes pids\n"
" --filter.exe: regular expression to match executable\n"
));
);
for (i = 0; i < ARRAY_SIZE(process_statuses); i++) {
printf("%s%s", i ? ", " : "", process_statuses[i]);
}
@@ -775,7 +773,7 @@ static int print_usage(const char *command, bool error)
status = EXIT_FAILURE;
}
printf(_("Usage: %s [OPTIONS]\n"
printf("Usage: %s [OPTIONS]\n"
"Displays various information about the currently loaded AppArmor policy.\n"
"Default if no options given\n"
" --show=all\n\n"
@@ -792,8 +790,8 @@ static int print_usage(const char *command, bool error)
" --verbose (default) displays data points about loaded policy set\n"
" --quiet don't output error messages\n"
" -h[(legacy|filters)] this message, or info on the specified option\n"
" --help[=(legacy|filters)] this message, or info on the specified option\n"),
command);
" --help[=(legacy|filters)] this message, or info on the specified option\n",
command);
exit(status);
@@ -869,7 +867,7 @@ static int parse_args(int argc, char **argv)
} else if (strcmp(optarg, "filters") == 0) {
usage_filters();
} else {
eprintf(_("Error: Invalid --help option '%s'.\n"), optarg);
eprintf("Error: Invalid --help option '%s'.\n", optarg);
print_usage(argv[0], true);
break;
}
@@ -937,7 +935,7 @@ static int parse_args(int argc, char **argv)
} else if (strcmp(optarg, "processes") == 0) {
opt_show = SHOW_PROCESSES;
} else {
eprintf(_("Error: Invalid --show option '%s'.\n"), optarg);
eprintf("Error: Invalid --show option '%s'.\n", optarg);
print_usage(argv[0], true);
break;
}
@@ -959,7 +957,7 @@ static int parse_args(int argc, char **argv)
break;
default:
eprintf(_("Error: Invalid command.\n"));
eprintf("Error: Invalid command.\n");
print_usage(argv[0], true);
break;
}
@@ -984,7 +982,7 @@ int main(int argc, char **argv)
if (argc > 1) {
int pos = parse_args(argc, argv);
if (pos < argc) {
eprintf(_("Error: Unknown options.\n"));
eprintf("Error: Unknown options.\n");
print_usage(progname, true);
}
} else {
@@ -996,24 +994,24 @@ int main(int argc, char **argv)
init_filters(&filters, &filter_set);
if (regcomp(filters.mode, opt_mode, REG_NOSUB) != 0) {
eprintf(_("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) {
eprintf(_("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) {
eprintf(_("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) {
eprintf(_("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;
@@ -1028,7 +1026,7 @@ int main(int argc, char **argv)
outf_save = outf;
outf = open_memstream(&buffer, &buffer_size);
if (!outf) {
eprintf(_("Failed to open memstream: %m\n"));
eprintf("Failed to open memstream: %m\n");
return AA_EXIT_INTERNAL_ERROR;
}
}
@@ -1039,7 +1037,7 @@ int main(int argc, char **argv)
*/
ret = get_profiles(fp, &profiles, &nprofiles);
if (ret != 0) {
eprintf(_("Failed to get profiles: %d....\n"), ret);
eprintf("Failed to get profiles: %d....\n", ret);
goto out;
}
@@ -1068,7 +1066,7 @@ int main(int argc, char **argv)
ret = get_processes(profiles, nprofiles, &processes, &nprocesses);
if (ret != 0) {
eprintf(_("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, opt_json,
processes, nprocesses);
@@ -1094,14 +1092,14 @@ int main(int argc, char **argv)
outf = outf_save;
json = cJSON_Parse(buffer);
if (!json) {
eprintf(_("Failed to parse json output"));
eprintf("Failed to parse json output");
ret = AA_EXIT_INTERNAL_ERROR;
goto out;
}
pretty = cJSON_Print(json);
if (!pretty) {
eprintf(_("Failed to print pretty json"));
eprintf("Failed to print pretty json");
ret = AA_EXIT_INTERNAL_ERROR;
goto out;
}

View File

@@ -1,14 +1,14 @@
# Translations for aa_enabled
# Copyright (C) 2024 Canonical Ltd
# This file is distributed under the same license as the AppArmor package.
# John Johansen <john.johansen@canonical.com>, 2020.
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Canonical Ltd
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n"
"POT-Creation-Date: 2024-08-31 15:59-0700\n"
"POT-Creation-Date: 2020-10-14 03:52-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"

View File

@@ -1,14 +1,14 @@
# Translations for aa_exec
# Copyright (C) 2024 Canonical Ltd
# This file is distributed under the same license as the AppArmor package.
# John Johansen <john.johansen@canonical.com>, 2020.
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Canonical Ltd
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n"
"POT-Creation-Date: 2024-08-31 15:59-0700\n"
"POT-Creation-Date: 2020-10-14 03:52-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"

View File

@@ -1,14 +1,14 @@
# Translations for aa_features_abi
# Copyright (C) 2024 Canonical Ltd
# This file is distributed under the same license as the AppArmor package.
# John Johansen <john.johansen@canonical.com>, 2011.
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Canonical Ltd
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n"
"POT-Creation-Date: 2024-08-31 15:59-0700\n"
"POT-Creation-Date: 2020-10-14 03:52-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"

View File

@@ -1,34 +0,0 @@
# Translations for aa_load
# Copyright (C) 2024 Canonical Ltd
# This file is distributed under the same license as the AppArmor package.
# John Johansen <john.johansen@canonical.com>, 2020.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n"
"POT-Creation-Date: 2024-08-31 15:59-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../aa_load.c:40
msgid "aa-load: WARN: "
msgstr ""
#: ../aa_load.c:41
msgid "aa-load: ERROR: "
msgstr ""
#: ../aa_load.c:51
msgid "\n"
msgstr ""
#: ../aa_load.c:52
msgid "aa-load: DEBUG: "
msgstr ""

View File

@@ -1,165 +0,0 @@
# Translations for aa_status
# Copyright (C) 2024 Canonical Ltd
# This file is distributed under the same license as the AppArmor package.
# John Johansen <john.johansen@canonical.com>, 2024.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n"
"POT-Creation-Date: 2024-08-31 17:49-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../aa_status.c:161
msgid "apparmor not present.\n"
msgstr ""
#: ../aa_status.c:164
msgid "apparmor module is loaded.\n"
msgstr ""
#: ../aa_status.c:168
msgid "apparmor filesystem is not mounted.\n"
msgstr ""
#: ../aa_status.c:181
msgid "You do not have enough privilege to read the profile set.\n"
msgstr ""
#: ../aa_status.c:183
#, c-format
msgid "Could not open %s: %s"
msgstr ""
#: ../aa_status.c:356 ../aa_status.c:379
msgid "ERROR: Failed to allocate memory\n"
msgstr ""
#: ../aa_status.c:587 ../aa_status.c:653
#, c-format
msgid "Error: failed to compile sub filter '%s'\n"
msgstr ""
#: ../aa_status.c:715
#, c-format
msgid ""
"Usage: %s [OPTIONS]\n"
"Legacy options and their equivalent command\n"
" --profiled --count --profiles\n"
" --enforced --count --profiles --mode=enforced\n"
" --complaining --count --profiles --mode=complain\n"
" --kill --count --profiles --mode=kill\n"
" --prompt --count --profiles --mode=prompt\n"
" --special-unconfined --count --profiles --mode=unconfined\n"
" --process-mixed --count --ps --mode=mixed\n"
msgstr ""
#: ../aa_status.c:734
#, c-format
msgid ""
"Usage of filters\n"
"Filters are used to reduce the output of information to only\n"
"those entries that will match the filter. Filters use posix\n"
"regular expression syntax. The possible values for exes that\n"
"support filters are below\n"
"\n"
" --filter.mode: regular expression to match the profile "
"mode modes: enforce, complain, kill, unconfined, mixed\n"
" --filter.profiles: regular expression to match displayed profile names\n"
" --filter.pid: regular expression to match displayed processes pids\n"
" --filter.exe: regular expression to match executable\n"
msgstr ""
#: ../aa_status.c:762
#, c-format
msgid ""
"Usage: %s [OPTIONS]\n"
"Displays various information about the currently loaded AppArmor policy.\n"
"Default if no options given\n"
" --show=all\n"
"\n"
"OPTIONS (one only):\n"
" --enabled returns error code if AppArmor not enabled\n"
" --show=X What information to show. {profiles,processes,all}\n"
" --count print the number of entries. Implies --quiet\n"
" --filter.mode=filter see filters\n"
" --filter.profiles=filter see filters\n"
" --filter.pid=filter see filters\n"
" --filter.exe=filter see filters\n"
" --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|filters)] this message, or info on the specified option\n"
" --help[=(legacy|filters)] this message, or info on the specified option\n"
msgstr ""
#: ../aa_status.c:856
#, c-format
msgid "Error: Invalid --help option '%s'.\n"
msgstr ""
#: ../aa_status.c:924
#, c-format
msgid "Error: Invalid --show option '%s'.\n"
msgstr ""
#: ../aa_status.c:946
msgid "Error: Invalid command.\n"
msgstr ""
#: ../aa_status.c:971
msgid "Error: Unknown options.\n"
msgstr ""
#: ../aa_status.c:983
#, c-format
msgid "Error: failed to compile mode filter '%s'\n"
msgstr ""
#: ../aa_status.c:988
#, c-format
msgid "Error: failed to compile profiles filter '%s'\n"
msgstr ""
#: ../aa_status.c:994
#, c-format
msgid "Error: failed to compile ps filter '%s'\n"
msgstr ""
#: ../aa_status.c:1000
#, c-format
msgid "Error: failed to compile exe filter '%s'\n"
msgstr ""
#: ../aa_status.c:1015
#, c-format
msgid "Failed to open memstream: %m\n"
msgstr ""
#: ../aa_status.c:1026
#, c-format
msgid "Failed to get profiles: %d....\n"
msgstr ""
#: ../aa_status.c:1050
#, c-format
msgid "Failed to get processes: %d....\n"
msgstr ""
#: ../aa_status.c:1076
msgid "Failed to parse json output"
msgstr ""
#: ../aa_status.c:1083
msgid "Failed to print pretty json"
msgstr ""

View File

@@ -1 +1 @@
4.1.0~beta1
4.1.0~beta3

View File

@@ -92,8 +92,6 @@ if test "$ac_cv_prog_cc_c99" = "no"; then
AC_MSG_ERROR([C99 mode is required to build libapparmor])
fi
AC_PROG_CXX
m4_ifndef([AX_CHECK_COMPILE_FLAG], [AC_MSG_ERROR(['autoconf-archive' missing])])
EXTRA_CFLAGS="-Wall $EXTRA_WARNINGS -fPIC"
AX_CHECK_COMPILE_FLAG([-flto-partition=none], , , [-Werror])
@@ -101,7 +99,6 @@ AS_VAR_IF([ax_cv_check_cflags__Werror__flto_partition_none], [yes],
[EXTRA_CFLAGS="$EXTRA_CFLAGS -flto-partition=none"]
,)
AC_SUBST([AM_CFLAGS], ["$EXTRA_CFLAGS"])
AC_SUBST([AM_CXXFLAGS], ["$EXTRA_CFLAGS"])
AC_OUTPUT(
Makefile

View File

@@ -22,15 +22,15 @@
=head1 NAME
aa_change_hat - change to or from a "hat" within a AppArmor profile
aa_change_hat - change to or from a "hat" within a AppArmor profile
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
B<int aa_change_hat (const char *subprofile, unsigned long magic_token);>
B<int aa_change_hat (char *subprofile, unsigned long magic_token);>
B<int aa_change_hatv (const char *subprofiles[], unsigned long magic_token);>
B<int aa_change_hatv (char *subprofiles[], unsigned long magic_token);>
B<int aa_change_hat_vargs (unsigned long magic_token, ...);>

View File

@@ -22,7 +22,7 @@
=head1 NAME
aa_change_profile, aa_change_onexec - change a task's profile
aa_change_profile, aa_change_onexec - change a tasks profile
=head1 SYNOPSIS
@@ -58,8 +58,8 @@ The aa_change_onexec() function is like the aa_change_profile() function
except it specifies that the profile transition should take place on the
next exec instead of immediately. The delayed profile change takes
precedence over any exec transition rules within the confining profile.
Delaying the profile boundary has a couple of advantages: it removes the
need for stub transition profiles, and the exec boundary is a natural security
Delaying the profile boundary has a couple of advantages, it removes the
need for stub transition profiles and the exec boundary is a natural security
layer where potentially sensitive memory is unmapped.
=head1 RETURN VALUE

View File

@@ -54,7 +54,7 @@ B<typedef struct aa_features aa_features;>
B<int aa_features_new(aa_features **features, int dirfd, const char *path);>
B<int aa_features_new_from_file(aa_features **features, int file);>
B<int aa_features_new_from_file(aa_features **features, int fd);>
B<int aa_features_new_from_string(aa_features **features, const char *string, size_t size);>

View File

@@ -58,9 +58,6 @@ appropriately.
=head1 ERRORS
# podchecker warns about duplicate link targets for EACCES, EBUSY, ENOENT,
# and ENOMEM, but this is a warning that is safe to ignore.
B<aa_is_enabled>
=over 4

View File

@@ -41,7 +41,7 @@ result is an intersection of all profiles which are stacked. Stacking profiles
together is desirable when wanting to ensure that confinement will never become
more permissive. When changing between two profiles, as performed with
aa_change_profile(2), there is always the possibility that the new profile is
more permissive than the old profile, but that possibility is eliminated when
more permissive than the old profile but that possibility is eliminated when
using aa_stack_profile().
To stack a profile with the current confinement context, a task can use the
@@ -68,7 +68,7 @@ The aa_stack_onexec() function is like the aa_stack_profile() function
except it specifies that the stacking should take place on the next exec
instead of immediately. The delayed profile change takes precedence over any
exec transition rules within the confining profile. Delaying the stacking
boundary has a couple of advantages: it removes the need for stub transition
boundary has a couple of advantages, it removes the need for stub transition
profiles and the exec boundary is a natural security layer where potentially
sensitive memory is unmapped.

View File

@@ -19,10 +19,6 @@
#ifndef __LIBAALOGPARSE_H_
#define __LIBAALOGPARSE_H_
#ifdef __cplusplus
extern "C" {
#endif
#define AA_RECORD_EXEC_MMAP 1
#define AA_RECORD_READ 2
#define AA_RECORD_WRITE 4
@@ -30,10 +26,10 @@ extern "C" {
#define AA_RECORD_LINK 16
/**
* Enum representing which syntax version the log entry used.
* Support for V1 parsing was completely removed in 2011 and that enum entry
* is only still there for API compatibility reasons.
* This is just for convenience now that we have two
* wildly different grammars.
*/
typedef enum
{
AA_RECORD_SYNTAX_V1,
@@ -52,23 +48,70 @@ typedef enum
AA_RECORD_STATUS /* Configuration change */
} aa_record_event_type;
/*
* Use this preprocessor dance to maintain backcompat for field names
* This will break C code that used the C++ reserved keywords "namespace"
* and "class" as identifiers, but this is bad practice anyways, and we
* hope that we are the only ones in a given C file that messed up this way
/**
* With the sole exception of active_hat, this is a 1:1
* mapping from the keys that the new syntax uses.
*
* TODO: document this in a man page for aalogparse?
*/
#if defined(SWIG) && defined(__cplusplus)
#error "SWIG and __cplusplus are defined together"
#elif !defined(SWIG) && !defined(__cplusplus)
/* Use SWIG's %rename feature to preserve backcompat */
#define class rule_class
#define namespace aa_namespace
#endif
* Some examples of the old syntax and how they're mapped with the aa_log_record struct:
*
* "PERMITTING r access to /path (program_name(12345) profile /profile active hat)"
* - operation: access
* - requested_mask: r
* - pid: 12345
* - profile: /profile
* - name: /path
* - info: program_name
* - active_hat: hat
*
* "REJECTING mkdir on /path/to/something (bash(23415) profile /bin/freak-aa-out active /bin/freak-aa-out"
* - operation: mkdir
* - name: /path/to/something
* - info: bash
* - pid: 23415
* - profile: /bin/freak-aa-out
* - active_hat: /bin/freak-aa-out
*
* "REJECTING xattr set on /path/to/something (bash(23415) profile /bin/freak-aa-out active /bin/freak-aa-out)"
* - operation: xattr
* - attribute: set
* - name: /path/to/something
* - info: bash
* - pid: 23415
* - profile: /bin/freak-aa-out
* - active_hat: /bin/freak-aa-out
*
* "PERMITTING attribute (something) change to /else (bash(23415) profile /bin/freak-aa-out active /bin/freak-aa-out)"
* - operation: setattr
* - attribute: something
* - name: /else
* - info: bash
* - pid: 23415
* - profile: /bin/freak-aa-out
* - active_hat: /bin/freak-aa-out
*
* "PERMITTING access to capability 'cap' (bash(23415) profile /bin/freak-aa-out active /bin/freak-aa-out)"
* - operation: capability
* - name: cap
* - info: bash
* - pid: 23415
* - profile: /bin/freak-aa-out
* - active_hat: /bin/freak-aa-out
*
* "LOGPROF-HINT unknown_hat TESTHAT pid=27764 profile=/change_hat_test/test_hat active=/change_hat_test/test_hat"
* - operation: change_hat
* - name: TESTHAT
* - info: unknown_hat
* - pid: 27764
* - profile: /change_hat_test/test_hat
* - active_hat: /change_hat_test/test_hat
*
* "LOGPROF-HINT fork pid=27764 child=38229"
* - operation: clone
* - task: 38229
* - pid: 27764
**/
typedef struct aa_log_record
typedef struct
{
aa_record_syntax_version version;
aa_record_event_type event; /* Event type */
@@ -91,7 +134,7 @@ typedef struct aa_log_record
char *comm; /* Command that triggered msg */
char *name;
char *name2;
char *aa_namespace;
char *namespace;
char *attribute;
unsigned long parent;
char *info;
@@ -118,7 +161,7 @@ typedef struct aa_log_record
char *flags;
char *src_name;
char *rule_class;
char *class;
char *net_addr;
char *peer_addr;
@@ -133,7 +176,7 @@ typedef struct aa_log_record
* @return Parsed data.
*/
aa_log_record *
parse_record(const char *str);
parse_record(char *str);
/**
* Frees all struct data.
@@ -142,9 +185,5 @@ parse_record(const char *str);
void
free_record(aa_log_record *record);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -105,8 +105,8 @@ extern int aa_getpeercon(int fd, char **label, char **mode);
#define AA_QUERY_CMD_LABEL "label"
#define AA_QUERY_CMD_LABEL_SIZE sizeof(AA_QUERY_CMD_LABEL)
extern int aa_query_label(uint32_t mask, char *query, size_t size, int *allowed,
int *audited);
extern int aa_query_label(uint32_t mask, char *query, size_t size, int *allow,
int *audit);
extern int aa_query_file_path_len(uint32_t mask, const char *label,
size_t label_len, const char *path,
size_t path_len, int *allowed, int *audited);

View File

@@ -44,7 +44,7 @@ include $(COMMONDIR)/Make.rules
BUILT_SOURCES = grammar.h scanner.h af_protos.h
AM_LFLAGS = -v
AM_YFLAGS = -Wno-yacc -d -p aalogparse_
AM_YFLAGS = -d -p aalogparse_
AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir)/include/
scanner.h: scanner.l
$(LEX) -v $<
@@ -73,16 +73,6 @@ CLEANFILES = libapparmor.pc
tst_aalogmisc_SOURCES = tst_aalogmisc.c
tst_aalogmisc_LDADD = .libs/libapparmor.a
tst_aalogparse_cpp_SOURCES = tst_aalogparse_cpp.cpp
tst_aalogparse_cpp_LDADD = .libs/libapparmor.a
tst_aalogparse_oldname_SOURCES = tst_aalogparse_oldname.c
tst_aalogparse_oldname_LDADD = .libs/libapparmor.a
tst_aalogparse_reentrancy_SOURCES = tst_aalogparse_reentrancy.c
tst_aalogparse_reentrancy_LDADD = .libs/libapparmor.a
tst_aalogparse_reentrancy_LDFLAGS = -pthread
tst_features_SOURCES = tst_features.c
tst_features_LDADD = .libs/libapparmor.a
@@ -90,7 +80,7 @@ tst_kernel_SOURCES = tst_kernel.c
tst_kernel_LDADD = .libs/libapparmor.a
tst_kernel_LDFLAGS = -pthread
check_PROGRAMS = tst_aalogmisc tst_aalogparse_cpp tst_aalogparse_reentrancy tst_aalogparse_oldname tst_features tst_kernel
check_PROGRAMS = tst_aalogmisc tst_features tst_kernel
TESTS = $(check_PROGRAMS)
.PHONY: check-local

View File

@@ -15,15 +15,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* aalogparse_error now requires visibility of the aa_log_record type
* Also include in a %code requires block to add it to the header
*/
%code requires{
#include <aalogparse.h>
}
%{
/* set the following to non-zero to get bison to emit debugging
* information about tokens given and rules matched.
* Also:
* Uncomment the %defines
* parse.error
* parse.trace
*/
#define YYDEBUG 0
#include <string.h>
#include <aalogparse.h>
#include "parser.h"
@@ -39,10 +41,12 @@
#define debug_unused_ unused_
#endif
aa_log_record *ret_record;
/* Since we're a library, on any errors we don't want to print out any
* error messages. We should probably add a debug interface that does
* emit messages when asked for. */
void aalogparse_error(unused_ void *scanner, aa_log_record *ret_record, debug_unused_ char const *s)
void aalogparse_error(unused_ void *scanner, debug_unused_ char const *s)
{
#if (YYDEBUG != 0)
printf("ERROR: %s\n", s);
@@ -85,10 +89,9 @@ aa_record_event_type lookup_aa_event(unsigned int type)
%define parse.trace
*/
%define api.pure full
%define api.pure
%lex-param{void *scanner}
%parse-param{void *scanner}
%parse-param{aa_log_record *ret_record}
%union
{
@@ -281,9 +284,8 @@ audit_user_msg: TOK_KEY_MSG TOK_EQUALS audit_id audit_user_msg_tail
audit_id: TOK_AUDIT TOK_OPEN_PAREN TOK_AUDIT_DIGITS TOK_PERIOD TOK_AUDIT_DIGITS TOK_COLON TOK_AUDIT_DIGITS TOK_CLOSE_PAREN TOK_COLON
{
if (!asprintf(&ret_record->audit_id, "%s.%s:%s", $3, $5, $7)) {
yyerror(scanner, ret_record, YY_("Out of memory"));
}
if (!asprintf(&ret_record->audit_id, "%s.%s:%s", $3, $5, $7))
yyerror(scanner, YY_("Out of memory"));
ret_record->epoch = atol($3);
ret_record->audit_sub_id = atoi($7);
free($3);
@@ -306,7 +308,7 @@ key: TOK_KEY_OPERATION TOK_EQUALS TOK_QUOTED_STRING
| TOK_KEY_NAME TOK_EQUALS safe_string
{ ret_record->name = $3;}
| TOK_KEY_NAMESPACE TOK_EQUALS safe_string
{ ret_record->aa_namespace = $3;}
{ ret_record->namespace = $3;}
| TOK_KEY_NAME2 TOK_EQUALS safe_string
{ ret_record->name2 = $3;}
| TOK_KEY_MASK TOK_EQUALS TOK_QUOTED_STRING
@@ -438,7 +440,7 @@ key: TOK_KEY_OPERATION TOK_EQUALS TOK_QUOTED_STRING
ret_record->info = $1;
}
| TOK_KEY_CLASS TOK_EQUALS TOK_QUOTED_STRING
{ ret_record->rule_class = $3; }
{ ret_record->class = $3; }
;
apparmor_event:
@@ -475,3 +477,31 @@ protocol: TOK_QUOTED_STRING
}
;
%%
aa_log_record *
_parse_yacc(char *str)
{
/* yydebug = 1; */
YY_BUFFER_STATE lex_buf;
yyscan_t scanner;
ret_record = NULL;
ret_record = malloc(sizeof(aa_log_record));
_init_log_record(ret_record);
if (ret_record == NULL)
return NULL;
#if (YYDEBUG != 0)
yydebug = 1;
#endif
aalogparse_lex_init(&scanner);
lex_buf = aalogparse__scan_string(str, scanner);
/* Ignore return value to return an AA_RECORD_INVALID event */
(void)aalogparse_parse(scanner);
aalogparse__delete_buffer(lex_buf, scanner);
aalogparse_lex_destroy(scanner);
return ret_record;
}

View File

@@ -34,42 +34,13 @@
#include <aalogparse.h>
#include "parser.h"
#include "grammar.h"
#include "scanner.h"
/* This is mostly just a wrapper around the code in grammar.y */
aa_log_record *parse_record(const char *str)
aa_log_record *parse_record(char *str)
{
YY_BUFFER_STATE lex_buf;
yyscan_t scanner;
aa_log_record *ret_record;
if (str == NULL)
return NULL;
ret_record = malloc(sizeof(aa_log_record));
_init_log_record(ret_record);
if (ret_record == NULL)
return NULL;
struct string_buf string_buf = {.buf = NULL, .buf_len = 0, .buf_alloc = 0};
#if (YYDEBUG != 0)
/* Warning: this is still a global even in reentrant parsers */
aalogparse_debug = 1;
#endif
aalogparse_lex_init_extra(&string_buf, &scanner);
lex_buf = aalogparse__scan_string(str, scanner);
/* Ignore return value to return an AA_RECORD_INVALID event */
(void)aalogparse_parse(scanner, ret_record);
aalogparse__delete_buffer(lex_buf, scanner);
aalogparse_lex_destroy(scanner);
/* free(NULL) is a no-op */
free(string_buf.buf);
return ret_record;
return _parse_yacc(str);
}
void free_record(aa_log_record *record)
@@ -92,8 +63,8 @@ void free_record(aa_log_record *record)
free(record->name);
if (record->name2 != NULL)
free(record->name2);
if (record->aa_namespace != NULL)
free(record->aa_namespace);
if (record->namespace != NULL)
free(record->namespace);
if (record->attribute != NULL)
free(record->attribute);
if (record->info != NULL)
@@ -139,8 +110,8 @@ void free_record(aa_log_record *record)
if (record->execpath != NULL)
free(record->execpath);
if (record->rule_class != NULL)
free(record->rule_class);
if (record->class != NULL)
free(record->class);
free(record);
}

View File

@@ -19,14 +19,8 @@
#ifndef __AA_LOG_PARSER_H__
#define __AA_LOG_PARSER_H__
// Internal-only type
struct string_buf {
char *buf;
unsigned int buf_len;
unsigned int buf_alloc;
};
extern void _init_log_record(aa_log_record *record);
extern aa_log_record *_parse_yacc(char *str);
extern char *hex_to_string(char *str);
extern char *ipproto_to_string(unsigned int proto);

View File

@@ -19,7 +19,6 @@
%option nounput
%option noyy_top_state
%option reentrant
%option extra-type="struct string_buf*"
%option prefix="aalogparse_"
%option bison-bridge
%option header-file="scanner.h"
@@ -35,37 +34,40 @@
#define YY_NO_INPUT
void string_buf_reset(struct string_buf* char_buf)
unsigned int string_buf_alloc = 0;
unsigned int string_buf_len = 0;
char *string_buf = NULL;
void string_buf_reset()
{
/* rewind buffer to zero, possibly doing initial allocation too */
char_buf->buf_len = 0;
if (char_buf->buf == NULL) {
char_buf->buf_alloc = 128;
char_buf->buf = malloc(char_buf->buf_alloc);
assert(char_buf->buf != NULL);
string_buf_len = 0;
if (string_buf == NULL) {
string_buf_alloc = 128;
string_buf = malloc(string_buf_alloc);
assert(string_buf != NULL);
}
/* always start with a valid but empty string */
char_buf->buf[0] = '\0';
string_buf[0] = '\0';
}
void string_buf_append(struct string_buf* char_buf, unsigned int length, char *text)
void string_buf_append(unsigned int length, char *text)
{
unsigned int current_length = char_buf->buf_len;
unsigned int current_length = string_buf_len;
/* handle calling ..._append before ..._reset */
if (char_buf->buf == NULL) string_buf_reset(char_buf);
if (string_buf == NULL) string_buf_reset();
char_buf->buf_len += length;
string_buf_len += length;
/* expand allocation if this append would exceed the allocation */
while (char_buf->buf_len >= char_buf->buf_alloc) {
// TODO: overflow?
char_buf->buf_alloc *= 2;
char_buf->buf = realloc(char_buf->buf, char_buf->buf_alloc);
assert(char_buf->buf != NULL);
while (string_buf_len >= string_buf_alloc) {
string_buf_alloc *= 2;
string_buf = realloc(string_buf, string_buf_alloc);
assert(string_buf != NULL);
}
/* copy and unconditionally terminate */
memcpy(char_buf->buf+current_length, text, length);
char_buf->buf[char_buf->buf_len] = '\0';
memcpy(string_buf+current_length, text, length);
string_buf[string_buf_len] = '\0';
}
%}
@@ -230,7 +232,7 @@ yy_flex_debug = 0;
{open_paren} { return(TOK_OPEN_PAREN); }
{close_paren} { BEGIN(INITIAL); return(TOK_CLOSE_PAREN); }
{ws} { }
\" { string_buf_reset(yyextra); BEGIN(quoted_string); }
\" { string_buf_reset(); BEGIN(quoted_string); }
{ID}+ {
yylval->t_str = strdup(yytext);
BEGIN(INITIAL);
@@ -239,20 +241,20 @@ yy_flex_debug = 0;
{equals} { return(TOK_EQUALS); }
}
\" { string_buf_reset(yyextra); BEGIN(quoted_string); }
\" { string_buf_reset(); BEGIN(quoted_string); }
<quoted_string>\" { /* End of the quoted string */
BEGIN(INITIAL);
yylval->t_str = strdup(yyextra->buf);
yylval->t_str = strdup(string_buf);
return(TOK_QUOTED_STRING);
}
<quoted_string>\\(.|\n) { string_buf_append(yyextra, 1, &yytext[1]); }
<quoted_string>\\(.|\n) { string_buf_append(1, &yytext[1]); }
<quoted_string>[^\\\n\"]+ { string_buf_append(yyextra, yyleng, yytext); }
<quoted_string>[^\\\n\"]+ { string_buf_append(yyleng, yytext); }
<safe_string>{
\" { string_buf_reset(yyextra); BEGIN(quoted_string); }
\" { string_buf_reset(); BEGIN(quoted_string); }
{hexstring} { yylval->t_str = hex_to_string(yytext); BEGIN(INITIAL); return(TOK_HEXSTRING);}
{equals} { return(TOK_EQUALS); }
. { /* eek, error! try another state */ BEGIN(INITIAL); yyless(0); }

View File

@@ -1,20 +0,0 @@
#include <aalogparse.h>
#include <string.h>
#include "private.h"
const char* log_line = "[23342.075380] audit: type=1400 audit(1725487203.971:1831): apparmor=\"DENIED\" operation=\"open\" class=\"file\" profile=\"snap-update-ns.firmware-updater\" name=\"/proc/202964/maps\" pid=202964 comm=\"5\" requested_mask=\"r\" denied_mask=\"r\" fsuid=1000 ouid=0";
int main(void) {
int rc = 0;
/* Very basic test to ensure we can do aalogparse stuff in C++ */
aa_log_record *record = parse_record(log_line);
MY_TEST(record != NULL, "Log failed to parse");
MY_TEST(record->version == AA_RECORD_SYNTAX_V2, "Log should have parsed as v2 form");
MY_TEST(record->aa_namespace == NULL, "Log should have NULL namespace");
MY_TEST((record->rule_class != NULL) && (strcmp(record->rule_class, "file") == 0), "Log should have file class");
free_record(record);
return rc;
}

View File

@@ -1,20 +0,0 @@
#include <aalogparse.h>
#include <string.h>
#include "private.h"
const char* log_line = "[23342.075380] audit: type=1400 audit(1725487203.971:1831): apparmor=\"DENIED\" operation=\"open\" class=\"file\" profile=\"snap-update-ns.firmware-updater\" name=\"/proc/202964/maps\" pid=202964 comm=\"5\" requested_mask=\"r\" denied_mask=\"r\" fsuid=1000 ouid=0";
int main(void) {
int rc = 0;
/* Very basic test to ensure we can use the C++-incompatible field names */
aa_log_record *record = parse_record(log_line);
MY_TEST(record != NULL, "Log failed to parse");
MY_TEST(record->version == AA_RECORD_SYNTAX_V2, "Log should have parsed as v2 form");
MY_TEST(record->namespace == NULL, "Log should have NULL namespace");
MY_TEST((record->class != NULL) && (strcmp(record->class, "file") == 0), "Log should have file class");
free_record(record);
return rc;
}

View File

@@ -1,154 +0,0 @@
#include <pthread.h>
#include <string.h>
#include <aalogparse.h>
#include "private.h"
const char* log_line = "[23342.075380] audit: type=1400 audit(1725487203.971:1831): apparmor=\"DENIED\" operation=\"open\" class=\"file\" profile=\"snap-update-ns.firmware-updater\" name=\"/proc/202964/maps\" pid=202964 comm=\"5\" requested_mask=\"r\" denied_mask=\"r\" fsuid=1000 ouid=0";
const char* log_line_2 = "[ 4074.372559] audit: type=1400 audit(1725553393.143:793): apparmor=\"DENIED\" operation=\"capable\" class=\"cap\" profile=\"/usr/lib/snapd/snap-confine\" pid=19034 comm=\"snap-confine\" capability=12 capname=\"net_admin\"";
static int pthread_barrier_ok(int barrier_result) {
return barrier_result == 0 || barrier_result == PTHREAD_BARRIER_SERIAL_THREAD;
}
static int nullcmp_and_strcmp(const void *s1, const void *s2)
{
/* Return 0 if both pointers are NULL & non-zero if only one is NULL */
if (!s1 || !s2)
return s1 != s2;
return strcmp(s1, s2);
}
int aa_log_record_eq(aa_log_record *record1, aa_log_record *record2) {
int are_eq = 1;
are_eq &= (record1->version == record2->version);
are_eq &= (record1->event == record2->event);
are_eq &= (record1->pid == record2->pid);
are_eq &= (record1->peer_pid == record2->peer_pid);
are_eq &= (record1->task == record2->task);
are_eq &= (record1->magic_token == record2->magic_token);
are_eq &= (record1->epoch == record2->epoch);
are_eq &= (record1->audit_sub_id == record2->audit_sub_id);
are_eq &= (record1->bitmask == record2->bitmask);
are_eq &= (nullcmp_and_strcmp(record1->audit_id, record2->audit_id) == 0);
are_eq &= (nullcmp_and_strcmp(record1->operation, record2->operation) == 0);
are_eq &= (nullcmp_and_strcmp(record1->denied_mask, record2->denied_mask) == 0);
are_eq &= (nullcmp_and_strcmp(record1->requested_mask, record2->requested_mask) == 0);
are_eq &= (record1->fsuid == record2->fsuid);
are_eq &= (record1->ouid == record2->ouid);
are_eq &= (nullcmp_and_strcmp(record1->profile, record2->profile) == 0);
are_eq &= (nullcmp_and_strcmp(record1->peer_profile, record2->peer_profile) == 0);
are_eq &= (nullcmp_and_strcmp(record1->comm, record2->comm) == 0);
are_eq &= (nullcmp_and_strcmp(record1->name, record2->name) == 0);
are_eq &= (nullcmp_and_strcmp(record1->name2, record2->name2) == 0);
are_eq &= (nullcmp_and_strcmp(record1->namespace, record2->namespace) == 0);
are_eq &= (nullcmp_and_strcmp(record1->attribute, record2->attribute) == 0);
are_eq &= (record1->parent == record2->parent);
are_eq &= (nullcmp_and_strcmp(record1->info, record2->info) == 0);
are_eq &= (nullcmp_and_strcmp(record1->peer_info, record2->peer_info) == 0);
are_eq &= (record1->error_code == record2->error_code);
are_eq &= (nullcmp_and_strcmp(record1->active_hat, record2->active_hat) == 0);
are_eq &= (nullcmp_and_strcmp(record1->net_family, record2->net_family) == 0);
are_eq &= (nullcmp_and_strcmp(record1->net_protocol, record2->net_protocol) == 0);
are_eq &= (nullcmp_and_strcmp(record1->net_sock_type, record2->net_sock_type) == 0);
are_eq &= (nullcmp_and_strcmp(record1->net_local_addr, record2->net_local_addr) == 0);
are_eq &= (record1->net_local_port == record2->net_local_port);
are_eq &= (nullcmp_and_strcmp(record1->net_foreign_addr, record2->net_foreign_addr) == 0);
are_eq &= (record1->net_foreign_port == record2->net_foreign_port);
are_eq &= (nullcmp_and_strcmp(record1->execpath, record2->execpath) == 0);
are_eq &= (nullcmp_and_strcmp(record1->dbus_bus, record2->dbus_bus) == 0);
are_eq &= (nullcmp_and_strcmp(record1->dbus_path, record2->dbus_path) == 0);
are_eq &= (nullcmp_and_strcmp(record1->dbus_interface, record2->dbus_interface) == 0);
are_eq &= (nullcmp_and_strcmp(record1->dbus_member, record2->dbus_member) == 0);
are_eq &= (nullcmp_and_strcmp(record1->signal, record2->signal) == 0);
are_eq &= (nullcmp_and_strcmp(record1->peer, record2->peer) == 0);
are_eq &= (nullcmp_and_strcmp(record1->fs_type, record2->fs_type) == 0);
are_eq &= (nullcmp_and_strcmp(record1->flags, record2->flags) == 0);
are_eq &= (nullcmp_and_strcmp(record1->src_name, record2->src_name) == 0);
are_eq &= (nullcmp_and_strcmp(record1->class, record2->class) == 0);
are_eq &= (nullcmp_and_strcmp(record1->net_addr, record2->net_addr) == 0);
are_eq &= (nullcmp_and_strcmp(record1->peer_addr, record2->peer_addr) == 0);
return are_eq;
}
typedef struct {
const char* log;
pthread_barrier_t *barrier;
} pthread_parse_args;
void* pthread_parse_log(void* args) {
pthread_parse_args *args_real = (pthread_parse_args *) args;
int barrier_wait_result = pthread_barrier_wait(args_real->barrier);
/* Return NULL and fail test if barrier wait fails */
if (!pthread_barrier_ok(barrier_wait_result)) {
return NULL;
}
aa_log_record *record = parse_record(args_real->log);
return (void*) record;
}
#define NUM_THREADS 16
int main(void) {
pthread_t thread_ids[NUM_THREADS];
pthread_barrier_t barrier;
int barrier_wait_result;
aa_log_record* parsed_logs[NUM_THREADS];
int rc = 0;
/* Set up arguments to be passed to threads */
pthread_parse_args args = {.log=log_line, .barrier=&barrier};
pthread_parse_args args2 = {.log=log_line_2, .barrier=&barrier};
MY_TEST(NUM_THREADS > 2, "Test requires more than 2 threads");
/* Use barrier to synchronize the start of log parsing among all the threads
* This increases the likelihood of tickling race conditions, if there are any
*/
MY_TEST(pthread_barrier_init(&barrier, NULL, NUM_THREADS+1) == 0,
"Could not init pthread barrier");
for (int i=0; i<NUM_THREADS; i++) {
if (i%2 == 0) {
pthread_create(&thread_ids[i], NULL, pthread_parse_log, (void *) &args);
} else {
pthread_create(&thread_ids[i], NULL, pthread_parse_log, (void *) &args2);
}
}
/* Final barrier_wait to set off the thread race */
barrier_wait_result = pthread_barrier_wait(&barrier);
MY_TEST(pthread_barrier_ok(barrier_wait_result), "Could not wait on pthread barrier");
/* Wait for threads to finish parsing the logs */
for (int i=0; i<NUM_THREADS; i++) {
MY_TEST(pthread_join(thread_ids[i], (void*) &parsed_logs[i]) == 0, "Could not join thread");
}
/* Check that all logs parsed and are equal */
for (int i=0; i<NUM_THREADS; i++) {
MY_TEST(parsed_logs[i] != NULL, "Log failed to parse");
MY_TEST(parsed_logs[i]->version == AA_RECORD_SYNTAX_V2, "Log should have parsed as v2 form");
MY_TEST(parsed_logs[i]->event == AA_RECORD_DENIED, "Log should have parsed as denied");
/* Also check i==0 and i==1 as a sanity check for aa_log_record_eq */
if (i%2 == 0) {
MY_TEST(aa_log_record_eq(parsed_logs[0], parsed_logs[i]), "Log 0 != Log even");
} else {
MY_TEST(aa_log_record_eq(parsed_logs[1], parsed_logs[i]), "Log 1 != Log odd");
}
}
MY_TEST(!aa_log_record_eq(parsed_logs[0], parsed_logs[1]), "Log 0 and log 1 shouldn't be equal");
/* Clean up */
MY_TEST(pthread_barrier_destroy(&barrier) == 0, "Could not destroy pthread barrier");
for (int i=0; i<NUM_THREADS; i++) {
free_record(parsed_logs[i]);
}
return rc;
}

View File

@@ -1,3 +1,3 @@
SUBDIRS = perl python ruby
EXTRA_DIST = SWIG/*.i
EXTRA_DIST = SWIG/*.i java/Makefile.am

View File

@@ -5,98 +5,9 @@
#include <sys/apparmor.h>
#include <sys/apparmor_private.h>
// Include static_assert if the C compiler supports it
// static_assert standardized since C11, assert.h not needed since C23
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && __STDC_VERSION__ < 202311L
#include <assert.h>
#endif
%}
%include "typemaps.i"
%include <cstring.i>
%include <stdint.i>
%include <exception.i>
/*
* SWIG 4.3 included https://github.com/swig/swig/pull/2907 to distinguish
* between Py_None being returned as a default void and Py_None being returned
* as the equivalent of C NULL. Unfortunately, this turns into an API breaking
* change with our use of %append_output when we want the Python function to
* return something even when the C function has a void return type. Thus, we
* need an additional macro to smooth over the differences. Include all affected
* languages, even ones we don't build bindings for, for completeness.
*/
#if SWIG_VERSION >= 0x040300
#ifdef SWIGPYTHON
#define ISVOID_APPEND_OUTPUT(value) {$result = SWIG_Python_AppendOutput($result, value, 1);}
#elif defined(SWIGRUBY)
#define ISVOID_APPEND_OUTPUT(value) {$result = SWIG_Ruby_AppendOutput($result, value, 1);}
#elif defined(SWIGPHP)
#define ISVOID_APPEND_OUTPUT(value) {$result = SWIG_Php_AppendOutput($result, value, 1);}
#else
#define ISVOID_APPEND_OUTPUT(value) %append_output(value)
#endif
#else
#define ISVOID_APPEND_OUTPUT(value) %append_output(value)
#endif
%newobject parse_record;
%delobject free_record;
/*
* Despite its name, %delobject does not hook up destructors to language
* deletion mechanisms. Instead, it sets flags so that manually calling the
* free function and then deleting by language mechanisms doesn't cause a
* double-free.
*
* Additionally, we can manually extend the struct with a C++-like
* destructor. This ensures that the record struct is freed
* automatically when the high-level object goes out of scope.
*/
%extend aa_log_record {
~aa_log_record() {
free_record($self);
}
}
/*
* Generate a no-op free_record wrapper to avoid making a double-free footgun.
* Use rename directive to avoid colliding with the actual free_record, which
* we use above to clean up when the higher-level language deletes the object.
*
* Ideally we would not expose a free_record at all, but we need to maintain
* backwards compatibility with the existing high-level code that uses it.
*/
%rename(free_record) noop_free_record;
#ifdef SWIGPYTHON
%pythonprepend noop_free_record %{
import warnings
warnings.warn("free_record is now a no-op as the record's memory is handled automatically", DeprecationWarning)
%}
#endif
%feature("autodoc",
"This function used to free aa_log_record objects. Freeing is now handled "
"automatically, so this no-op function remains for backwards compatibility.") noop_free_record;
%inline %{
void noop_free_record(aa_log_record *record) {(void) record;}
%}
/*
* Do not autogenerate a wrapper around free_record. This does not prevent us
* from calling it ourselves in %extend C code.
*/
%ignore free_record;
/*
* Map names to preserve backwards compatibility
*/
#ifdef SWIGPYTHON
%rename("_class") aa_log_record::rule_class;
#else
%rename("class") aa_log_record::rule_class;
#endif
%rename("namespace") aa_log_record::aa_namespace;
%include <aalogparse.h>
/**
@@ -110,75 +21,18 @@ warnings.warn("free_record is now a no-op as the record's memory is handled auto
/* apparmor.h */
/*
* label is a heap-allocated pointer, but when label and mode occur together,
* the freeing of label must be deferred because mode points into label.
*
* %cstring_output_allocate((char **label, char **mode), free(*$1))
* does not handle multi-argument typemaps correctly, so we write our own
* typemap based on it instead.
*/
%typemap(in,noblock=1,numinputs=0) (char **label, char **mode) ($*1_ltype temp_label = 0, $*2_ltype temp_mode = 0) {
$1 = &temp_label;
$2 = &temp_mode;
}
%typemap(freearg,match="in") (char **label, char **mode) ""
%typemap(argout,noblock=1,fragment="SWIG_FromCharPtr") (char **label, char **mode) {
ISVOID_APPEND_OUTPUT(SWIG_FromCharPtr(*$1));
ISVOID_APPEND_OUTPUT(SWIG_FromCharPtr(*$2));
free(*$1);
}
/*
* mode also occurs in combination with con in aa_splitcon
* typemap based on %cstring_mutable but with substantial modifications
*/
%typemap(in,numinputs=1,fragment="SWIG_AsCharPtrAndSize") (char *con, char **mode) ($*2_ltype temp_mode = 0) {
int alloc_status = 0;
$1_ltype con_ptr = NULL;
size_t con_len = 0;
int char_ptr_res = SWIG_AsCharPtrAndSize($input, &con_ptr, &con_len, &alloc_status);
if (!SWIG_IsOK(char_ptr_res)) {
%argument_fail(char_ptr_res, "char *con", $symname, $argnum);
}
if (alloc_status != SWIG_NEWOBJ) {
// Unconditionally copy because the C function modifies the string in place
$1 = %new_copy_array(con_ptr, con_len+1, char);
} else {
$1 = con_ptr;
}
$2 = &temp_mode;
}
%typemap(freearg,noblock=1,match="in") (char *con, char **mode) {
%delete_array($1);
}
%typemap(argout,noblock=1,fragment="SWIG_FromCharPtr") (char *con, char **mode) {
/*
* aa_splitcon returns either con or NULL so we don't need to explicitly
* append it to the output, and we don't need the ISVOID helper here
*
* SWIG_FromCharPtr does NULL checks for us
*/
%append_output(SWIG_FromCharPtr(*$2));
}
%exception aa_splitcon {
$action
if (result == NULL) {
SWIG_exception_fail(SWIG_ValueError, "received invalid confinement context");
}
}
extern char *aa_splitcon(char *con, char **mode);
/* apparmor_private.h */
extern int _aa_is_blacklisted(const char *name);
#ifdef SWIGPYTHON
%exception {
$action
if (result < 0) {
// Unfortunately SWIG_exception does not support OSError
PyErr_SetFromErrno(PyExc_OSError);
SWIG_fail;
return NULL;
}
}
#endif
@@ -187,206 +41,33 @@ extern char *aa_splitcon(char *con, char **mode);
/* apparmor.h */
/*
* aa_is_enabled returns a boolean as an int with failure reason in errno
* Therefore, aa_is_enabled either returns True or throws an exception
*
* Keep that behavior for backwards compatibilty but return a boolean on Python
* where it makes more sense, which isn't a breaking change because a boolean is
* a subclass of int
*/
#ifdef SWIGPYTHON
%typemap(out) int {
$result = PyBool_FromLong($1);
}
#endif
extern int aa_is_enabled(void);
#ifdef SWIGPYTHON
// Based on SWIG's argcargv.i but we don't have an argc
%typemap(in,fragment="SWIG_AsCharPtr") const char *subprofiles[] (Py_ssize_t seq_len=0, int* alloc_tracking = NULL) {
void* arg_as_ptr = NULL;
int res_convertptr = SWIG_ConvertPtr($input, &arg_as_ptr, $descriptor(char*[]), 0);
if (SWIG_IsOK(res_convertptr)) {
$1 = %static_cast(arg_as_ptr, $1_ltype);
} else {
// Clear error that would be set if ptr conversion failed
PyErr_Clear();
int is_list = PyList_Check($input);
if (is_list || PyTuple_Check($input)) {
seq_len = PySequence_Length($input);
/*
* %new_array zero-inits for cleaner error handling and memory cleanup
* %delete_array(NULL) is no-op (either free or delete), and
* alloc_tracking of 0 is uninit
*
* Further note: SWIG_exception_fail jumps to the freearg typemap
*/
$1 = %new_array(seq_len+1, char *);
if ($1 == NULL) {
SWIG_exception_fail(SWIG_MemoryError, "could not allocate C subprofiles");
}
alloc_tracking = %new_array(seq_len, int);
if (alloc_tracking == NULL) {
SWIG_exception_fail(SWIG_MemoryError, "could not allocate C alloc track arr");
}
for (Py_ssize_t i=0; i<seq_len; i++) {
PyObject *o = is_list ? PyList_GetItem($input, i) : PyTuple_GetItem($input, i);
if (o == NULL) {
// Failed to get item-Python already set exception info
SWIG_fail;
} else if (o == Py_None) {
// SWIG_AsCharPtr(Py_None, ...) succeeds with ptr output being NULL
SWIG_exception_fail(SWIG_ValueError, "sequence contains a None object");
}
int res = SWIG_AsCharPtr(o, &$1[i], &alloc_tracking[i]);
if (!SWIG_IsOK(res)) {
// Could emit idx of error here, maybe?
SWIG_exception_fail(SWIG_ArgError(res), "sequence does not contain all strings");
}
}
} else {
SWIG_exception_fail(SWIG_TypeError, "subprofiles is not a list or tuple");
}
}
}
%typemap(freearg,noblock=1) const char *subprofiles[] {
/*
* If static_assert is present, use it to verify the assumption that
* allocation uninitialized (0) != SWIG_NEWOBJ
*/
%#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
static_assert(SWIG_NEWOBJ != 0);
%#endif
if ($1 != NULL && alloc_tracking$argnum != NULL) {
for (Py_ssize_t i=0; i<seq_len$argnum; i++) {
if (alloc_tracking$argnum[i] == SWIG_NEWOBJ) {
%delete_array($1[i]);
}
}
}
%delete_array(alloc_tracking$argnum);
%delete_array($1);
}
#endif
/* These should not receive the VOID_Object typemap */
extern int aa_find_mountpoint(char **mnt);
extern int aa_change_hat(const char *subprofile, unsigned long magic_token);
extern int aa_change_profile(const char *profile);
extern int aa_change_onexec(const char *profile);
extern int aa_change_hatv(const char *subprofiles[], unsigned long token);
extern int aa_change_hat_vargs(unsigned long token, int count, ...);
extern int aa_stack_profile(const char *profile);
extern int aa_stack_onexec(const char *profile);
/*
* aa_find_mountpoint mnt is an output pointer to a heap-allocated string
*
* This is a replica of %cstring_output_allocate(char **mnt, free(*$1))
* that uses the ISVOID helper to work correctly on SWIG 4.3 or later.
*/
%typemap(in,noblock=1,numinputs=0) (char **mnt) ($*1_ltype temp_mnt = 0) {
$1 = &temp_mnt;
}
%typemap(freearg,match="in") (char **mnt) ""
%typemap(argout,noblock=1,fragment="SWIG_FromCharPtr") (char **mnt) {
ISVOID_APPEND_OUTPUT(SWIG_FromCharPtr(*$1));
free(*$1);
}
/* The other errno-based functions should not always be returning the int value:
* - Python exceptions signal success/failure status instead via the %exception
* handler above.
* - Perl (the other binding) has $! for accessing errno but would check the int
* return status first.
*
* The generated C code for (out) resets the return value to None
* before appending the returned data (argout generated by %cstring stuff)
*/
#ifdef SWIGPYTHON
%typemap(out,noblock=1) int {
#if defined(VOID_Object)
$result = VOID_Object;
#endif
}
#endif
/*
* We can't use "typedef int pid_t" because we still support systems
* with 16-bit PIDs and SWIG can't find sys/types.h
*
* Capture the passed-in value as an intmax_t because pid_t is guaranteed
* to be a signed integer
*/
%typemap(in,noblock=1,fragment="SWIG_AsVal_long") pid_t (int conv_pid, intmax_t pid_large) {
conv_pid = SWIG_AsVal_long($input, &pid_large);
if (!SWIG_IsOK(conv_pid)) {
%argument_fail(conv_pid, "pid_t", $symname, $argnum);
}
/*
* Cast the long to a pid_t and then cast back to check for overflow
* Technically this is implementation-defined behaviour but we should be fine
*/
$1 = (pid_t) pid_large;
if ((intmax_t) $1 != pid_large) {
SWIG_exception_fail(SWIG_OverflowError, "pid_t is too large");
}
}
extern int aa_find_mountpoint(char **mnt);
extern int aa_getprocattr(pid_t tid, const char *attr, char **label, char **mode);
extern int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
char **mode);
extern int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode);
extern int aa_gettaskcon(pid_t target, char **label, char **mode);
extern int aa_getcon(char **label, char **mode);
extern int aa_getpeercon_raw(int fd, char *buf, socklen_t *len, char **mode);
extern int aa_getpeercon(int fd, char **label, char **mode);
/*
* Typemaps for the boolean outputs of the query functions
* Use boolean types for Python and int types elsewhere
*/
#ifdef SWIGPYTHON
// TODO: find a way to deduplicate these
%typemap(in, numinputs=0) int *allowed (int temp) {
$1 = &temp;
}
%typemap(argout) int *allowed {
ISVOID_APPEND_OUTPUT(PyBool_FromLong(*$1));
}
%typemap(in, numinputs=0) int *audited (int temp) {
$1 = &temp;
}
%typemap(argout) int *audited {
ISVOID_APPEND_OUTPUT(PyBool_FromLong(*$1));
}
#else
%apply int *OUTPUT { int *allowed };
%apply int *OUTPUT { int *audited };
#endif
/* Sync this with the apparmor.h */
/* Permission flags for the AA_CLASS_FILE mediation class */
#define AA_MAY_EXEC (1 << 0)
#define AA_MAY_WRITE (1 << 1)
#define AA_MAY_READ (1 << 2)
#define AA_MAY_APPEND (1 << 3)
#define AA_MAY_CREATE (1 << 4)
#define AA_MAY_DELETE (1 << 5)
#define AA_MAY_OPEN (1 << 6)
#define AA_MAY_RENAME (1 << 7)
#define AA_MAY_SETATTR (1 << 8)
#define AA_MAY_GETATTR (1 << 9)
#define AA_MAY_SETCRED (1 << 10)
#define AA_MAY_GETCRED (1 << 11)
#define AA_MAY_CHMOD (1 << 12)
#define AA_MAY_CHOWN (1 << 13)
#define AA_MAY_LOCK 0x8000
#define AA_EXEC_MMAP 0x10000
#define AA_MAY_LINK 0x40000
#define AA_MAY_ONEXEC 0x20000000
#define AA_MAY_CHANGE_PROFILE 0x40000000
extern int aa_query_label(uint32_t mask, char *query, size_t size, int *allow,
int *audit);
extern int aa_query_file_path_len(uint32_t mask, const char *label,
size_t label_len, const char *path,
size_t path_len, int *allowed, int *audited);
extern int aa_query_file_path(uint32_t mask, const char *label,
const char *path, int *allowed, int *audited);
extern int aa_query_link_path_len(const char *label, size_t label_len,
const char *target, size_t target_len,
const char *link, size_t link_len,
int *allowed, int *audited);
extern int aa_query_link_path(const char *label, const char *target,
const char *link, int *allowed, int *audited);

View File

@@ -0,0 +1,21 @@
WRAPPERFILES = apparmorlogparse_wrap.c
BUILT_SOURCES = apparmorlogparse_wrap.c
all-local: apparmorlogparse_wrap.o
$(CC) -module apparmorlogparse_wrap.o -o libaalogparse.so
apparmorlogparse_wrap.o: apparmorlogparse_wrap.c
$(CC) -c apparmorlogparse_wrap.c $(CFLAGS) -I../../src -I/usr/include/classpath -fno-strict-aliasing -o apparmorlogparse_wrap.o
clean-local:
rm -rf org
apparmorlogparse_wrap.c: org/aalogparse ../SWIG/*.i
$(SWIG) -java -I../SWIG -I../../src -outdir org/aalogparse \
-package org.aalogparse -o apparmorlogparse_wrap.c libaalogparse.i
org/aalogparse:
mkdir -p org/aalogparse
EXTRA_DIST = $(BUILT_SOURCES)

View File

@@ -55,100 +55,10 @@ NO_VALUE_MAP = {
'fsuid': int(ctypes.c_ulong(-1).value),
'ouid': int(ctypes.c_ulong(-1).value),
}
class AAPythonBindingsTests(unittest.TestCase):
def setUp(self):
# REPORT ALL THE OUTPUT
self.maxDiff = None
def test_aa_splitcon(self):
AA_SPLITCON_EXPECT = [
("unconfined", "unconfined", None),
("unconfined\n", "unconfined", None),
("/bin/ping (enforce)", "/bin/ping", "enforce"),
("/bin/ping (enforce)\n", "/bin/ping", "enforce"),
("/usr/sbin/rsyslog (complain)", "/usr/sbin/rsyslog", "complain"),
]
for context, expected_label, expected_mode in AA_SPLITCON_EXPECT:
actual_label, actual_mode = libapparmor.aa_splitcon(context)
if expected_label is None:
self.assertIsNone(actual_label)
else:
self.assertIsInstance(actual_label, str)
self.assertEqual(expected_label, actual_label)
if expected_mode is None:
self.assertIsNone(actual_mode)
else:
self.assertIsInstance(actual_mode, str)
self.assertEqual(expected_mode, actual_mode)
with self.assertRaises(ValueError):
libapparmor.aa_splitcon("")
def test_aa_is_enabled(self):
aa_enabled = libapparmor.aa_is_enabled()
self.assertIsInstance(aa_enabled, bool)
@unittest.skipUnless(libapparmor.aa_is_enabled(), "AppArmor is not enabled")
def test_aa_find_mountpoint(self):
mount_point = libapparmor.aa_find_mountpoint()
self.assertIsInstance(mount_point, str)
self.assertGreater(len(mount_point), 0, "mount point should not be empty")
self.assertTrue(os.path.isdir(mount_point))
# TODO: test commented out functions (or at least their prototypes)
# extern int aa_change_profile(const char *profile);
# extern int aa_change_onexec(const char *profile);
@unittest.skipUnless(libapparmor.aa_is_enabled(), "AppArmor is not enabled")
def test_change_hats(self):
# Changing hats will fail because we have no valid hats to change to
# However, we still verify that we get an OSError instead of a TypeError
with self.assertRaises(OSError):
libapparmor.aa_change_hat("nonexistent_profile", 12345678)
with self.assertRaises(OSError):
libapparmor.aa_change_hatv(["nonexistent_1", "nonexistent_2"], 0xabcdef)
libapparmor.aa_change_hatv(("nonexistent_1", "nonexistent_2"), 0xabcdef)
# extern int aa_stack_profile(const char *profile);
# extern int aa_stack_onexec(const char *profile);
# extern int aa_getprocattr(pid_t tid, const char *attr, char **label, char **mode);
# extern int aa_gettaskcon(pid_t target, char **label, char **mode);
@unittest.skipUnless(libapparmor.aa_is_enabled(), "AppArmor is not enabled")
def test_aa_gettaskcon(self):
# Our test harness should be running us as unconfined
# Get our own pid and this should be equivalent to aa_getcon
pid = os.getpid()
label, mode = libapparmor.aa_gettaskcon(pid)
self.assertEqual(label, "unconfined", "aa_gettaskcon label should be unconfined")
self.assertIsNone(mode, "aa_gettaskcon mode should be unconfined")
@unittest.skipUnless(libapparmor.aa_is_enabled(), "AppArmor is not enabled")
def test_aa_getcon(self):
# Our test harness should be running us as unconfined
label, mode = libapparmor.aa_getcon()
self.assertEqual(label, "unconfined", "aa_getcon label should be unconfined")
self.assertIsNone(mode, "aa_getcon mode should be unconfined")
# extern int aa_getpeercon(int fd, char **label, char **mode);
# extern int aa_query_file_path(uint32_t mask, const char *label,
# const char *path, int *allowed, int *audited);
@unittest.skipUnless(libapparmor.aa_is_enabled(), "AppArmor is not enabled")
def test_aa_query_file_path(self):
aa_query_mask = libapparmor.AA_MAY_EXEC | libapparmor.AA_MAY_READ | libapparmor.AA_MAY_WRITE
allowed, audited = libapparmor.aa_query_file_path(aa_query_mask, "unconfined", "/tmp/hello")
self.assertTrue(allowed)
self.assertFalse(audited)
# extern int aa_query_link_path(const char *label, const char *target,
# const char *link, int *allowed, int *audited);
class AALogParsePythonBindingsTests(unittest.TestCase):
def setUp(self):
# REPORT ALL THE OUTPUT
self.maxDiff = None
@@ -208,9 +118,6 @@ class AALogParsePythonBindingsTests(unittest.TestCase):
# FIXME: out files should report log version?
# FIXME: or can we just deprecate v1 logs?
continue
elif key == "thisown":
# SWIG generates this key to track memory allocation
continue
elif key in NO_VALUE_MAP:
if NO_VALUE_MAP[key] == value:
continue
@@ -235,7 +142,7 @@ def main():
def stub_test(self, testname=f):
self._runtest(testname)
stub_test.__doc__ = "test " + f
setattr(AALogParsePythonBindingsTests, 'test_' + f, stub_test)
setattr(AAPythonBindingsTests, 'test_' + f, stub_test)
return unittest.main(verbosity=2)

View File

@@ -107,7 +107,7 @@ int print_results(aa_log_record *record)
print_string("Name", record->name);
print_string("Command", record->comm);
print_string("Name2", record->name2);
print_string("Namespace", record->aa_namespace);
print_string("Namespace", record->namespace);
print_string("Attribute", record->attribute);
print_long("Task", record->task, 0);
print_long("Parent", record->parent, 0);
@@ -142,7 +142,7 @@ int print_results(aa_log_record *record)
print_string("Execpath", record->execpath);
print_string("Class", record->rule_class);
print_string("Class", record->class);
print_long("Epoch", record->epoch, 0);
print_long("Audit subid", (long) record->audit_sub_id, 0);

View File

@@ -1,4 +1,2 @@
/home/cb/bin/hello.sh {
/usr/bin/rm mrix,
}

View File

@@ -1,4 +1,2 @@
/usr/bin/wireshark {
/usr/lib64/wireshark/extcap/androiddump mrix,
}

View File

@@ -1,4 +1,4 @@
/bin/ping {
/bin/ping mrix,
ping2 ix,
}

View File

@@ -1,4 +1,4 @@
/bin/ping {
/bin/ping mrix,
/bin/ping ix,
}

View File

@@ -1,4 +1,4 @@
/bin/ping {
/bin/ping mrix,
/bin/ping ix,
}

View File

@@ -1,4 +0,0 @@
/home/steve/aa-regression-tests/link {
/tmp/sdtest.8236-29816-IN8243/target l,
}

View File

@@ -1 +0,0 @@
2025-01-27T13:01:36.226987+05:30 sec-plucky-amd64 kernel: audit: type=1400 audit(1737963096.225:3240): apparmor="AUDIT" operation="getattr" class="file" profile="/usr/sbin/mosquitto" name="/etc/mosquitto/pwfile" pid=8119 comm="mosquitto" requested_mask="r" fsuid=122 ouid=122

View File

@@ -1,15 +0,0 @@
START
File: testcase36.in
Event type: AA_RECORD_AUDIT
Audit ID: 1737963096.225:3240
Operation: getattr
Mask: r
fsuid: 122
ouid: 122
Profile: /usr/sbin/mosquitto
Name: /etc/mosquitto/pwfile
Command: mosquitto
PID: 8119
Class: file
Epoch: 1737963096
Audit subid: 3240

View File

@@ -1,4 +0,0 @@
/usr/sbin/mosquitto {
/etc/mosquitto/pwfile r,
}

View File

@@ -1,4 +1,3 @@
/tmp/apparmor-2.8.0/tests/regression/apparmor/dbus_service {
dbus send bus=system path=/org/freedesktop/systemd1 interface=org.freedesktop.systemd1.Manager member=LookupDynamicUserByName peer=(label=unconfined),
dbus send bus=system path=/org/freedesktop/systemd1 interface=org.freedesktop.systemd1.Manager member=LookupDynamicUserByName peer=( name=org.freedesktop.systemd1, label=unconfined),
}

View File

@@ -365,9 +365,6 @@ cap_names.h: generated_cap_names.h base_cap_names.h
exit 1; \
fi
.PHONY: tst_binaries
tst_binaries: $(TESTS)
tst_lib: lib.c parser.h $(filter-out lib.o, ${TEST_OBJECTS})
$(CXX) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS) $(TEST_LDLIBS)
tst_%: parser_%.c parser.h $(filter-out parser_%.o, ${TEST_OBJECTS})

View File

@@ -388,7 +388,7 @@ aa_change_hat(2) can take advantage of subprofiles to run under different
confinements, dependent on program logic. Several aa_change_hat(2)-aware
applications exist, including an Apache module, mod_apparmor(5); a PAM
module, pam_apparmor; and a Tomcat valve, tomcat_apparmor. Applications
written or modified to use aa_change_profile(2) transition permanently to the
written or modified to use change_profile(2) transition permanently to the
specified profile. libvirt is one such application.
=head2 Profile Head
@@ -604,7 +604,7 @@ modes:
=item B<Ux>
- unconfined execute -- use ld.so(8) secure-execution mode
- unconfined execute -- scrub the environment
=item B<px>
@@ -612,7 +612,7 @@ modes:
=item B<Px>
- discrete profile execute -- use ld.so(8) secure-execution mode
- discrete profile execute -- scrub the environment
=item B<cx>
@@ -620,7 +620,7 @@ modes:
=item B<Cx>
- transition to subprofile on execute -- use ld.so(8) secure-execution mode
- transition to subprofile on execute -- scrub the environment
=item B<ix>
@@ -632,7 +632,7 @@ modes:
=item B<Pix>
- discrete profile execute with inherit fallback -- use ld.so(8) secure-execution mode
- discrete profile execute with inherit fallback -- scrub the environment
=item B<cix>
@@ -640,7 +640,7 @@ modes:
=item B<Cix>
- transition to subprofile on execute with inherit fallback -- use ld.so(8) secure-execution mode
- transition to subprofile on execute with inherit fallback -- scrub the environment
=item B<pux>
@@ -648,7 +648,7 @@ modes:
=item B<PUx>
- discrete profile execute with fallback to unconfined -- use ld.so(8) secure-execution mode
- discrete profile execute with fallback to unconfined -- scrub the environment
=item B<cux>
@@ -656,7 +656,7 @@ modes:
=item B<CUx>
- transition to subprofile on execute with fallback to unconfined -- use ld.so(8) secure-execution mode
- transition to subprofile on execute with fallback to unconfined -- scrub the environment
=item B<deny x>
@@ -715,20 +715,20 @@ constrained, see the apparmor(7) man page.
B<WARNING> 'ux' should only be used in very special cases. It enables the
designated child processes to be run without any AppArmor protection.
'ux' does not use ld.so(8) secure-execution mode to clear variables such as
LD_PRELOAD; as a result, the calling domain may have an undue amount of
influence over the callee. Use this mode only if the child absolutely must be
'ux' does not scrub the environment of variables such as LD_PRELOAD;
as a result, the calling domain may have an undue amount of influence
over the callee. Use this mode only if the child absolutely must be
run unconfined and LD_PRELOAD must be used. Any profile using this mode
provides negligible security. Use at your own risk.
Incompatible with other exec transition modes and the deny qualifier.
=item B<Ux - unconfined execute -- use ld.so(8) secure-execution mode>
=item B<Ux - unconfined execute -- scrub the environment>
'Ux' allows the named program to run in 'ux' mode, but AppArmor
will invoke the Linux Kernel's B<unsafe_exec> routines to set ld.so(8)
secure-execution mode and clear environment variables such as LD_PRELOAD,
similar to setuid programs. (See ld.so(8) for more information.)
will invoke the Linux Kernel's B<unsafe_exec> routines to scrub
the environment, similar to setuid programs. (See ld.so(8) for some
information on setuid/setgid environment scrubbing.)
B<WARNING> 'Ux' should only be used in very special cases. It enables the
designated child processes to be run without any AppArmor protection.
@@ -743,18 +743,18 @@ This mode requires that a discrete security profile is defined for a
program executed and forces an AppArmor domain transition. If there is
no profile defined then the access will be denied.
B<WARNING> 'px' does not use ld.so(8) secure-execution mode to clear variables
such as LD_PRELOAD; as a result, the calling domain may have an undue amount of
B<WARNING> 'px' does not scrub the environment of variables such as
LD_PRELOAD; as a result, the calling domain may have an undue amount of
influence over the callee.
Incompatible with other exec transition modes and the deny qualifier.
=item B<Px - Discrete Profile execute mode -- use ld.so(8) secure-execution mode>
=item B<Px - Discrete Profile execute mode -- scrub the environment>
'Px' allows the named program to run in 'px' mode, but AppArmor
will invoke the Linux Kernel's B<unsafe_exec> routines to set ld.so(8)
secure-execution mode and clear environment variables such as LD_PRELOAD,
similar to setuid programs. (See ld.so(8) for more information.)
will invoke the Linux Kernel's B<unsafe_exec> routines to scrub
the environment, similar to setuid programs. (See ld.so(8) for some
information on setuid/setgid environment scrubbing.)
Incompatible with other exec transition modes and the deny qualifier.
@@ -764,18 +764,18 @@ This mode requires that a local security profile is defined and forces an
AppArmor domain transition to the named profile. If there is no profile
defined then the access will be denied.
B<WARNING> 'cx' does not use ld.so(8) secure-execution mode to clear variables
such as LD_PRELOAD; as a result, the calling domain may have an undue amount of
B<WARNING> 'cx' does not scrub the environment of variables such as
LD_PRELOAD; as a result, the calling domain may have an undue amount of
influence over the callee.
Incompatible with other exec transition modes and the deny qualifier.
=item B<Cx - Transition to Subprofile execute mode -- use ld.so(8) secure-execution mode>
=item B<Cx - Transition to Subprofile execute mode -- scrub the environment>
'Cx' allows the named program to run in 'cx' mode, but AppArmor
will invoke the Linux Kernel's B<unsafe_exec> routines to set ld.so(8)
secure-execution mode and clear environment variables such as LD_PRELOAD,
similar to setuid programs. (See ld.so(8) for more information.)
will invoke the Linux Kernel's B<unsafe_exec> routines to scrub
the environment, similar to setuid programs. (See ld.so(8) for some
information on setuid/setgid environment scrubbing.)
Incompatible with other exec transition modes and the deny qualifier.
@@ -788,7 +788,7 @@ will inherit the current profile.
This mode is useful when a confined program needs to call another
confined program without gaining the permissions of the target's
profile, or losing the permissions of the current profile. There is no
version to set secure-execution mode because 'ix' executions don't change
version to scrub the environment because 'ix' executions don't change
privileges.
Incompatible with other exec transition modes and the deny qualifier.
@@ -1024,7 +1024,7 @@ If a conditional is specified using '=', then the rule only grants permission
for mounts matching the exactly specified options. For example, an AppArmor
policy with the following rule:
mount options=ro /dev/foo -> /mnt/,
mount options=ro /dev/foo -E<gt> /mnt/,
Would match:
@@ -1071,7 +1071,7 @@ grants permission for each set of options. This provides a shorthand when
writing mount rules which might help to logically break up a conditional. For
example, if an AppArmor policy has the following rule:
mount options=ro options=atime,
mount options=ro options=atime
both of these mount commands will match:
@@ -1319,7 +1319,7 @@ Example IO_URING rules:
=over 4
# Allow io_uring operations
io_uring,
io_ring,
# Allow creation of a polling thread
io_uring sqpoll,
@@ -1339,9 +1339,8 @@ pivot_root(2) is optionally specified in the 'pivot_root' rule using the
'oldroot=' prefix.
AppArmor 'pivot_root' rules can specify a profile transition to occur during
the pivot_root(2) system call. Note that currently, this feature is not
supported by any kernel. When this feature will be supported, AppArmor will
only transition the process calling pivot_root(2) to the new profile.
the pivot_root(2) system call. Note that AppArmor will only transition the
process calling pivot_root(2) to the new profile.
The paths specified in 'pivot_root' rules must end with '/' since they are
directories.
@@ -1690,11 +1689,11 @@ rule set. Eg.
change_profile /bin/bash -> {new_profile1,new_profile2,new_profile3},
The exec mode dictates whether or not the Linux Kernel's B<unsafe_exec>
routines should be used to set ld.so(8) secure-execution mode and clear
environment variables such as LD_PRELOAD, similar to setuid programs.
(See ld.so(8) for more information.) The B<safe> mode sets up secure-execution
mode for the new application, and B<unsafe> mode disables AppArmor's
requirement for it (the kernel and/or libc may still turn it on). An
routines should be used to scrub the environment, similar to setuid programs.
(See ld.so(8) for some information on setuid/setgid environment scrubbing.) The
B<safe> mode sets up environment scrubbing to occur when the new application is
executed and B<unsafe> mode disables AppArmor's requirement for environment
scrubbing (the kernel and/or libc may still require environment scrubbing). An
exec mode can only be specified when an exec condition is present.
change_profile safe /bin/bash -> new_profile,
@@ -2159,7 +2158,7 @@ negative values match when specifying one or the other. Eg, 'rw' matches when
=head1 SEE ALSO
apparmor(7), apparmor_parser(8), apparmor_xattrs(7), aa-complain(1),
aa-enforce(1), aa_change_hat(2), aa_change_profile(2), mod_apparmor(5), and
aa-enforce(1), aa_change_hat(2), mod_apparmor(5), and
L<https://wiki.apparmor.net>.
=cut

View File

@@ -206,8 +206,8 @@ which can help debugging profiles.
=head2 Enable debug mode
When debug mode is enabled, AppArmor will log a few extra messages to
dmesg (not via the audit subsystem). For example, the logs will state when
ld.so(8) secure-execution mode has been applied in a profile transition.
dmesg (not via the audit subsystem). For example, the logs will tell
whether environment scrubbing has been applied.
To enable debug mode, run:

View File

@@ -63,6 +63,7 @@ typedef enum capability_flags {
} capability_flags;
int name_to_capability(const char *keyword);
void capabilities_init(void);
void __debug_capabilities(uint64_t capset, const char *name);
bool add_cap_feature_mask(struct aa_features *features, capability_flags flags);
void clear_cap_flag(capability_flags flags);

View File

@@ -44,12 +44,10 @@ optflag_table_t dumpflag_table[] = {
DUMP_DFA_PROGRESS | DUMP_DFA_STATS },
{ 1, "dfa-stats", "Dump dfa creation stats", DUMP_DFA_STATS },
{ 1, "dfa-states", "Dump final dfa state information", DUMP_DFA_STATES },
{ 1, "dfa-compressed-states", "Dump compressed dfa state information", DUMP_DFA_COMPTRESSED_STATES },
{ 1, "dfa-states-initial", "Dump dfa state immediately after initial build", DUMP_DFA_STATES_INIT },
{ 1, "dfa-states-post-filter", "Dump dfa state immediately after filtering deny", DUMP_DFA_STATES_POST_FILTER },
{ 1, "dfa-states-post-minimize", "Dump dfa state immediately after initial build", DUMP_DFA_STATES_POST_MINIMIZE },
{ 1, "dfa-states-post-unreachable", "Dump dfa state immediately after filtering deny", DUMP_DFA_STATES_POST_UNREACHABLE },
{ 1, "dfa-perms-build", "Dump permission being built from accept node", DUMP_DFA_PERMS },
{ 1, "dfa-graph", "Dump dfa dot (graphviz) graph", DUMP_DFA_GRAPH },
{ 1, "dfa-minimize", "Dump dfa minimization", DUMP_DFA_MINIMIZE },
{ 1, "dfa-unreachable", "Dump dfa unreachable states",

View File

@@ -95,12 +95,6 @@
#define ALL_USER_EXEC (AA_USER_EXEC | AA_USER_EXEC_TYPE)
#define ALL_OTHER_EXEC (AA_OTHER_EXEC | AA_OTHER_EXEC_TYPE)
#define AA_USER_EXEC_INHERIT (AA_EXEC_INHERIT << AA_USER_SHIFT)
#define AA_OTHER_EXEC_INHERIT (AA_EXEC_INHERIT << AA_OTHER_SHIFT)
#define AA_USER_EXEC_MMAP (AA_OLD_EXEC_MMAP << AA_USER_SHIFT)
#define AA_OTHER_EXEC_MMAP (AA_OLD_EXEC_MMAP << AA_OTHER_SHIFT)
#define AA_LINK_BITS ((AA_OLD_MAY_LINK << AA_USER_SHIFT) | \
(AA_OLD_MAY_LINK << AA_OTHER_SHIFT))

View File

@@ -10,199 +10,6 @@ aare_rules.{h,cc} - code to that binds parse -> expr-tree -> hfa generation
-> chfa generation into a basic interface for converting
rules to a runtime ready state machine.
Notes on the compiler pipeline order
============================================
Front End: Program driver logic and policy text parsing into an
abstract syntax tree.
Middle Layer: Transforms and operations on the abstract syntax tree.
Converts syntax tree into expression tree for back end.
Back End: transforms of syntax tree, and creation of policy HFA from
expression trees and HFAs.
Basic order of the backend of the compiler pipe line and where the
dump information occurs in the pipeline.
===== Front End (parse -> AST ================
|
v
yyparse
|
+--->--+-->-+
| |
| +-->---- +---------------------------<-----------------------+
| | | |
| | v |
| | yylex |
| | | |
| ^ token match |
| | | |
| | +----------------------------+ |
| | | | ^
| | v v |
| +-<- rule match? preprocess |
| | | |
| early var expansion +----------+-----------+ |
| | | | | |
^ v v v v |
| new rule() / new ent include variable conditional |
| | | | | |
| v +---->-----+----->-----+----->----+
| new rule semantic check
| |
+-----<-----+
|
----------- | ------ End of Parse --------------------
|
v
post_parse_profile semantic check
|
v
post_process
|
v
add implied rules()
|
v
process_profile_variables()
|
v
rule->expand_variables()
|
+--------+
|
v
replace aliases (to be moved to backend rewrite)
|
v
merge rules
|
v
profile->merge_rules()
|
v
+-->--rule->is_mergeable()
| |
^ v
| add to table
| |
+-------+--------+
|
v
sort->cmp()/oper<()
|
rule->merge()
|
+------------+
|
v
process_profile_rules
|
v
rule->gen_policy_re()
|
v
===== Mid layer (AST -> expr tree) =================
|
+-> add_rule() (aare_rules.{h,cc})
| |
| v
| rule parse (parse.y)
| | |
| | v
| | expr tree (expr-tree.{h,cc})
| | |
| v |
| unique perms | (aare_rules.{h,cc})
| | |
| +------ +
| |
| v
| add to rules expr tree (aare_rules.{h,c})
| |
+------+
|
+------------------+
|
v
create_dfablob()
|
v
expr tree
|
v
create_chfa() (aare_rules.cc)
|
v
expr normalization (expr-tree.{h,cc})
|
v
expr simplification (expr-tree.{h,c})
|
+- D expr-tree
|
+- D expr-simplified
|
==== Back End - Create cHFA out of expr tree and other HFAs ====
v
hfa creation (hfa.{h,cc})
|
+- D dfa-node-map
|
+- D dfa-uniq-perms
|
+- D dfa-states-initial
|
v
hfa rewrite (not yet implemented)
|
v
filter deny (hfa.{h,cc})
|
+- D dfa-states-post-filter
|
v
minimization (hfa.{h,cc})
|
+- D dfa-minimize-partitions
|
+- D dfa-minimize-uniq-perms
|
+- D dfa-states-post-minimize
|
v
unreachable state removal (hfa.{h,cc})
|
+- D dfa-states-post-unreachable
|
+- D dfa-states constructed hfa
|
+- D dfa-graph
|
v
equivalence class construction
|
+- D equiv
|
diff encode (hfa.{h,cc})
|
+- D diff-encode
|
compute perms table
|
+- D compressed-dfa == perm table dump
|
compressed hfa (chfa.{h,cc}
|
+- D compressed-dfa == transition tables
|
+- D dfa-compressed-states - compress HFA in state form
|
v
Return to Mid Layer
Notes on the compress hfa file format (chfa)
==============================================

View File

@@ -259,7 +259,7 @@ CHFA *aare_rules::create_chfa(int *min_match_len,
dfa.dump_uniq_perms("dfa");
if (opts.dump & DUMP_DFA_STATES_INIT)
dfa.dump(cerr, NULL);
dfa.dump(cerr);
/* since we are building a chfa, use the info about
* whether the chfa supports extended perms to help
@@ -271,23 +271,23 @@ CHFA *aare_rules::create_chfa(int *min_match_len,
((opts.control & CONTROL_DFA_FILTER_DENY))) {
dfa.apply_and_clear_deny();
if (opts.dump & DUMP_DFA_STATES_POST_FILTER)
dfa.dump(cerr, NULL);
dfa.dump(cerr);
}
if (opts.control & CONTROL_DFA_MINIMIZE) {
dfa.minimize(opts);
if (opts.dump & DUMP_DFA_MIN_UNIQ_PERMS)
dfa.dump_uniq_perms("minimized dfa");
if (opts.dump & DUMP_DFA_STATES_POST_MINIMIZE)
dfa.dump(cerr, NULL);
dfa.dump(cerr);
}
if (opts.control & CONTROL_DFA_REMOVE_UNREACHABLE) {
dfa.remove_unreachable(opts);
if (opts.dump & DUMP_DFA_STATES_POST_UNREACHABLE)
dfa.dump(cerr, NULL);
dfa.dump(cerr);
}
if (opts.dump & DUMP_DFA_STATES)
dfa.dump(cerr, NULL);
dfa.dump(cerr);
if (opts.dump & DUMP_DFA_GRAPH)
dfa.dump_dot_graph(cerr);
@@ -331,8 +331,6 @@ CHFA *aare_rules::create_chfa(int *min_match_len,
chfa = new CHFA(dfa, eq, opts, extended_perms, prompt);
if (opts.dump & DUMP_DFA_TRANS_TABLE)
chfa->dump(cerr);
if (opts.dump & DUMP_DFA_COMPTRESSED_STATES)
dfa.dump(cerr, &chfa->num);
}
catch(int error) {
return NULL;

View File

@@ -64,7 +64,5 @@
#define DUMP_DFA_STATES_POST_FILTER (1 << 26)
#define DUMP_DFA_STATES_POST_MINIMIZE (1 << 27)
#define DUMP_DFA_STATES_POST_UNREACHABLE (1 << 28)
#define DUMP_DFA_COMPTRESSED_STATES (1 << 29)
#define DUMP_DFA_PERMS (1 << 30)
#endif /* APPARMOR_RE_H */

View File

@@ -25,8 +25,6 @@
#include <iostream>
#include <fstream>
#include <limits>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
@@ -594,11 +592,10 @@ void CHFA::weld_file_to_policy(CHFA &file_chfa, size_t &new_start,
// to repeat
assert(accept.size() == old_base_size);
accept.resize(accept.size() + file_chfa.accept.size());
assert(policy_perms.size() < std::numeric_limits<ssize_t>::max());
ssize_t size = (ssize_t) policy_perms.size();
size_t size = policy_perms.size();
policy_perms.resize(size*2 + file_perms.size());
// shift and double the policy perms
for (ssize_t i = size - 1; i >= 0; i--) {
for (size_t i = size - 1; size >= 0; i--) {
policy_perms[i*2] = policy_perms[i];
policy_perms[i*2 + 1] = policy_perms[i];
}

View File

@@ -63,7 +63,7 @@ class CHFA {
DefaultBase default_base;
NextCheck next_check;
const State *start;
Renumber_Map num;
map<const State *, size_t> num;
map<transchar, transchar> eq;
unsigned int chfaflags;
private:

View File

@@ -245,7 +245,6 @@ ostream &operator<<(ostream &os, Node &node);
#define NODE_TYPE_MATCHFLAG (1 << 18)
#define NODE_TYPE_EXACTMATCHFLAG (1 << 19)
#define NODE_TYPE_DENYMATCHFLAG (1 << 20)
#define NODE_TYPE_PROMPTMATCHFLAG (1 << 21)
/* An abstract node in the syntax tree. */
class Node {
@@ -916,10 +915,7 @@ public:
class PromptMatchFlag: public MatchFlag {
public:
PromptMatchFlag(int priority, perm32_t prompt, perm32_t audit): MatchFlag(priority, prompt, audit)
{
type_flags |= NODE_TYPE_PROMPTMATCHFLAG;
}
PromptMatchFlag(int priority, perm32_t prompt, perm32_t audit): MatchFlag(priority, prompt, audit) {}
};

View File

@@ -83,21 +83,6 @@ ostream &operator<<(ostream &os, State &state)
return os;
}
ostream &operator<<(ostream &os,
const std::pair<State * const, Renumber_Map *> &p)
{
/* dump the state label */
if (p.second && (*p.second)[p.first] != (size_t) p.first->label) {
os << '{';
os << (*p.second)[p.first];
os << " == " << *(p.first);
os << '}';
} else {
os << *(p.first);
}
return os;
}
/**
* diff_weight - Find differential compression distance between @rel and @this
* @rel: State to compare too
@@ -317,8 +302,7 @@ static void split_node_types(NodeSet *nodes, NodeSet **anodes, NodeSet **nnodes
*nnodes = nodes;
}
State *DFA::add_new_state(optflags const &opts, NodeSet *anodes,
NodeSet *nnodes, State *other)
State *DFA::add_new_state(NodeSet *anodes, NodeSet *nnodes, State *other)
{
NodeVec *nnodev, *anodev;
nnodev = nnodes_cache.insert(nnodes);
@@ -326,7 +310,7 @@ State *DFA::add_new_state(optflags const &opts, NodeSet *anodes,
ProtoState proto;
proto.init(nnodev, anodev);
State *state = new State(opts, node_map.size(), proto, other, filedfa);
State *state = new State(node_map.size(), proto, other, filedfa);
pair<NodeMap::iterator,bool> x = node_map.insert(proto, state);
if (x.second == false) {
delete state;
@@ -338,7 +322,7 @@ State *DFA::add_new_state(optflags const &opts, NodeSet *anodes,
return x.first->second;
}
State *DFA::add_new_state(optflags const &opts, NodeSet *nodes, State *other)
State *DFA::add_new_state(NodeSet *nodes, State *other)
{
/* The splitting of nodes should probably get pushed down into
* follow(), ie. put in separate lists from the start
@@ -346,12 +330,12 @@ State *DFA::add_new_state(optflags const &opts, NodeSet *nodes, State *other)
NodeSet *anodes, *nnodes;
split_node_types(nodes, &anodes, &nnodes);
State *state = add_new_state(opts, anodes, nnodes, other);
State *state = add_new_state(anodes, nnodes, other);
return state;
}
void DFA::update_state_transitions(optflags const &opts, State *state)
void DFA::update_state_transitions(State *state)
{
/* Compute possible transitions for state->nodes. This is done by
* iterating over all the nodes in state->nodes and combining the
@@ -374,8 +358,7 @@ void DFA::update_state_transitions(optflags const &opts, State *state)
/* check the default transition first */
if (cases.otherwise)
state->otherwise = add_new_state(opts, cases.otherwise,
nonmatching);
state->otherwise = add_new_state(cases.otherwise, nonmatching);
else
state->otherwise = nonmatching;
@@ -384,7 +367,7 @@ void DFA::update_state_transitions(optflags const &opts, State *state)
*/
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
State *target;
target = add_new_state(opts, j->second, nonmatching);
target = add_new_state(j->second, nonmatching);
/* Don't insert transition that the otherwise transition
* already covers
@@ -431,7 +414,7 @@ void DFA::process_work_queue(const char *header, optflags const &opts)
/* Update 'from's transitions, and if it transitions to any
* unknown State create it and add it to the work_queue
*/
update_state_transitions(opts, from);
update_state_transitions(from);
} /* while (!work_queue.empty()) */
}
@@ -461,8 +444,8 @@ DFA::DFA(Node *root, optflags const &opts, bool buildfiledfa): root(root), filed
(*i)->compute_followpos();
}
nonmatching = add_new_state(opts, new NodeSet, NULL);
start = add_new_state(opts, new NodeSet(root->firstpos), nonmatching);
nonmatching = add_new_state(new NodeSet, NULL);
start = add_new_state(new NodeSet(root->firstpos), nonmatching);
/* the work_queue contains the states that need to have their
* transitions computed. This could be done with a recursive
@@ -510,6 +493,11 @@ DFA::DFA(Node *root, optflags const &opts, bool buildfiledfa): root(root), filed
*/
nnodes_cache.clear();
node_map.clear();
/* once created the priority information is no longer needed and
* can prevent sets with the same perms and different priorities
* from being merged during minimization
*/
clear_priorities();
}
DFA::~DFA()
@@ -558,14 +546,6 @@ void DFA::dump_uniq_perms(const char *s)
//TODO: add prompt
}
// make sure work_queue and reachable insertion are always done together
static void push_reachable(set<State *> &reachable, list<State *> &work_queue,
State *state)
{
work_queue.push_back(state);
reachable.insert(state);
}
/* Remove dead or unreachable states */
void DFA::remove_unreachable(optflags const &opts)
{
@@ -573,18 +553,19 @@ void DFA::remove_unreachable(optflags const &opts)
/* find the set of reachable states */
reachable.insert(nonmatching);
push_reachable(reachable, work_queue, start);
work_queue.push_back(start);
while (!work_queue.empty()) {
State *from = work_queue.front();
work_queue.pop_front();
reachable.insert(from);
if (from->otherwise != nonmatching &&
reachable.find(from->otherwise) == reachable.end())
push_reachable(reachable, work_queue, from->otherwise);
work_queue.push_back(from->otherwise);
for (StateTrans::iterator j = from->trans.begin(); j != from->trans.end(); j++) {
if (reachable.find(j->second) == reachable.end())
push_reachable(reachable, work_queue, j->second);
work_queue.push_back(j->second);
}
}
@@ -670,6 +651,13 @@ int DFA::apply_and_clear_deny(void)
return c;
}
void DFA::clear_priorities(void)
{
for (Partition::iterator i = states.begin(); i != states.end(); i++)
(*i)->perms.priority = 0;
}
/* minimize the number of dfa states */
void DFA::minimize(optflags const &opts)
@@ -1087,11 +1075,11 @@ void DFA::dump_diff_encode(ostream &os)
/**
* text-dump the DFA (for debugging).
*/
void DFA::dump(ostream &os, Renumber_Map *renum)
void DFA::dump(ostream & os)
{
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
if (*i == start || (*i)->perms.is_accept()) {
os << make_pair(*i, renum);
os << **i;
if (*i == start) {
os << " <== ";
(*i)->perms.dump_header(os);
@@ -1114,7 +1102,7 @@ void DFA::dump(ostream &os, Renumber_Map *renum)
} else {
if (first) {
first = false;
os << make_pair(*i, renum) << " perms: ";
os << **i << " perms: ";
if ((*i)->perms.is_accept())
(*i)->perms.dump(os);
else
@@ -1122,7 +1110,7 @@ void DFA::dump(ostream &os, Renumber_Map *renum)
os << "\n";
}
os << " "; j->first.dump(os) << " -> " <<
make_pair(j->second, renum);
*(j)->second;
if ((j)->second->perms.is_accept())
os << " ", (j->second)->perms.dump(os);
os << "\n";
@@ -1132,7 +1120,7 @@ void DFA::dump(ostream &os, Renumber_Map *renum)
if ((*i)->otherwise != nonmatching) {
if (first) {
first = false;
os << make_pair(*i, renum) << " perms: ";
os << **i << " perms: ";
if ((*i)->perms.is_accept())
(*i)->perms.dump(os);
else
@@ -1147,7 +1135,7 @@ void DFA::dump(ostream &os, Renumber_Map *renum)
os << *k;
}
}
os << "] -> " << make_pair((*i)->otherwise, renum);
os << "] -> " << *(*i)->otherwise;
if ((*i)->otherwise->perms.is_accept())
os << " ", (*i)->otherwise->perms.dump(os);
os << "\n";
@@ -1393,186 +1381,84 @@ static inline int diff_qualifiers(perm32_t perm1, perm32_t perm2)
(perm1 & AA_EXEC_TYPE) != (perm2 & AA_EXEC_TYPE));
}
/* update a single permission based on priority - only called if match->perm | match-> audit bit set */
static int pri_update_perm(optflags const &opts, vector<int> &priority, int i,
MatchFlag *match, perms_t &perms, perms_t &exact,
bool filedfa)
{
if (priority[i] > match->priority) {
if (opts.dump & DUMP_DFA_PERMS)
cerr << " " << match << "[" << i << "]=" << priority[i] << " > " << match->priority << " SKIPPING " << hex << (match->perms) << "/" << (match->audit) << dec << "\n";
return 0;
}
perm32_t xmask = 0;
perm32_t mask = 1 << i;
perm32_t amask = mask;
// drop once we move the xindex out of the perms in the front end
if (filedfa) {
if (mask & AA_USER_EXEC) {
xmask = AA_USER_EXEC_TYPE;
// ix implies EXEC_MMAP
if (match->perms & AA_EXEC_INHERIT) {
xmask |= AA_USER_EXEC_MMAP;
//USER_EXEC_MAP = 6
if (priority[6] < match->priority)
priority[6] = match->priority;
}
amask = mask | xmask;
} else if (mask & AA_OTHER_EXEC) {
xmask = AA_OTHER_EXEC_TYPE;
// ix implies EXEC_MMAP
if (match->perms & AA_OTHER_EXEC_INHERIT) {
xmask |= AA_OTHER_EXEC_MMAP;
//OTHER_EXEC_MAP = 20
if (priority[20] < match->priority)
priority[20] = match->priority;
}
amask = mask | xmask;
} else if (((mask & AA_USER_EXEC_MMAP) &&
(match->perms & AA_USER_EXEC_INHERIT)) ||
((mask & AA_OTHER_EXEC_MMAP) &&
(match->perms & AA_OTHER_EXEC_INHERIT))) {
// if exec && ix we handled mmp above
if (opts.dump & DUMP_DFA_PERMS)
cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << match->priority << " SKIPPING mmap unmasked " << hex << match->perms << "/" << match->audit << " masked " << (match->perms & amask) << "/" << (match->audit & amask) << " data " << (perms.allow & mask) << "/" << (perms.audit & mask) << " exact " << (exact.allow & mask) << "/" << (exact.audit & mask) << dec << "\n";
return 0;
}
}
if (opts.dump & DUMP_DFA_PERMS)
cerr << " " << match << "[" << i << "]=" << priority[i] << " vs. " << match->priority << " mask: " << hex << mask << " xmask: " << xmask << " amask: " << amask << dec << "\n";
if (priority[i] < match->priority) {
if (opts.dump & DUMP_DFA_PERMS)
cerr << " " << match << "[" << i << "]=" << priority[i] << " < " << match->priority << " clearing " << hex << (perms.allow & amask) << "/" << (perms.audit & amask) << " -> " << dec;
priority[i] = match->priority;
perms.clear_bits(amask);
exact.clear_bits(amask);
if (opts.dump & DUMP_DFA_PERMS)
cerr << hex << (perms.allow & amask) << "/" << (perms.audit & amask) << dec << "\n";
}
// the if conditions in order of permission priority
if (match->is_type(NODE_TYPE_DENYMATCHFLAG)) {
if (opts.dump & DUMP_DFA_PERMS)
cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << match->priority << " deny " << hex << (match->perms & amask) << "/" << (match->audit & amask) << dec << "\n";
perms.deny |= match->perms & amask;
perms.quiet |= match->audit & amask;
perms.allow &= ~amask;
perms.audit &= ~amask;
perms.prompt &= ~amask;
} else if (match->is_type(NODE_TYPE_EXACTMATCHFLAG)) {
/* exact match only asserts dominance on the XTYPE */
if (opts.dump & DUMP_DFA_PERMS)
cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << match->priority << " exact " << hex << (match->perms & amask) << "/" << (match->audit & amask) << dec << "\n";
if (filedfa &&
!is_merged_x_consistent(exact.allow, match->perms & amask)) {
if (opts.dump & DUMP_DFA_PERMS)
cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << match->priority << " exact match conflict" << "\n";
return 1;
}
exact.allow |= match->perms & amask;
exact.audit |= match->audit & amask;
// dominance is only done for XTYPE so only clear that
// note xmask only set if setting x perm bit, so this
// won't clear for other bit types
perms.allow &= ~xmask;
perms.audit &= ~xmask;
perms.prompt &= ~xmask;
perms.allow |= match->perms & amask;
perms.audit |= match->audit & amask;
// can't specify exact prompt atm
} else if (!match->is_type(NODE_TYPE_PROMPTMATCHFLAG)) {
// allow perms, if exact has been encountered will already be set
// if overlaps x here, don't conflict, because exact will override
if (opts.dump & DUMP_DFA_PERMS)
cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << match->priority << " allow " << hex << (match->perms & amask) << "/" << (match->audit & amask) << dec << "\n";
if (filedfa && !(exact.allow & mask) &&
!is_merged_x_consistent(perms.allow, match->perms & amask)) {
if (opts.dump & DUMP_DFA_PERMS)
cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << match->priority << " allow match conflict" << "\n";
return 1;
}
// mask off if XTYPE in xmatch
if ((exact.allow | exact.audit) & mask) {
// mask == amask & ~xmask
perms.allow |= match->perms & mask;
perms.audit |= match->audit & mask;
} else {
perms.allow |= match->perms & amask;
perms.audit |= match->audit & amask;
}
} else { // if (match->is_type(NODE_TYPE_PROMPTMATCHFLAG)) {
if (opts.dump & DUMP_DFA_PERMS)
cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << match->priority << " promt " << hex << (match->perms & amask) << "/" << (match->audit & amask) << dec << "\n";
if (filedfa && !((exact.allow | perms.allow) & mask) &&
!is_merged_x_consistent(perms.allow, match->perms & amask)) {
if (opts.dump & DUMP_DFA_PERMS)
cerr << " " << match << "[" << i << "]=" << priority[i] << " <= " << match->priority << " prompt match conflict" << "\n";
return 1;
}
if ((exact.allow | exact.audit | perms.allow | perms.audit) & mask) {
// mask == amask & ~xmask
perms.prompt |= match->perms & mask;
perms.audit |= match->audit & mask;
} else {
perms.prompt |= match->perms & amask;
perms.audit |= match->audit & amask;
}
}
return 0;
}
/**
* Compute the permission flags that this state corresponds to. If we
* have any exact matches, then they override the execute and safe
* execute flags.
*/
int accept_perms(optflags const &opts, NodeVec *state, perms_t &perms,
bool filedfa)
int accept_perms(NodeVec *state, perms_t &perms, bool filedfa)
{
int error = 0;
perms_t exact;
// size of vector needs to be number of bits in the data type
// being used for the permission set.
std::vector<int> priority(sizeof(perm32_t)*8, MIN_INTERNAL_PRIORITY);
perms.clear();
if (!state)
return error;
if (opts.dump & DUMP_DFA_PERMS)
cerr << "Building\n";
for (NodeVec::iterator i = state->begin(); i != state->end(); i++) {
if (!(*i)->is_type(NODE_TYPE_MATCHFLAG))
continue;
MatchFlag *match = static_cast<MatchFlag *>(*i);
if (perms.priority > match->priority)
continue;
perm32_t bit = 1;
perm32_t check = match->perms | match->audit;
if (filedfa)
check &= ~ALL_AA_EXEC_TYPE;
for (int i = 0; check; i++) {
if (check & bit) {
error = pri_update_perm(opts, priority, i, match, perms, exact, filedfa);
if (error)
goto out;
}
check &= ~bit;
bit <<= 1;
if (perms.priority < match->priority) {
perms.clear(match->priority);
exact.clear(match->priority);
}
if (match->is_type(NODE_TYPE_EXACTMATCHFLAG)) {
/* exact match only ever happens with x */
if (filedfa &&
!is_merged_x_consistent(exact.allow, match->perms))
error = 1;
exact.allow |= match->perms;
exact.audit |= match->audit;
} else if (match->is_type(NODE_TYPE_DENYMATCHFLAG)) {
perms.deny |= match->perms;
perms.quiet |= match->audit;
} else if (dynamic_cast<PromptMatchFlag *>(match)) {
perms.prompt |= match->perms;
perms.audit |= match->audit;
} else {
if (filedfa &&
!is_merged_x_consistent(perms.allow, match->perms))
error = 1;
perms.allow |= match->perms;
perms.audit |= match->audit;
}
}
if (opts.dump & DUMP_DFA_PERMS) {
cerr << " computed: "; perms.dump(cerr); cerr << "\n";
if (filedfa) {
perms.allow |= exact.allow & ~(ALL_AA_EXEC_TYPE);
perms.prompt |= exact.prompt & ~(ALL_AA_EXEC_TYPE);
perms.audit |= exact.audit & ~(ALL_AA_EXEC_TYPE);
} else {
perms.allow |= exact.allow;
perms.prompt |= exact.prompt;
perms.audit |= exact.audit;
}
out:
if (exact.allow & AA_USER_EXEC) {
perms.allow = (exact.allow & AA_USER_EXEC_TYPE) |
(perms.allow & ~AA_USER_EXEC_TYPE);
perms.exact = AA_USER_EXEC_TYPE;
}
if (exact.allow & AA_OTHER_EXEC) {
perms.allow = (exact.allow & AA_OTHER_EXEC_TYPE) |
(perms.allow & ~AA_OTHER_EXEC_TYPE);
perms.exact |= AA_OTHER_EXEC_TYPE;
}
if (filedfa && (AA_USER_EXEC & perms.deny))
perms.deny |= AA_USER_EXEC_TYPE;
if (filedfa && (AA_OTHER_EXEC & perms.deny))
perms.deny |= AA_OTHER_EXEC_TYPE;
perms.allow &= ~perms.deny;
perms.quiet &= perms.deny;
perms.prompt &= ~perms.deny;
perms.prompt &= ~perms.allow;
if (error)
fprintf(stderr, "profile has merged rule with conflicting x modifiers\n");

View File

@@ -52,37 +52,37 @@ ostream &operator<<(ostream &os, State &state);
class perms_t {
public:
perms_t(void): allow(0), deny(0), prompt(0), audit(0), quiet(0), exact(0) { };
perms_t(void): priority(MIN_INTERNAL_PRIORITY), allow(0), deny(0), prompt(0), audit(0), quiet(0), exact(0) { };
bool is_accept(void) { return (allow | deny | prompt | audit | quiet); }
void dump_header(ostream &os)
{
os << "(allow/deny/prompt/audit/quiet)";
os << "priority (allow/deny/prompt/audit/quiet)";
}
void dump(ostream &os)
{
os << "(0x " << hex
os << " " << priority << " (0x " << hex
<< allow << "/" << deny << "/" << "/" << prompt << "/" << audit << "/" << quiet
<< ')' << dec;
}
void clear(void) {
priority = MIN_INTERNAL_PRIORITY;
allow = deny = prompt = audit = quiet = exact = 0;
}
void clear_bits(perm32_t bits)
{
allow &= ~bits;
deny &= ~bits;
prompt &= ~bits;
audit &= ~bits;
quiet &= ~bits;
exact &= ~bits;
void clear(int p) {
priority = p;
allow = deny = prompt = audit = quiet = exact = 0;
}
void add(perms_t &rhs, bool filedfa)
{
if (priority > rhs.priority)
return;
if (priority < rhs.priority) {
*this = rhs;
return;
} //else if (rhs.priority == priority) {
deny |= rhs.deny;
if (filedfa && !is_merged_x_consistent(allow & ALL_USER_EXEC,
@@ -156,6 +156,8 @@ public:
bool operator<(perms_t const &rhs)const
{
if (priority < rhs.priority)
return priority < rhs.priority;
if (allow < rhs.allow)
return allow < rhs.allow;
if (deny < rhs.deny)
@@ -167,11 +169,11 @@ public:
return quiet < rhs.quiet;
}
int priority;
perm32_t allow, deny, prompt, audit, quiet, exact;
};
int accept_perms(optflags const &opts, NodeVec *state, perms_t &perms,
bool filedfa);
int accept_perms(NodeVec *state, perms_t &perms, bool filedfa);
/*
* ProtoState - NodeSet and ancillery information used to create a state
@@ -235,8 +237,7 @@ struct DiffDag {
*/
class State {
public:
State(optflags const &opts, int l, ProtoState &n, State *other,
bool filedfa):
State(int l, ProtoState &n, State *other, bool filedfa):
label(l), flags(0), idx(0), perms(), trans()
{
int error;
@@ -249,7 +250,7 @@ public:
proto = n;
/* Compute permissions associated with the State. */
error = accept_perms(opts, n.anodes, perms, filedfa);
error = accept_perms(n.anodes, perms, filedfa);
if (error) {
//cerr << "Failing on accept perms " << error << "\n";
throw error;
@@ -275,7 +276,7 @@ public:
ostream &dump(ostream &os)
{
os << *this << "\n";
cerr << *this << "\n";
for (StateTrans::iterator i = trans.begin(); i != trans.end(); i++) {
os << " " << i->first.c << " -> " << *i->second << "\n";
}
@@ -348,16 +349,12 @@ public:
}
};
typedef map<const State *, size_t> Renumber_Map;
/* Transitions in the DFA. */
class DFA {
void dump_node_to_dfa(void);
State *add_new_state(optflags const &opts, NodeSet *nodes,
State *other);
State *add_new_state(optflags const &opts,NodeSet *anodes,
NodeSet *nnodes, State *other);
void update_state_transitions(optflags const &opts, State *state);
State *add_new_state(NodeSet *nodes, State *other);
State *add_new_state(NodeSet *anodes, NodeSet *nnodes, State *other);
void update_state_transitions(State *state);
void process_work_queue(const char *header, optflags const &);
void dump_diff_chain(ostream &os, map<State *, Partition> &relmap,
Partition &chain, State *state,
@@ -388,7 +385,7 @@ public:
void undiff_encode(void);
void dump_diff_encode(ostream &os);
void dump(ostream &os, Renumber_Map *renum);
void dump(ostream &os);
void dump_dot_graph(ostream &os);
void dump_uniq_perms(const char *s);

View File

@@ -355,8 +355,7 @@ int is_valid_mnt_cond(const char *name, int src)
static unsigned int extract_flags(struct value_list **list, unsigned int *inv)
{
unsigned int flags = 0, invflags = 0;
if (inv)
*inv = 0;
*inv = 0;
struct value_list *entry, *tmp, *prev = NULL;
list_for_each_safe(*list, entry, tmp) {
@@ -369,7 +368,11 @@ static unsigned int extract_flags(struct value_list **list, unsigned int *inv)
" => req: 0x%x inv: 0x%x\n",
entry->value, mnt_opts_table[i].set,
mnt_opts_table[i].clear, flags, invflags);
list_remove_at(*list, prev, entry);
if (prev)
prev->next = tmp;
if (entry == *list)
*list = tmp;
entry->next = NULL;
free_value_list(entry);
} else
prev = entry;
@@ -680,7 +683,7 @@ int mnt_rule::cmp(rule_t const &rhs) const {
return cmp_vec_int(opt_flagsv, rhs_mnt.opt_flagsv);
}
static bool build_mnt_flags(char *buffer, int size, unsigned int flags,
static int build_mnt_flags(char *buffer, int size, unsigned int flags,
unsigned int opt_flags)
{
char *p = buffer;
@@ -690,8 +693,8 @@ static bool build_mnt_flags(char *buffer, int size, unsigned int flags,
/* all flags are optional */
len = snprintf(p, size, "%s", default_match_pattern);
if (len < 0 || len >= size)
return false;
return true;
return FALSE;
return TRUE;
}
for (i = 0; i <= 31; ++i) {
if ((opt_flags) & (1 << i))
@@ -702,7 +705,7 @@ static bool build_mnt_flags(char *buffer, int size, unsigned int flags,
continue;
if (len < 0 || len >= size)
return false;
return FALSE;
p += len;
size -= len;
}
@@ -713,15 +716,15 @@ static bool build_mnt_flags(char *buffer, int size, unsigned int flags,
* like the empty string
*/
if (size < 9)
return false;
return FALSE;
strcpy(p, "(\\xfe|)");
}
return true;
return TRUE;
}
static bool build_mnt_opts(std::string& buffer, struct value_list *opts)
static int build_mnt_opts(std::string& buffer, struct value_list *opts)
{
struct value_list *ent;
pattern_t ptype;
@@ -729,19 +732,19 @@ static bool build_mnt_opts(std::string& buffer, struct value_list *opts)
if (!opts) {
buffer.append(default_match_pattern);
return true;
return TRUE;
}
list_for_each(opts, ent) {
ptype = convert_aaregex_to_pcre(ent->value, 0, glob_default, buffer, &pos);
if (ptype == ePatternInvalid)
return false;
return FALSE;
if (ent->next)
buffer.append(",");
}
return true;
return TRUE;
}
void mnt_rule::warn_once(const char *name)

View File

@@ -179,6 +179,8 @@ struct var_string {
#define OPTION_STDOUT 4
#define OPTION_OFILE 5
#define BOOL int
extern int preprocess_only;
#define PATH_CHROOT_REL 0x1
@@ -211,6 +213,13 @@ do { \
errno = perror_error; \
} while (0)
#ifndef TRUE
#define TRUE (1)
#endif
#ifndef FALSE
#define FALSE (0)
#endif
#define MIN_PORT 0
#define MAX_PORT 65535
@@ -242,6 +251,17 @@ do { \
len; \
})
#define list_find_prev(LIST, ENTRY) \
({ \
typeof(ENTRY) tmp, prev = NULL; \
list_for_each((LIST), tmp) { \
if (tmp == (ENTRY)) \
break; \
prev = tmp; \
} \
prev; \
})
#define list_pop(LIST) \
({ \
typeof(LIST) _entry = (LIST); \
@@ -259,6 +279,12 @@ do { \
(LIST) = (ENTRY)->next; \
(ENTRY)->next = NULL; \
#define list_remove(LIST, ENTRY) \
do { \
typeof(ENTRY) prev = list_find_prev((LIST), (ENTRY)); \
list_remove_at((LIST), prev, (ENTRY)); \
} while (0)
#define DUP_STRING(orig, new, field, fail_target) \
do { \
@@ -397,10 +423,10 @@ extern const char *basedir;
#define glob_null 1
extern pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
std::string& pcre, int *first_re_pos);
extern bool build_list_val_expr(std::string& buffer, struct value_list *list);
extern bool convert_entry(std::string& buffer, char *entry);
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 bool convert_range(std::string& buffer, bignum start, bignum end);
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);
@@ -419,6 +445,7 @@ extern void free_var_string(struct var_string *var);
extern void warn_uppercase(void);
extern int is_blacklisted(const char *name, const char *path);
extern struct value_list *new_value_list(char *value);
extern struct value_list *dup_value_list(struct value_list *list);
extern void free_value_list(struct value_list *list);
extern void print_value_list(struct value_list *list);
extern struct cond_entry *new_cond_entry(char *name, int eq, struct value_list *list);

View File

@@ -142,10 +142,8 @@ static void process_entries(const void *nodep, VISIT value, int level unused)
}
if (dup) {
dup->alias_ignore = true;
/* The original entry->next is in dup->next, so we don't lose
* any of the original elements of the linked list. Also, by
* setting dup->alias_ignore, we trigger the check at the start
* of the loop, skipping the new entry we just inserted.
/* adds to the front of the list, list iteratition
* will skip it
*/
entry->next = dup;

View File

@@ -202,7 +202,7 @@ static void start_include_position(const char *filename)
current_lineno = 1;
}
void push_include_stack(const char *filename)
void push_include_stack(char *filename)
{
struct include_stack_t *include = NULL;

View File

@@ -29,7 +29,7 @@ extern void parse_default_paths(void);
extern int do_include_preprocessing(char *profilename);
FILE *search_path(char *filename, char **fullpath, bool *skip);
extern void push_include_stack(const char *filename);
extern void push_include_stack(char *filename);
extern void pop_include_stack(void);
extern void reset_include_stack(const char *filename);

View File

@@ -245,7 +245,7 @@ static inline void sd_write_uint64(std::ostringstream &buf, u64 b)
static inline void sd_write_name(std::ostringstream &buf, const char *name)
{
PDEBUG("Writing name '%s'\n", name ? name : "(null)");
PDEBUG("Writing name '%s'\n", name);
if (name) {
sd_write8(buf, SD_NAME);
sd_write16(buf, strlen(name) + 1);

View File

@@ -1620,6 +1620,7 @@ int main(int argc, char *argv[])
progname = argv[0];
init_base_dir();
capabilities_init();
process_early_args(argc, argv);
process_config_file(config_file);

View File

@@ -35,7 +35,6 @@
#include <sys/apparmor_private.h>
#include <algorithm>
#include <unordered_map>
#include "capability.h"
#include "lib.h"
@@ -62,10 +61,6 @@ void *reallocarray(void *ptr, size_t nmemb, size_t size)
}
#endif
#ifndef NULL
#define NULL nullptr
#endif
int is_blacklisted(const char *name, const char *path)
{
int retval = _aa_is_blacklisted(name);
@@ -76,7 +71,12 @@ int is_blacklisted(const char *name, const char *path)
return !retval ? 0 : 1;
}
static const unordered_map<string, int> keyword_table = {
struct keyword_table {
const char *keyword;
unsigned int token;
};
static struct keyword_table keyword_table[] = {
/* network */
{"network", TOK_NETWORK},
{"unix", TOK_UNIX},
@@ -132,9 +132,11 @@ static const unordered_map<string, int> keyword_table = {
{"sqpoll", TOK_SQPOLL},
{"all", TOK_ALL},
{"priority", TOK_PRIORITY},
/* terminate */
{NULL, 0}
};
static const unordered_map<string, int> rlimit_table = {
static struct keyword_table rlimit_table[] = {
{"cpu", RLIMIT_CPU},
{"fsize", RLIMIT_FSIZE},
{"data", RLIMIT_DATA},
@@ -160,33 +162,37 @@ static const unordered_map<string, int> rlimit_table = {
#ifdef RLIMIT_RTTIME
{"rttime", RLIMIT_RTTIME},
#endif
/* terminate */
{NULL, 0}
};
/* for alpha matches, check for keywords */
static int get_table_token(const char *name unused, const unordered_map<string, int> &table,
const string &keyword)
static int get_table_token(const char *name unused, struct keyword_table *table,
const char *keyword)
{
auto token_entry = table.find(keyword);
if (token_entry == table.end()) {
PDEBUG("Unable to find %s %s\n", name, keyword.c_str());
return -1;
} else {
PDEBUG("Found %s %s\n", name, keyword.c_str());
return token_entry->second;
int i;
for (i = 0; table[i].keyword; i++) {
PDEBUG("Checking %s %s\n", name, table[i].keyword);
if (strcmp(keyword, table[i].keyword) == 0) {
PDEBUG("Found %s %s\n", name, table[i].keyword);
return table[i].token;
}
}
PDEBUG("Unable to find %s %s\n", name, keyword);
return -1;
}
/* for alpha matches, check for keywords */
int get_keyword_token(const char *keyword)
{
// Can't use string_view because that requires C++17
return get_table_token("keyword", keyword_table, string(keyword));
return get_table_token("keyword", keyword_table, keyword);
}
int get_rlimit(const char *name)
{
// Can't use string_view because that requires C++17
return get_table_token("rlimit", rlimit_table, string(name));
return get_table_token("rlimit", rlimit_table, name);
}
@@ -202,164 +208,55 @@ struct capability_table {
capability_flags flags;
};
/*
* Enum for the results of adding a capability, with values assigned to match
* the int values returned by the old capable_add_cap function:
*
* -1: error
* 0: no change - capability already in table
* 1: added flag to capability in table
* 2: added new capability
*/
enum add_cap_result {
ERROR = -1, // Was only used for OOM conditions
ALREADY_EXISTS = 0,
FLAG_ADDED = 1,
CAP_ADDED = 2
};
static struct capability_table base_capability_table[] = {
/* capabilities */
#include "cap_names.h"
};
static const size_t BASE_CAP_TABLE_SIZE = sizeof(base_capability_table)/sizeof(struct capability_table);
class capability_lookup {
vector<capability_table> cap_table;
// Use unordered_map to avoid pulling in two map implementations
// We may want to switch to boost::multiindex to avoid duplication
unordered_map<string, capability_table&> name_cap_map;
unordered_map<unsigned int, capability_table&> int_cap_map;
private:
void add_capability_table_entry_raw(capability_table entry) {
cap_table.push_back(entry);
capability_table &entry_ref = cap_table.back();
name_cap_map.emplace(string(entry_ref.name), entry_ref);
int_cap_map.emplace(entry_ref.cap, entry_ref);
}
public:
capability_lookup() :
cap_table(vector<capability_table>()),
name_cap_map(unordered_map<string, capability_table&>(BASE_CAP_TABLE_SIZE)),
int_cap_map(unordered_map<unsigned int, capability_table&>(BASE_CAP_TABLE_SIZE)) {
cap_table.reserve(BASE_CAP_TABLE_SIZE);
for (size_t i=0; i<BASE_CAP_TABLE_SIZE; i++) {
add_capability_table_entry_raw(base_capability_table[i]);
}
}
capability_table* find_cap_entry_by_name(string const & name) const {
auto map_entry = this->name_cap_map.find(name);
if (map_entry == this->name_cap_map.end()) {
return NULL;
} else {
PDEBUG("Found %s %s\n", name.c_str(), map_entry->second.name);
return &map_entry->second;
}
}
capability_table* find_cap_entry_by_num(unsigned int cap) const {
auto map_entry = this->int_cap_map.find(cap);
if (map_entry == this->int_cap_map.end()) {
return NULL;
} else {
PDEBUG("Found %d %d\n", cap, map_entry->second.cap);
return &map_entry->second;
}
}
int name_to_capability(string const &cap) const {
auto map_entry = this->name_cap_map.find(cap);
if (map_entry == this->name_cap_map.end()) {
PDEBUG("Unable to find %s %s\n", "capability", cap.c_str());
return -1;
} else {
return map_entry->second.cap;
}
}
const char *capability_to_name(unsigned int cap) const {
auto map_entry = this->int_cap_map.find(cap);
if (map_entry == this->int_cap_map.end()) {
return "invalid-capability";
} else {
return map_entry->second.name;
}
}
int capability_backmap(unsigned int cap) const {
auto map_entry = this->int_cap_map.find(cap);
if (map_entry == this->int_cap_map.end()) {
return NO_BACKMAP_CAP;
} else {
return map_entry->second.backmap;
}
}
bool capability_in_kernel(unsigned int cap) const {
auto map_entry = this->int_cap_map.find(cap);
if (map_entry == this->int_cap_map.end()) {
return false;
} else {
return map_entry->second.flags & CAPFLAG_KERNEL_FEATURE;
}
}
void __debug_capabilities(uint64_t capset, const char *name) const {
printf("%s:", name);
for (auto it = this->cap_table.cbegin(); it != this->cap_table.cend(); it++) {
if ((1ull << it->cap) & capset)
printf (" %s", it->name);
}
printf("\n");
}
add_cap_result capable_add_cap(string const & str, unsigned int cap,
capability_flags flag) {
struct capability_table *ent = this->find_cap_entry_by_name(str);
if (ent) {
if (ent->cap != cap) {
pwarn(WARN_UNEXPECTED, "feature capability '%s:%d' does not equal expected %d. Ignoring ...\n", str.c_str(), cap, ent->cap);
/* TODO: make warn to error config */
return add_cap_result::ALREADY_EXISTS;
}
if (ent->flags & flag)
return add_cap_result::ALREADY_EXISTS;
ent->flags = (capability_flags) (ent->flags | flag);
return add_cap_result::FLAG_ADDED;
} else {
struct capability_table new_entry;
new_entry.name = strdup(str.c_str());
if (!new_entry.name) {
yyerror(_("Out of memory"));
return add_cap_result::ERROR;
}
new_entry.cap = cap;
new_entry.backmap = 0;
new_entry.flags = flag;
try {
this->add_capability_table_entry_raw(new_entry);
} catch (const std::bad_alloc &_e) {
yyerror(_("Out of memory"));
return add_cap_result::ERROR;
}
// TODO: exception catching for causes other than OOM
return add_cap_result::CAP_ADDED;
}
}
void clear_cap_flag(capability_flags flags)
{
for (auto it = this->cap_table.begin(); it != this->cap_table.end(); it++) {
PDEBUG("Clearing capability flag for capability \"%s\"\n", it->name);
it->flags = (capability_flags) (it->flags & ~flags);
}
}
/* terminate */
{NULL, 0, 0, CAPFLAGS_CLEAR}
};
static capability_lookup cap_table;
static struct capability_table *cap_table;
static int cap_table_size;
void capabilities_init(void)
{
cap_table = (struct capability_table *) malloc(sizeof(base_capability_table));
if (!cap_table)
yyerror(_("Memory allocation error."));
memcpy(cap_table, base_capability_table, sizeof(base_capability_table));
cap_table_size = sizeof(base_capability_table)/sizeof(struct capability_table);
}
struct capability_table *find_cap_entry_by_name(const char *name)
{
int i;
for (i = 0; cap_table[i].name; i++) {
PDEBUG("Checking %s %s\n", name, cap_table[i].name);
if (strcmp(name, cap_table[i].name) == 0) {
PDEBUG("Found %s %s\n", name, cap_table[i].name);
return &cap_table[i];
}
}
return NULL;
}
struct capability_table *find_cap_entry_by_num(unsigned int cap)
{
int i;
for (i = 0; cap_table[i].name; i++) {
PDEBUG("Checking %d %d\n", cap, cap_table[i].cap);
if (cap == cap_table[i].cap) {
PDEBUG("Found %d %d\n", cap, cap_table[i].cap);
return &cap_table[i];
}
}
return NULL;
}
/* don't mark up str with \0 */
static const char *strn_token(const char *str, size_t &len)
@@ -397,6 +294,59 @@ bool strcomp (const char *lhs, const char *rhs)
return null_strcmp(lhs, rhs) < 0;
}
/*
* Returns: -1: error
* 0: no change - capability already in table
* 1: added flag to capability in table
* 2: added new capability
*/
static int capable_add_cap(const char *str, int len, unsigned int cap,
capability_flags flag)
{
/* extract name from str so we can treat as a string */
autofree char *name = strndup(str, len);
if (!name) {
yyerror(_("Out of memory"));
return -1;
}
struct capability_table *ent = find_cap_entry_by_name(name);
if (ent) {
if (ent->cap != cap) {
pwarn(WARN_UNEXPECTED, "feature capability '%s:%d' does not equal expected %d. Ignoring ...\n", name, cap, ent->cap);
/* TODO: make warn to error config */
return 0;
}
if (ent->flags & flag)
return 0; /* no change */
ent->flags = (capability_flags) (ent->flags | flag);
return 1; /* modified */
} else {
struct capability_table *tmp;
tmp = (struct capability_table *) reallocarray(cap_table, sizeof(struct capability_table), cap_table_size+1);
if (!tmp) {
yyerror(_("Out of memory"));
/* TODO: change away from yyerror */
return -1;
}
cap_table = tmp;
ent = &cap_table[cap_table_size - 1]; /* overwrite null */
ent->name = strndup(name, len);
if (!ent->name) {
/* TODO: change away from yyerror */
yyerror(_("Out of memory"));
return -1;
}
ent->cap = cap;
ent->flags = flag;
cap_table[cap_table_size].name = NULL; /* new null */
cap_table_size++;
}
return 2; /* added */
}
bool add_cap_feature_mask(struct aa_features *features, capability_flags flags)
{
autofree char *value = NULL;
@@ -413,8 +363,7 @@ bool add_cap_feature_mask(struct aa_features *features, capability_flags flags)
for (capstr = strn_token(value, len);
capstr;
capstr = strn_token(capstr + len, len)) {
string capstr_as_str = string(capstr, len);
if (cap_table.capable_add_cap(capstr_as_str, n, flags) < 0)
if (capable_add_cap(capstr, len, n, flags) < 0)
return false;
n++;
if (len > valuelen) {
@@ -430,32 +379,70 @@ bool add_cap_feature_mask(struct aa_features *features, capability_flags flags)
void clear_cap_flag(capability_flags flags)
{
cap_table.clear_cap_flag(flags);
int i;
for (i = 0; cap_table[i].name; i++) {
PDEBUG("Clearing capability flag for capability \"%s\"\n", cap_table[i].name);
cap_table[i].flags = (capability_flags) (cap_table[i].flags & ~flags);
}
}
int name_to_capability(const char *cap)
{
return cap_table.name_to_capability(string(cap));
struct capability_table *ent;
ent = find_cap_entry_by_name(cap);
if (ent)
return ent->cap;
PDEBUG("Unable to find %s %s\n", "capability", cap);
return -1;
}
const char *capability_to_name(unsigned int cap)
{
return cap_table.capability_to_name(cap);
struct capability_table *ent;
ent = find_cap_entry_by_num(cap);
if (ent)
return ent->name;
return "invalid-capability";
}
int capability_backmap(unsigned int cap)
{
return cap_table.capability_backmap(cap);
struct capability_table *ent;
ent = find_cap_entry_by_num(cap);
if (ent)
return ent->backmap;
return NO_BACKMAP_CAP;
}
bool capability_in_kernel(unsigned int cap)
{
return cap_table.capability_in_kernel(cap);
struct capability_table *ent;
ent = find_cap_entry_by_num(cap);
if (ent)
return ent->flags & CAPFLAG_KERNEL_FEATURE;
return false;
}
void __debug_capabilities(uint64_t capset, const char *name)
{
cap_table.__debug_capabilities(capset, name);
unsigned int i;
printf("%s:", name);
for (i = 0; cap_table[i].name; i++) {
if ((1ull << cap_table[i].cap) & capset)
printf (" %s", cap_table[i].name);
}
printf("\n");
}
char *processunquoted(const char *string, int len)
@@ -1213,6 +1200,37 @@ void free_value_list(struct value_list *list)
}
}
struct value_list *dup_value_list(struct value_list *list)
{
struct value_list *entry, *dup, *head = NULL;
char *value;
list_for_each(list, entry) {
value = NULL;
if (list->value) {
value = strdup(list->value);
if (!value)
goto fail2;
}
dup = new_value_list(value);
if (!dup)
goto fail;
if (head)
list_append(head, dup);
else
head = dup;
}
return head;
fail:
free(value);
fail2:
free_value_list(head);
return NULL;
}
void print_value_list(struct value_list *list)
{
struct value_list *entry;

View File

@@ -50,7 +50,7 @@ enum error_type {
void filter_slashes(char *path)
{
char *sptr, *dptr;
bool seen_slash = false;
BOOL seen_slash = 0;
if (!path || (strlen(path) < 2))
return;
@@ -69,7 +69,7 @@ void filter_slashes(char *path)
++sptr;
} else {
*dptr++ = *sptr++;
seen_slash = true;
seen_slash = TRUE;
}
} else {
seen_slash = 0;
@@ -111,14 +111,14 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
#define MAX_ALT_DEPTH 50
*first_re_pos = 0;
int ret = 1;
int ret = TRUE;
/* flag to indicate input error */
enum error_type error;
const char *sptr;
pattern_t ptype;
bool bEscape = false; /* flag to indicate escape */
BOOL bEscape = 0; /* flag to indicate escape */
int ingrouping = 0; /* flag to indicate {} context */
int incharclass = 0; /* flag to indicate [ ] context */
int grouping_count[MAX_ALT_DEPTH] = {0};
@@ -150,7 +150,7 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
if (bEscape) {
pcre.append("\\\\");
} else {
bEscape = true;
bEscape = TRUE;
++sptr;
continue; /*skip turning bEscape off */
} /* bEscape */
@@ -393,7 +393,7 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
break;
} /* switch (*sptr) */
bEscape = false;
bEscape = FALSE;
++sptr;
} /* while error == e_no_error && *sptr) */
@@ -419,12 +419,12 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
PERROR(_("%s: Unable to parse input line '%s'\n"),
progname, aare);
ret = 0;
ret = FALSE;
goto out;
}
out:
if (ret == 0)
if (ret == FALSE)
ptype = ePatternInvalid;
if (parseopts.dump & DUMP_DFA_RULE_EXPR)
@@ -464,7 +464,7 @@ static void warn_once_xattr(const char *name)
common_warn_once(name, "xattr attachment conditional ignored", &warned_name);
}
static bool process_profile_name_xmatch(Profile *prof)
static int process_profile_name_xmatch(Profile *prof)
{
std::string tbuf;
pattern_t ptype;
@@ -479,7 +479,7 @@ static bool process_profile_name_xmatch(Profile *prof)
/* don't filter_slashes for profile names, do on attachment */
name = strdup(local_name(prof->name));
if (!name)
return false;
return FALSE;
}
filter_slashes(name);
ptype = convert_aaregex_to_pcre(name, 0, glob_default, tbuf,
@@ -491,7 +491,7 @@ static bool process_profile_name_xmatch(Profile *prof)
PERROR(_("%s: Invalid profile name '%s' - bad regular expression\n"), progname, name);
if (!prof->attachment)
free(name);
return false;
return FALSE;
}
if (!prof->attachment)
@@ -506,11 +506,11 @@ static bool process_profile_name_xmatch(Profile *prof)
/* build a dfa */
aare_rules *rules = new aare_rules();
if (!rules)
return false;
return FALSE;
if (!rules->add_rule(tbuf.c_str(), 0, RULE_ALLOW,
AA_MAY_EXEC, 0, parseopts)) {
delete rules;
return false;
return FALSE;
}
if (prof->altnames) {
struct alt_name *alt;
@@ -525,7 +525,7 @@ static bool process_profile_name_xmatch(Profile *prof)
RULE_ALLOW, AA_MAY_EXEC,
0, parseopts)) {
delete rules;
return false;
return FALSE;
}
}
}
@@ -567,7 +567,7 @@ static bool process_profile_name_xmatch(Profile *prof)
&len);
if (!rules->append_rule(tbuf.c_str(), true, true, parseopts)) {
delete rules;
return false;
return FALSE;
}
}
}
@@ -581,10 +581,10 @@ build:
prof->xmatch = rules->create_dfablob(&prof->xmatch_size, &prof->xmatch_len, prof->xmatch_perms_table, parseopts, false, false, false);
delete rules;
if (!prof->xmatch)
return false;
return FALSE;
}
return true;
return TRUE;
}
static int warn_change_profile = 1;
@@ -606,21 +606,21 @@ static bool is_change_profile_perms(perm32_t perms)
return perms & AA_CHANGE_PROFILE;
}
static bool process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
{
std::string tbuf;
pattern_t ptype;
int pos;
if (!entry) /* shouldn't happen */
return false;
return TRUE;
if (!is_change_profile_perms(entry->perms))
filter_slashes(entry->name);
ptype = convert_aaregex_to_pcre(entry->name, 0, glob_default, tbuf, &pos);
if (ptype == ePatternInvalid)
return false;
return FALSE;
entry->pattern_type = ptype;
@@ -649,13 +649,13 @@ static bool process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE),
entry->audit == AUDIT_FORCE ? entry->perms & ~(AA_LINK_BITS | AA_CHANGE_PROFILE) : 0,
parseopts))
return false;
return FALSE;
} else if (!is_change_profile_perms(entry->perms)) {
if (!dfarules->add_rule(tbuf.c_str(), entry->priority,
entry->rule_mode, entry->perms,
entry->audit == AUDIT_FORCE ? entry->perms : 0,
parseopts))
return false;
return FALSE;
}
if (entry->perms & (AA_LINK_BITS)) {
@@ -669,7 +669,7 @@ static bool process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
filter_slashes(entry->link_name);
ptype = convert_aaregex_to_pcre(entry->link_name, 0, glob_default, lbuf, &pos);
if (ptype == ePatternInvalid)
return false;
return FALSE;
if (entry->subset)
perms |= LINK_TO_LINK_SUBSET(perms);
vec[1] = lbuf.c_str();
@@ -681,7 +681,7 @@ static bool process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
entry->rule_mode, perms,
entry->audit == AUDIT_FORCE ? perms & AA_LINK_BITS : 0,
2, vec, parseopts, false))
return false;
return FALSE;
}
if (is_change_profile_perms(entry->perms)) {
const char *vec[3];
@@ -702,7 +702,7 @@ static bool process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
if (entry->onexec) {
ptype = convert_aaregex_to_pcre(entry->onexec, 0, glob_default, xbuf, &pos);
if (ptype == ePatternInvalid)
return false;
return FALSE;
vec[0] = xbuf.c_str();
} else
/* allow change_profile for all execs */
@@ -713,14 +713,14 @@ static bool process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
if (!parse_label(&stack, &ns, &name,
tbuf.c_str(), false)) {
return false;
return FALSE;
}
if (stack) {
fprintf(stderr,
_("The current kernel does not support stacking of named transitions: %s\n"),
tbuf.c_str());
return false;
return FALSE;
}
if (ns)
@@ -734,13 +734,13 @@ static bool process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
if (!dfarules->add_rule_vec(entry->priority, entry->rule_mode,
AA_CHANGE_PROFILE | onexec_perms,
0, index - 1, &vec[1], parseopts, false))
return false;
return FALSE;
/* onexec rules - both rules are needed for onexec */
if (!dfarules->add_rule_vec(entry->priority, entry->rule_mode,
onexec_perms,
0, 1, vec, parseopts, false))
return false;
return FALSE;
/**
* pick up any exec bits, from the frontend parser, related to
@@ -750,19 +750,19 @@ static bool process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
if (!dfarules->add_rule_vec(entry->priority, entry->rule_mode,
onexec_perms, 0, index, vec,
parseopts, false))
return false;
return FALSE;
}
return true;
return TRUE;
}
bool post_process_entries(Profile *prof)
int post_process_entries(Profile *prof)
{
int ret = true;
int ret = TRUE;
struct cod_entry *entry;
list_for_each(prof->entries, entry) {
if (!process_dfa_entry(prof->dfa.rules, entry))
ret = false;
ret = FALSE;
}
return ret;
@@ -815,7 +815,7 @@ out:
return error;
}
bool build_list_val_expr(std::string& buffer, struct value_list *list)
int build_list_val_expr(std::string& buffer, struct value_list *list)
{
struct value_list *ent;
pattern_t ptype;
@@ -823,7 +823,7 @@ bool build_list_val_expr(std::string& buffer, struct value_list *list)
if (!list) {
buffer.append(default_match_pattern);
return true;
return TRUE;
}
buffer.append("(");
@@ -840,12 +840,12 @@ bool build_list_val_expr(std::string& buffer, struct value_list *list)
}
buffer.append(")");
return true;
return TRUE;
fail:
return false;
return FALSE;
}
bool convert_entry(std::string& buffer, char *entry)
int convert_entry(std::string& buffer, char *entry)
{
pattern_t ptype;
int pos;
@@ -853,12 +853,12 @@ bool convert_entry(std::string& buffer, char *entry)
if (entry) {
ptype = convert_aaregex_to_pcre(entry, 0, glob_default, buffer, &pos);
if (ptype == ePatternInvalid)
return false;
return FALSE;
} else {
buffer.append(default_match_pattern);
}
return true;
return TRUE;
}
int clear_and_convert_entry(std::string& buffer, char *entry)
@@ -959,7 +959,7 @@ static std::string generate_regex_range(bignum start, bignum end)
return result.str();
}
bool convert_range(std::string& buffer, bignum start, bignum end)
int convert_range(std::string& buffer, bignum start, bignum end)
{
pattern_t ptype;
int pos;
@@ -969,24 +969,24 @@ bool convert_range(std::string& buffer, bignum start, bignum end)
if (!regex_range.empty()) {
ptype = convert_aaregex_to_pcre(regex_range.c_str(), 0, glob_default, buffer, &pos);
if (ptype == ePatternInvalid)
return false;
return FALSE;
} else {
buffer.append(default_match_pattern);
}
return true;
return TRUE;
}
bool post_process_policydb_ents(Profile *prof)
int post_process_policydb_ents(Profile *prof)
{
for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) {
if ((*i)->skip())
continue;
if ((*i)->gen_policy_re(*prof) == RULE_ERROR)
return false;
return FALSE;
}
return true;
return TRUE;
}

View File

@@ -79,7 +79,7 @@ struct var_string *split_out_var(const char *string)
{
struct var_string *n = NULL;
const char *sptr;
bool bEscape = false; /* flag to indicate escape */
BOOL bEscape = 0; /* flag to indicate escape */
if (!string) /* shouldn't happen */
return NULL;
@@ -89,11 +89,15 @@ struct var_string *split_out_var(const char *string)
while (!n && *sptr) {
switch (*sptr) {
case '\\':
bEscape = !bEscape;
if (bEscape) {
bEscape = FALSE;
} else {
bEscape = TRUE;
}
break;
case '@':
if (bEscape) {
bEscape = false;
bEscape = FALSE;
} else if (*(sptr + 1) == '{') {
const char *eptr = get_var_end(sptr + 2);
if (!eptr)
@@ -107,7 +111,8 @@ struct var_string *split_out_var(const char *string)
}
break;
default:
bEscape = false;
if (bEscape)
bEscape = FALSE;
}
sptr++;
}

View File

@@ -704,7 +704,7 @@ rules: rules opt_prefix block
if (($2).priority != 0) {
yyerror(_("priority is not allowed on rule blocks"));
}
PDEBUG("matched: %s%s%s%sblock\n",
PDEBUG("matched: %s%s%sblock\n",
$2.audit == AUDIT_FORCE ? "audit " : "",
$2.rule_mode == RULE_DENY ? "deny " : "",
$2.rule_mode == RULE_PROMPT ? "prompt " : "",

View File

@@ -1,14 +1,14 @@
# Translations for apparmor_parser
# Copyright (C) 2024 YEAR Canonical Ltd
# This file is distributed under the same license as the AppArmor package.
# John Johansen <john.johansen@canonical.com>, 2011.
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Canonical Ltd
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: apparmor@lists.ubuntu.com\n"
"POT-Creation-Date: 2024-08-31 15:55-0700\n"
"POT-Creation-Date: 2020-10-14 03:51-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -86,41 +86,37 @@ msgid "Permission denied; attempted to load a profile while confined?\n"
msgstr ""
#: ../parser_interface.c:99 ../parser_interface.c:102 ../parser_interface.c:79
#: ../parser_interface.c:82 ../parser_interface.c:84
#: ../parser_interface.c:82
#, c-format
msgid "Unknown error (%d): %s\n"
msgstr ""
#: ../parser_interface.c:116 ../parser_interface.c:119 ../parser_interface.c:96
#: ../parser_interface.c:100 ../parser_interface.c:102
#: ../parser_interface.c:100
#, c-format
msgid "%s: Unable to add \"%s\". "
msgstr ""
#: ../parser_interface.c:121 ../parser_interface.c:124
#: ../parser_interface.c:101 ../parser_interface.c:105
#: ../parser_interface.c:107
#, c-format
msgid "%s: Unable to replace \"%s\". "
msgstr ""
#: ../parser_interface.c:126 ../parser_interface.c:129
#: ../parser_interface.c:106 ../parser_interface.c:110
#: ../parser_interface.c:112
#, c-format
msgid "%s: Unable to remove \"%s\". "
msgstr ""
#: ../parser_interface.c:131 ../parser_interface.c:134
#: ../parser_interface.c:111 ../parser_interface.c:115
#: ../parser_interface.c:117
#, c-format
msgid "%s: Unable to write to stdout\n"
msgstr ""
#: ../parser_interface.c:135 ../parser_interface.c:138
#: ../parser_interface.c:115 ../parser_interface.c:119
#: ../parser_interface.c:121
#, c-format
msgid "%s: Unable to write to output file\n"
msgstr ""
@@ -129,28 +125,24 @@ msgstr ""
#: ../parser_interface.c:141 ../parser_interface.c:165
#: ../parser_interface.c:118 ../parser_interface.c:142
#: ../parser_interface.c:123 ../parser_interface.c:147
#: ../parser_interface.c:125 ../parser_interface.c:149
#, c-format
msgid "%s: ASSERT: Invalid option: %d\n"
msgstr ""
#: ../parser_interface.c:147 ../parser_interface.c:150
#: ../parser_interface.c:127 ../parser_interface.c:132
#: ../parser_interface.c:134
#, c-format
msgid "Addition succeeded for \"%s\".\n"
msgstr ""
#: ../parser_interface.c:151 ../parser_interface.c:154
#: ../parser_interface.c:131 ../parser_interface.c:136
#: ../parser_interface.c:138
#, c-format
msgid "Replacement succeeded for \"%s\".\n"
msgstr ""
#: ../parser_interface.c:155 ../parser_interface.c:158
#: ../parser_interface.c:135 ../parser_interface.c:140
#: ../parser_interface.c:142
#, c-format
msgid "Removal succeeded for \"%s\".\n"
msgstr ""
@@ -162,7 +154,6 @@ msgstr ""
#: ../parser_interface.c:656 ../parser_interface.c:658
#: ../parser_interface.c:446 ../parser_interface.c:476
#: ../parser_interface.c:542
#, c-format
msgid "profile %s network rules not enforced\n"
msgstr ""
@@ -208,18 +199,16 @@ msgstr ""
#: ../parser_interface.c:839 ../parser_interface.c:831
#: ../parser_interface.c:593 ../parser_interface.c:579
#: ../parser_interface.c:673
#, c-format
msgid "%s: Unable to write entire profile entry to cache\n"
msgstr ""
#: parser_lex.l:100 parser_lex.l:163 parser_lex.l:169 parser_lex.l:192
#: parser_lex.l:100 parser_lex.l:163 parser_lex.l:169
#, c-format
msgid "Could not open '%s'"
msgstr ""
#: parser_lex.l:104 parser_lex.l:167 parser_lex.l:173 parser_lex.l:174
#: parser_lex.l:197
#, c-format
msgid "fstat failed for '%s'"
msgstr ""
@@ -234,7 +223,7 @@ msgstr ""
msgid "stat failed for '%s'"
msgstr ""
#: parser_lex.l:155 parser_lex.l:133 parser_lex.l:139 parser_lex.l:148
#: parser_lex.l:155 parser_lex.l:133 parser_lex.l:139
#, c-format
msgid "Could not open '%s' in '%s'"
msgstr ""
@@ -246,7 +235,6 @@ msgid "Found unexpected character: '%s'"
msgstr ""
#: parser_lex.l:386 parser_lex.l:418 parser_lex.l:428 parser_lex.l:474
#: parser_lex.l:516
msgid "Variable declarations do not accept trailing commas"
msgstr ""
@@ -266,7 +254,7 @@ msgid "%s: Could not allocate memory for subdomainbase mount point\n"
msgstr ""
#: ../parser_main.c:577 ../parser_main.c:616 ../parser_main.c:479
#: ../parser_main.c:1444 ../parser_main.c:1654
#: ../parser_main.c:1444
#, c-format
msgid ""
"Warning: unable to find a suitable fs in %s, is it mounted?\n"
@@ -282,7 +270,7 @@ msgid ""
msgstr ""
#: ../parser_main.c:604 ../parser_main.c:642 ../parser_main.c:505
#: ../parser_main.c:828 ../parser_main.c:891
#: ../parser_main.c:828
#, c-format
msgid ""
"%s: Warning! You've set this program setuid root.\n"
@@ -292,7 +280,6 @@ msgstr ""
#: ../parser_main.c:704 ../parser_main.c:813 ../parser_main.c:836
#: ../parser_main.c:946 ../parser_main.c:860 ../parser_main.c:1038
#: ../parser_main.c:1127
#, c-format
msgid "Error: Could not read profile %s: %s.\n"
msgstr ""
@@ -321,36 +308,29 @@ msgstr ""
#: parser_yacc.y:1278 parser_yacc.y:1288 parser_yacc.y:1382 parser_yacc.y:1460
#: parser_yacc.y:1592 parser_yacc.y:1597 parser_yacc.y:1674 parser_yacc.y:1692
#: parser_yacc.y:1699 parser_yacc.y:1748 ../network.c:315 ../af_unix.cc:194
#: ../parser_misc.c:226 ../parser_misc.c:970 parser_yacc.y:379
#: parser_yacc.y:403 parser_yacc.y:571 parser_yacc.y:581 parser_yacc.y:673
#: parser_yacc.y:744 parser_yacc.y:1073 parser_yacc.y:1160 parser_yacc.y:1169
#: parser_yacc.y:1173 parser_yacc.y:1183 parser_yacc.y:1193 parser_yacc.y:1287
#: parser_yacc.y:1365 parser_yacc.y:1561 parser_yacc.y:1569 parser_yacc.y:1619
#: parser_yacc.y:1624 parser_yacc.y:1701 parser_yacc.y:1750 ../network.cc:899
#: ../af_unix.cc:197 ../all_rule.cc:102 ../all_rule.cc:131
msgid "Memory allocation error."
msgstr ""
#: ../parser_main.c:740 ../parser_main.c:872 ../parser_main.c:757
#: ../parser_main.c:975 ../parser_main.c:1062
#: ../parser_main.c:975
#, c-format
msgid "Cached load succeeded for \"%s\".\n"
msgstr ""
#: ../parser_main.c:744 ../parser_main.c:876 ../parser_main.c:761
#: ../parser_main.c:979 ../parser_main.c:1066
#: ../parser_main.c:979
#, c-format
msgid "Cached reload succeeded for \"%s\".\n"
msgstr ""
#: ../parser_main.c:910 ../parser_main.c:1058 ../parser_main.c:967
#: ../parser_main.c:1132 ../parser_main.c:1221
#: ../parser_main.c:1132
#, c-format
msgid "%s: Errors found in file. Aborting.\n"
msgstr ""
#: ../parser_misc.c:426 ../parser_misc.c:597 ../parser_misc.c:339
#: ../parser_misc.c:532 ../parser_misc.c:563
#: ../parser_misc.c:532
msgid ""
"Uppercase qualifiers \"RWLIMX\" are deprecated, please convert to lowercase\n"
"See the apparmor.d(5) manpage for details.\n"
@@ -358,18 +338,17 @@ msgstr ""
#: ../parser_misc.c:467 ../parser_misc.c:474 ../parser_misc.c:638
#: ../parser_misc.c:645 ../parser_misc.c:380 ../parser_misc.c:387
#: ../parser_misc.c:573 ../parser_misc.c:580 ../parser_misc.c:604
#: ../parser_misc.c:611
#: ../parser_misc.c:573 ../parser_misc.c:580
msgid "Conflict 'a' and 'w' perms are mutually exclusive."
msgstr ""
#: ../parser_misc.c:491 ../parser_misc.c:662 ../parser_misc.c:404
#: ../parser_misc.c:597 ../parser_misc.c:628
#: ../parser_misc.c:597
msgid "Exec qualifier 'i' invalid, conflicting qualifier already specified"
msgstr ""
#: ../parser_misc.c:502 ../parser_misc.c:673 ../parser_misc.c:415
#: ../parser_misc.c:608 ../parser_misc.c:639
#: ../parser_misc.c:608
#, c-format
msgid ""
"Unconfined exec qualifier (%c%c) allows some dangerous environment variables "
@@ -378,16 +357,14 @@ msgstr ""
#: ../parser_misc.c:510 ../parser_misc.c:551 ../parser_misc.c:681
#: ../parser_misc.c:722 ../parser_misc.c:423 ../parser_misc.c:464
#: ../parser_misc.c:616 ../parser_misc.c:657 ../parser_misc.c:647
#: ../parser_misc.c:688
#: ../parser_misc.c:616 ../parser_misc.c:657
#, c-format
msgid "Exec qualifier '%c' invalid, conflicting qualifier already specified"
msgstr ""
#: ../parser_misc.c:537 ../parser_misc.c:545 ../parser_misc.c:708
#: ../parser_misc.c:716 ../parser_misc.c:450 ../parser_misc.c:458
#: ../parser_misc.c:643 ../parser_misc.c:651 ../parser_misc.c:674
#: ../parser_misc.c:682
#: ../parser_misc.c:643 ../parser_misc.c:651
#, c-format
msgid "Exec qualifier '%c%c' invalid, conflicting qualifier already specified"
msgstr ""
@@ -399,7 +376,7 @@ msgid "Internal: unexpected mode character '%c' in input"
msgstr ""
#: ../parser_misc.c:615 ../parser_misc.c:786 ../parser_misc.c:528
#: ../parser_misc.c:721 ../parser_misc.c:752
#: ../parser_misc.c:721
#, c-format
msgid "Internal error generated invalid perm 0x%llx\n"
msgstr ""
@@ -411,12 +388,12 @@ msgid "AppArmor parser error: %s\n"
msgstr ""
#: ../parser_merge.c:92 ../parser_merge.c:91 ../parser_merge.c:83
#: ../parser_merge.c:71 ../parser_merge.c:74
#: ../parser_merge.c:71
msgid "Couldn't merge entries. Out of Memory\n"
msgstr ""
#: ../parser_merge.c:111 ../parser_merge.c:113 ../parser_merge.c:105
#: ../parser_merge.c:93 ../parser_merge.c:97
#: ../parser_merge.c:93
#, c-format
msgid "profile %s: has merged rule %s with conflicting x modifiers\n"
msgstr ""
@@ -426,34 +403,28 @@ msgid "Profile attachment must begin with a '/'."
msgstr ""
#: parser_yacc.y:260 parser_yacc.y:302 parser_yacc.y:348 parser_yacc.y:407
#: parser_yacc.y:446
msgid ""
"Profile names must begin with a '/', namespace or keyword 'profile' or 'hat'."
msgstr ""
#: parser_yacc.y:296 parser_yacc.y:338 parser_yacc.y:384 parser_yacc.y:449
#: parser_yacc.y:487
#, c-format
msgid "Failed to create alias %s -> %s\n"
msgstr ""
#: parser_yacc.y:417 parser_yacc.y:460 parser_yacc.y:506 parser_yacc.y:581
#: ../profile.h:272
msgid "Profile flag chroot_relative conflicts with namespace_relative"
msgstr ""
#: parser_yacc.y:421 parser_yacc.y:464 parser_yacc.y:510 parser_yacc.y:585
#: ../profile.h:276
msgid "Profile flag mediate_deleted conflicts with delegate_deleted"
msgstr ""
#: parser_yacc.y:424 parser_yacc.y:467 parser_yacc.y:513 parser_yacc.y:588
#: ../profile.h:279
msgid "Profile flag attach_disconnected conflicts with no_attach_disconnected"
msgstr ""
#: parser_yacc.y:427 parser_yacc.y:470 parser_yacc.y:516 parser_yacc.y:591
#: ../profile.h:282
msgid "Profile flag chroot_attach conflicts with chroot_no_attach"
msgstr ""
@@ -462,13 +433,12 @@ msgid "Profile flag 'debug' is no longer valid."
msgstr ""
#: parser_yacc.y:463 parser_yacc.y:506 parser_yacc.y:552 parser_yacc.y:629
#: ../profile.h:220
#, c-format
msgid "Invalid profile flag: %s."
msgstr ""
#: parser_yacc.y:498 parser_yacc.y:520 parser_yacc.y:548 parser_yacc.y:594
#: parser_yacc.y:673 parser_yacc.y:687
#: parser_yacc.y:673
msgid "Assert: `rule' returned NULL."
msgstr ""
@@ -494,66 +464,55 @@ msgid "Assert: `network_rule' return invalid protocol."
msgstr ""
#: parser_yacc.y:649 parser_yacc.y:696 parser_yacc.y:786 parser_yacc.y:867
#: parser_yacc.y:776
msgid "Assert: `change_profile' returned NULL."
msgstr ""
#: parser_yacc.y:680 parser_yacc.y:720 parser_yacc.y:810 parser_yacc.y:905
#: parser_yacc.y:814
msgid "Assert: 'hat rule' returned NULL."
msgstr ""
#: parser_yacc.y:689 parser_yacc.y:729 parser_yacc.y:819 parser_yacc.y:914
#: parser_yacc.y:823
msgid "Assert: 'local_profile rule' returned NULL."
msgstr ""
#: parser_yacc.y:824 parser_yacc.y:885 parser_yacc.y:992 parser_yacc.y:1077
#: ../cond_expr.cc:36
#, c-format
msgid "Unset boolean variable %s used in if-expression"
msgstr ""
#: parser_yacc.y:882 parser_yacc.y:986 parser_yacc.y:1092 parser_yacc.y:1181
#: parser_yacc.y:1083
msgid "unsafe rule missing exec permissions"
msgstr ""
#: parser_yacc.y:901 parser_yacc.y:954 parser_yacc.y:1060 parser_yacc.y:1148
#: parser_yacc.y:1050
msgid "subset can only be used with link rules."
msgstr ""
#: parser_yacc.y:903 parser_yacc.y:956 parser_yacc.y:1062 parser_yacc.y:1150
#: parser_yacc.y:1052
msgid "link and exec perms conflict on a file rule using ->"
msgstr ""
#: parser_yacc.y:905 parser_yacc.y:958 parser_yacc.y:1064 parser_yacc.y:1152
#: parser_yacc.y:1054
msgid "link perms are not allowed on a named profile transition.\n"
msgstr ""
#: parser_yacc.y:921 parser_yacc.y:1003 parser_yacc.y:1109 parser_yacc.y:1198
#: parser_yacc.y:1100
#, c-format
msgid "missing an end of line character? (entry: %s)"
msgstr ""
#: parser_yacc.y:975 parser_yacc.y:985 parser_yacc.y:1057 parser_yacc.y:1067
#: parser_yacc.y:1145 parser_yacc.y:1155 parser_yacc.y:1234 parser_yacc.y:1244
#: ../network.cc:484
msgid "Invalid network entry."
msgstr ""
#: parser_yacc.y:1039 parser_yacc.y:1048 parser_yacc.y:1254 parser_yacc.y:1510
#: parser_yacc.y:1617 parser_yacc.y:1644
#: parser_yacc.y:1617
#, c-format
msgid "Invalid capability %s."
msgstr ""
#: parser_yacc.y:1066 parser_yacc.y:1269 parser_yacc.y:1525 parser_yacc.y:1637
#: parser_yacc.y:1664
#, c-format
msgid "AppArmor parser error for %s%s%s at line %d: %s\n"
msgstr ""
@@ -601,13 +560,13 @@ msgid "%s: Unable to parse input line '%s'\n"
msgstr ""
#: ../parser_regex.c:397 ../parser_regex.c:405 ../parser_regex.c:421
#: ../parser_regex.c:487 ../parser_regex.c:491
#: ../parser_regex.c:487
#, c-format
msgid "%s: Invalid profile name '%s' - bad regular expression\n"
msgstr ""
#: ../parser_policy.c:202 ../parser_policy.c:402 ../parser_policy.c:375
#: ../parser_policy.c:383 ../parser_policy.c:231
#: ../parser_policy.c:383
#, c-format
msgid "ERROR merging rules for profile %s, failed to load\n"
msgstr ""
@@ -621,19 +580,19 @@ msgid ""
msgstr ""
#: ../parser_policy.c:279 ../parser_policy.c:359 ../parser_policy.c:332
#: ../parser_policy.c:340 ../parser_policy.c:193
#: ../parser_policy.c:340
#, c-format
msgid "ERROR processing regexs for profile %s, failed to load\n"
msgstr ""
#: ../parser_policy.c:306 ../parser_policy.c:389 ../parser_policy.c:362
#: ../parser_policy.c:370 ../parser_policy.c:218
#: ../parser_policy.c:370
#, c-format
msgid "ERROR expanding variables for profile %s, failed to load\n"
msgstr ""
#: ../parser_policy.c:390 ../parser_policy.c:382 ../parser_policy.c:355
#: ../parser_policy.c:363 ../profile.cc:366
#: ../parser_policy.c:363
#, c-format
msgid "ERROR adding hat access rule for profile %s\n"
msgstr ""
@@ -663,7 +622,7 @@ msgstr ""
msgid "%s: Errors found in combining rules postprocessing. Aborting.\n"
msgstr ""
#: parser_lex.l:180 parser_lex.l:186 parser_lex.l:187 parser_lex.l:211
#: parser_lex.l:180 parser_lex.l:186 parser_lex.l:187
#, c-format
msgid "Could not process include directory '%s' in '%s'"
msgstr ""
@@ -675,8 +634,6 @@ msgstr ""
#: ../parser_main.c:1115 ../parser_main.c:1132 ../parser_main.c:1024
#: ../parser_main.c:1041 ../parser_main.c:1332 ../parser_main.c:1354
#: ../parser_misc.c:280 ../parser_misc.c:299 ../parser_misc.c:308
#: ../parser_main.c:1511 ../parser_main.c:1535 ../parser_misc.c:310
#: ../parser_misc.c:329 ../parser_misc.c:338
msgid "Out of memory"
msgstr ""
@@ -725,20 +682,20 @@ msgstr ""
msgid "owner prefix not allow on capability rules"
msgstr ""
#: parser_yacc.y:1357 parser_yacc.y:1613 parser_yacc.y:1722 parser_yacc.y:1724
#: parser_yacc.y:1357 parser_yacc.y:1613 parser_yacc.y:1722
#, c-format
msgid "invalid mount conditional %s%s"
msgstr ""
#: parser_yacc.y:1374 parser_yacc.y:1628 parser_yacc.y:1737 parser_yacc.y:1739
#: parser_yacc.y:1374 parser_yacc.y:1628 parser_yacc.y:1737
msgid "bad mount rule"
msgstr ""
#: parser_yacc.y:1381 parser_yacc.y:1635 parser_yacc.y:1744 parser_yacc.y:1746
#: parser_yacc.y:1381 parser_yacc.y:1635 parser_yacc.y:1744
msgid "mount point conditions not currently supported"
msgstr ""
#: parser_yacc.y:1398 parser_yacc.y:1650 parser_yacc.y:1759 parser_yacc.y:1761
#: parser_yacc.y:1398 parser_yacc.y:1650 parser_yacc.y:1759
#, c-format
msgid "invalid pivotroot conditional '%s'"
msgstr ""
@@ -755,13 +712,11 @@ msgid "%s: Regex grouping error: Exceeded maximum nesting of {}\n"
msgstr ""
#: ../parser_policy.c:366 ../parser_policy.c:339 ../parser_policy.c:347
#: ../parser_policy.c:200
#, c-format
msgid "ERROR processing policydb rules for profile %s, failed to load\n"
msgstr ""
#: ../parser_policy.c:396 ../parser_policy.c:369 ../parser_policy.c:377
#: ../parser_policy.c:225
#, c-format
msgid "ERROR replacing aliases for profile %s, failed to load\n"
msgstr ""
@@ -786,7 +741,7 @@ msgstr ""
msgid "Internal: unexpected %s mode character '%c' in input"
msgstr ""
#: ../parser_misc.c:599 ../parser_misc.c:792 ../parser_misc.c:823
#: ../parser_misc.c:599 ../parser_misc.c:792
#, c-format
msgid "Internal error generated invalid %s perm 0x%x\n"
msgstr ""
@@ -811,16 +766,16 @@ msgstr ""
msgid "owner prefix not allowed on unix rules"
msgstr ""
#: parser_yacc.y:794 parser_yacc.y:885 ../all_rule.cc:141
#: parser_yacc.y:794 parser_yacc.y:885
msgid "owner prefix not allowed on capability rules"
msgstr ""
#: parser_yacc.y:1293 parser_yacc.y:1377 parser_yacc.y:1282
#: parser_yacc.y:1293 parser_yacc.y:1377
#, c-format
msgid "dbus rule: invalid conditional group %s=()"
msgstr ""
#: parser_yacc.y:1371 parser_yacc.y:1455 parser_yacc.y:1360
#: parser_yacc.y:1371 parser_yacc.y:1455
#, c-format
msgid "unix rule: invalid conditional group %s=()"
msgstr ""
@@ -830,183 +785,183 @@ msgstr ""
msgid "%s: Regex error: trailing '\\' escape character\n"
msgstr ""
#: ../parser_common.c:112 ../parser_common.c:134
#: ../parser_common.c:112
#, c-format
msgid "%s from %s (%s%sline %d): %s"
msgstr ""
#: ../parser_common.c:113 ../parser_common.c:135
#: ../parser_common.c:113
msgid "Warning converted to Error"
msgstr ""
#: ../parser_common.c:113 ../parser_common.c:135
#: ../parser_common.c:113
msgid "Warning"
msgstr ""
#: ../parser_interface.c:524 ../parser_interface.c:618
#: ../parser_interface.c:524
#, c-format
msgid "Unable to open stdout - %s\n"
msgstr ""
#: ../parser_interface.c:533 ../parser_interface.c:627
#: ../parser_interface.c:533
#, c-format
msgid "Unable to open output file - %s\n"
msgstr ""
#: parser_lex.l:326 parser_lex.l:363
#: parser_lex.l:326
msgid "Failed to process filename\n"
msgstr ""
#: parser_lex.l:720 parser_lex.l:799
#: parser_lex.l:720
#, c-format
msgid "Lexer found unexpected character: '%s' (0x%x) in state: %s"
msgstr ""
#: ../parser_main.c:915 ../parser_main.c:1002
#: ../parser_main.c:915
#, c-format
msgid "Unable to print the cache directory: %m\n"
msgstr ""
#: ../parser_main.c:951 ../parser_main.c:1038
#: ../parser_main.c:951
#, c-format
msgid "Error: Could not load profile %s: %s\n"
msgstr ""
#: ../parser_main.c:961 ../parser_main.c:1048
#: ../parser_main.c:961
#, c-format
msgid "Error: Could not replace profile %s: %s\n"
msgstr ""
#: ../parser_main.c:966 ../parser_main.c:1053
#: ../parser_main.c:966
#, c-format
msgid "Error: Invalid load option specified: %d\n"
msgstr ""
#: ../parser_main.c:1077 ../parser_main.c:1166
#: ../parser_main.c:1077
#, c-format
msgid "Could not get cachename for '%s'\n"
msgstr ""
#: ../parser_main.c:1434 ../parser_main.c:1644
#: ../parser_main.c:1434
msgid "Kernel features abi not found"
msgstr ""
#: ../parser_main.c:1438 ../parser_main.c:1648
#: ../parser_main.c:1438
msgid "Failed to add kernel capabilities to known capabilities set"
msgstr ""
#: ../parser_main.c:1465 ../parser_main.c:1675
#: ../parser_main.c:1465
#, c-format
msgid "Failed to clear cache files (%s): %s\n"
msgstr ""
#: ../parser_main.c:1474 ../parser_main.c:1684
#: ../parser_main.c:1474
msgid ""
"The --create-cache-dir option is deprecated. Please use --write-cache.\n"
msgstr ""
#: ../parser_main.c:1479 ../parser_main.c:1689
#: ../parser_main.c:1479
#, c-format
msgid "Failed setting up policy cache (%s): %s\n"
msgstr ""
#: ../parser_misc.c:904 ../parser_misc.c:935
#: ../parser_misc.c:904
#, c-format
msgid "Namespace not terminated: %s\n"
msgstr ""
#: ../parser_misc.c:906 ../parser_misc.c:937
#: ../parser_misc.c:906
#, c-format
msgid "Empty namespace: %s\n"
msgstr ""
#: ../parser_misc.c:908 ../parser_misc.c:939
#: ../parser_misc.c:908
#, c-format
msgid "Empty named transition profile name: %s\n"
msgstr ""
#: ../parser_misc.c:910 ../parser_misc.c:941
#: ../parser_misc.c:910
#, c-format
msgid "Unknown error while parsing label: %s\n"
msgstr ""
#: parser_yacc.y:306 parser_yacc.y:334
#: parser_yacc.y:306
msgid "Failed to setup default policy feature abi"
msgstr ""
#: parser_yacc.y:308 parser_yacc.y:336
#: parser_yacc.y:308
#, c-format
msgid ""
"%s: File '%s' missing feature abi, falling back to default policy feature "
"abi\n"
msgstr ""
#: parser_yacc.y:313 parser_yacc.y:341
#: parser_yacc.y:313
msgid "Failed to add policy capabilities to known capabilities set"
msgstr ""
#: parser_yacc.y:350 parser_yacc.y:386
#: parser_yacc.y:350
msgid "Profile names must begin with a '/' or a namespace"
msgstr ""
#: parser_yacc.y:372 parser_yacc.y:408
#: parser_yacc.y:372
msgid "Profile attachment must begin with a '/' or variable."
msgstr ""
#: parser_yacc.y:375 parser_yacc.y:411
#: parser_yacc.y:375
#, c-format
msgid "profile id: invalid conditional group %s=()"
msgstr ""
#: parser_yacc.y:404 parser_yacc.y:443
#: parser_yacc.y:404
msgid ""
"The use of file paths as profile names is deprecated. See man apparmor.d for "
"more information\n"
msgstr ""
#: parser_yacc.y:573 ../profile.h:264
#: parser_yacc.y:573
#, c-format
msgid "Profile flag '%s' conflicts with '%s'"
msgstr ""
#: parser_yacc.y:954 parser_yacc.y:862
#: parser_yacc.y:954
msgid "RLIMIT 'cpu' no units specified using default units of seconds\n"
msgstr ""
#: parser_yacc.y:966 parser_yacc.y:874
#: parser_yacc.y:966
msgid ""
"RLIMIT 'rttime' no units specified using default units of microseconds\n"
msgstr ""
#: parser_yacc.y:1582 parser_yacc.y:1609
#: parser_yacc.y:1582
msgid "Exec condition is required when unsafe or safe keywords are present"
msgstr ""
#: parser_yacc.y:1584 parser_yacc.y:1611
#: parser_yacc.y:1584
msgid "Exec condition must begin with '/'."
msgstr ""
#: parser_yacc.y:1643 parser_yacc.y:1670
#: parser_yacc.y:1643
#, c-format
msgid "AppArmor parser error at line %d: %s\n"
msgstr ""
#: parser_yacc.y:1790 parser_yacc.y:1797
#: parser_yacc.y:1790
#, c-format
msgid "Could not open '%s': %m"
msgstr ""
#: parser_yacc.y:1795 parser_yacc.y:1802
#: parser_yacc.y:1795
#, c-format
msgid "fstat failed for '%s': %m"
msgstr ""
#: parser_yacc.y:1809 parser_yacc.y:1816
#: parser_yacc.y:1809
#, c-format
msgid "failed to find features abi '%s': %m"
msgstr ""
#: parser_yacc.y:1813 parser_yacc.y:1821
#: parser_yacc.y:1813
#, c-format
msgid ""
"%s: %s features abi '%s' differs from policy declared feature abi, using the "
@@ -1018,124 +973,7 @@ msgstr ""
msgid "%s: Invalid glob type %d\n"
msgstr ""
#: ../parser_regex.c:693 ../parser_regex.c:721
#: ../parser_regex.c:693
#, c-format
msgid "The current kernel does not support stacking of named transitions: %s\n"
msgstr ""
#: ../parser_interface.c:76
#, c-format
msgid ""
"%s: Permission denied. You need policy admin privileges to manage profiles.\n"
"\n"
msgstr ""
#: ../parser_interface.c:80
#, c-format
msgid ""
"%s: Access denied. You need policy admin privileges to manage profiles.\n"
"\n"
msgstr ""
#: parser_lex.l:653 parser_lex.l:668
msgid "deprecated use of '#include'\n"
msgstr ""
#: ../parser_misc.c:730
#, c-format
msgid "Internal: unexpected perms character '%c' in input"
msgstr ""
#: ../parser_misc.c:799
#, c-format
msgid "Internal: unexpected %s perms character '%c' in input"
msgstr ""
#: ../parser_misc.c:1098
msgid ""
"Invalid perms, in deny rules 'x' must not be preceded by exec qualifier 'i', "
"'p', or 'u'"
msgstr ""
#: ../parser_misc.c:1102
msgid "Invalid perms, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'"
msgstr ""
#: parser_yacc.y:689 parser_yacc.y:716 ../all_rule.cc:105 ../all_rule.cc:134
#, c-format
msgid "%s"
msgstr ""
#: parser_yacc.y:705
msgid "priority is not allowed on rule blocks"
msgstr ""
#: parser_yacc.y:778
msgid "owner conditional not allowed on unix rules"
msgstr ""
#: parser_yacc.y:794
msgid "owner conditional not allowed on capability rules"
msgstr ""
#: parser_yacc.y:1119 parser_yacc.y:1132 parser_yacc.y:1146
#, c-format
msgid "network rule: invalid conditional group %s=()"
msgstr ""
#: parser_yacc.y:1827
msgid "failed features abi not set but include cache skipped\n"
msgstr ""
#: ../parser_variable.c:295
msgid "attach_disconnected_path value must begin with a /"
msgstr ""
#: ../mount.cc:897
msgid ""
"The use of source as mount point for propagation type flags is deprecated.\n"
msgstr ""
#: ../network.h:200
msgid "priority prefix not allowed on network rules"
msgstr ""
#: ../network.h:204
msgid "owner prefix not allowed on network rules"
msgstr ""
#: ../profile.h:287
#, c-format
msgid ""
"Profile flag attach_disconnected set to conflicting values: '%s' and '%s'"
msgstr ""
#: ../profile.h:297
#, c-format
msgid "Profile flag kill.signal set to conflicting values: '%d' and '%d'"
msgstr ""
#: ../profile.h:307
#, c-format
msgid "Profile flag error set to conflicting values: '%s' and '%s'"
msgstr ""
#: ../userns.h:36
msgid "owner prefix not allowed on userns rules"
msgstr ""
#: ../mqueue.h:106
msgid "owner prefix not allowed on mqueue rules"
msgstr ""
#: ../io_uring.h:42
msgid "owner prefix not allowed on io_uring rules"
msgstr ""
#: ../all_rule.h:36
msgid "priority prefix not allowed on all rules"
msgstr ""
#: ../all_rule.h:40
msgid "owner prefix not allowed on all rules"
msgstr ""

View File

@@ -226,13 +226,13 @@ static bool add_proc_access(Profile *prof, const char *rule)
char *buffer = strdup("/proc/*/attr/apparmor/");
if (!buffer) {
PERROR("Memory allocation error\n");
return false;
return FALSE;
}
new_ent = new_entry(buffer, AA_MAY_READ, NULL);
if (!new_ent) {
free(buffer);
PERROR("Memory allocation error\n");
return false;
return FALSE;
}
add_entry_to_policy(prof, new_ent);
@@ -240,13 +240,13 @@ static bool add_proc_access(Profile *prof, const char *rule)
buffer = strdup("/sys/module/apparmor/parameters/enabled");
if (!buffer) {
PERROR("Memory allocation error\n");
return false;
return FALSE;
}
new_ent = new_entry(buffer, AA_MAY_READ, NULL);
if (!new_ent) {
free(buffer);
PERROR("Memory allocation error\n");
return false;
return FALSE;
}
add_entry_to_policy(prof, new_ent);
@@ -254,17 +254,17 @@ static bool add_proc_access(Profile *prof, const char *rule)
buffer = strdup(rule);
if (!buffer) {
PERROR("Memory allocation error\n");
return false;
return FALSE;
}
new_ent = new_entry(buffer, AA_MAY_WRITE, NULL);
if (!new_ent) {
free(buffer);
PERROR("Memory allocation error\n");
return false;
return FALSE;
}
add_entry_to_policy(prof, new_ent);
return true;
return TRUE;
}
#define CHANGEPROFILE_PATH "/proc/*/attr/{apparmor/,}{current,exec}"

View File

@@ -363,7 +363,7 @@ public:
struct cond_entry_list xattrs;
/* char *sub_name; */ /* subdomain name or NULL */
/* bool default_deny; */
/* int default_deny; */ /* TRUE or FALSE */
bool local;
Profile *parent;

View File

@@ -253,7 +253,7 @@ remove_profiles() {
retval=0
# We filter child profiles as removing the parent will remove
# the children
sed -e "s/ (\(enforce\|complain\|prompt\|kill\|unconfined\))$//" "$SFS_MOUNTPOINT/profiles" | \
sed -e "s/ (\(enforce\|complain\|unconfined\))$//" "$SFS_MOUNTPOINT/profiles" | \
LC_COLLATE=C sort | grep -v // | {
while read -r profile ; do
printf "%s" "$profile" > "$SFS_MOUNTPOINT/.remove"

View File

@@ -182,8 +182,6 @@ public:
{
bool output = true;
if (priority != 0)
os << "priority=" << priority << " ";
switch (audit) {
case AUDIT_FORCE:
os << "audit";

View File

@@ -23,7 +23,7 @@
#include <iomanip>
#include <string>
#include <sstream>
#include <unordered_map>
#include <map>
#include "parser.h"
#include "profile.h"
@@ -35,7 +35,7 @@
#define MAXRT_SIG 32 /* Max RT above MINRT_SIG */
/* Signal names mapped to and internal ordering */
static unordered_map<string, int> signal_map = {
static struct signal_map { const char *name; int num; } signal_map[] = {
{"hup", 1},
{"int", 2},
{"quit", 3},
@@ -55,8 +55,7 @@ static unordered_map<string, int> signal_map = {
{"chld", 17},
{"cont", 18},
{"stop", 19},
{"stp", 20}, // parser's previous name for SIGTSTP
{"tstp", 20},
{"stp", 20},
{"ttin", 21},
{"ttou", 22},
{"urg", 23},
@@ -65,12 +64,14 @@ static unordered_map<string, int> signal_map = {
{"vtalrm", 26},
{"prof", 27},
{"winch", 28},
{"io", 29}, // SIGIO == SIGPOLL
{"poll", 29},
{"io", 29},
{"pwr", 30},
{"sys", 31},
{"emt", 32},
{"exists", 35},
/* terminate */
{NULL, 0}
};
/* this table is ordered post sig_map[sig] mapping */
@@ -95,7 +96,7 @@ static const char *const sig_names[MAXMAPPED_SIG + 1] = {
"chld",
"cont",
"stop",
"tstp",
"stp",
"ttin",
"ttou",
"urg",
@@ -104,7 +105,7 @@ static const char *const sig_names[MAXMAPPED_SIG + 1] = {
"vtalrm",
"prof",
"winch",
"io", // SIGIO == SIGPOLL
"io",
"pwr",
"sys",
"emt",
@@ -129,14 +130,12 @@ int find_signal_mapping(const char *sig)
return -1;
return MINRT_SIG + n;
} else {
// Can't use string_view because that requires C++17
auto sigmap = signal_map.find(string(sig));
if (sigmap != signal_map.end()) {
return sigmap->second;
} else {
return -1;
for (int i = 0; signal_map[i].name; i++) {
if (strcmp(sig, signal_map[i].name) == 0)
return signal_map[i].num;
}
}
return -1;
}
void signal_rule::extract_sigs(struct value_list **list)

View File

@@ -6,7 +6,7 @@ PARSER_BIN=apparmor_parser
PARSER=$(PARSER_DIR)/$(PARSER_BIN)
# parser.conf to use in tests. Note that some test scripts have the parser options hardcoded, so passing PARSER_ARGS=... is not enough to override it.
PARSER_ARGS=--config-file=./parser.conf
PROVE_ARG=-f --directives -j2
PROVE_ARG=-f --directives
ifeq ($(VERBOSE),1)
PROVE_ARG+=-v
@@ -37,11 +37,6 @@ error_output: $(PARSER)
parser_sanity: $(PARSER) gen_xtrans gen_dbus
$(Q)LANG=C APPARMOR_PARSER="$(PARSER)" ${PROVE} ${PROVE_ARG} ${TESTS}
# use this target for faster manual testing if you don't want/need to test all the profiles generated by gen-*.py
parser_sanity-no-gen: clean $(PARSER)
@echo WARNING: not creating the profiles using the gen-*.py scripts
$(Q)LANG=C APPARMOR_PARSER="$(PARSER)" ${PROVE} ${PROVE_ARG} ${TESTS}
caching: $(PARSER)
LANG=C ./caching.py -p "$(PARSER)" $(PYTEST_ARG)

View File

@@ -725,7 +725,7 @@ else
#this one may not be true in the future depending on if the compiled profile
#is explicitly including deny permissions for dynamic composition
verify_binary_equality "'$p1'x'$p2' Deny of ungranted perm" \
verify_binary_inequality "'$p1'x'$p2' Deny of ungranted perm" \
"/t { $p1 /foo/[abc] r, audit deny /foo/b w, }" \
"/t { $p2 /foo/[abc] r, }"
fi
@@ -823,7 +823,7 @@ if { priority_lt "$p1" "" && priority_lt "$p2" "" ; } ||
"/t { $p1 owner /proc/[0-9]*/attr/{apparmor/,}current a, ^test { $p1 owner /proc/[0-9]*/attr/{apparmor/,}current a, /f r, }}" \
"/t { $p2 owner /proc/[0-9]*/attr/{apparmor/,}current w, ^test { $p2 owner /proc/[0-9]*/attr/{apparmor/,}current w, /f r, }}"
else
verify_binary_equality "'$p1'x'$p2' change_hat rules automatically inserted"\
verify_binary_inequality "'$p1'x'$p2' change_hat rules automatically inserted"\
"/t { $p1 owner /proc/[0-9]*/attr/{apparmor/,}current a, ^test { $p1 owner /proc/[0-9]*/attr/{apparmor/,}current a, /f r, }}" \
"/t { $p2 owner /proc/[0-9]*/attr/{apparmor/,}current w, ^test { $p2 owner /proc/[0-9]*/attr/{apparmor/,}current w, /f r, }}"
fi

View File

@@ -78,7 +78,7 @@ APPARMOR_PARSER="${APPARMOR_PARSER:-../apparmor_parser}"
# {a} (0x 40030/0/0/0)
echo -n "Minimize profiles basic perms "
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*}(.*)$')" -ne 6 ] ; then
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 6 ] ; then
echo "failed"
exit 1;
fi
@@ -93,7 +93,7 @@ echo "ok"
# {9} (0x 12804a/0/2800a/0)
# {c} (0x 40030/0/0/0)
echo -n "Minimize profiles audit perms "
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*}(.*)$')" -ne 6 ] ; then
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 6 ] ; then
echo "failed"
exit 1;
fi
@@ -112,7 +112,7 @@ echo "ok"
# {c} (0x 40030/0/0/0)
echo -n "Minimize profiles deny perms "
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*}(.*)$')" -ne 6 ] ; then
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 6 ] ; then
echo "failed"
exit 1;
fi
@@ -130,7 +130,7 @@ echo "ok"
# {c} (0x 40030/0/0/0)
echo -n "Minimize profiles audit deny perms "
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -O filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*}(.*)$')" -ne 5 ] ; then
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -O filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 5 ] ; then
echo "failed"
exit 1;
fi
@@ -155,7 +155,7 @@ echo "ok"
## NOTE: change count from 6 to 7 when extend perms is not dependent on
## prompt rules being present
echo -n "Minimize profiles extended no-filter audit deny perms "
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.extended-perms-no-policydb -QT -O minimize -O no-filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*}(.*)$')" -ne 7 ] ; then
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.extended-perms-no-policydb -QT -O minimize -O no-filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 7 ] ; then
echo "failed"
exit 1;
fi
@@ -173,7 +173,7 @@ echo "ok"
# {2} (0x 4/0//0/0/0) <- from policydb still showing up bug
echo -n "Minimize profiles extended filter audit deny perms "
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.extended-perms-no-policydb -QT -O minimize -O filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*}(.*)$')" -ne 6 ] ; then
if [ "$(echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ${APPARMOR_PARSER} -M features_files/features.extended-perms-no-policydb -QT -O minimize -O filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 6 ] ; then
echo "failed"
exit 1;
fi
@@ -208,7 +208,7 @@ echo "ok"
#
echo -n "Minimize profiles xtrans "
if [ "$(echo "/t { /b px, /* Pixr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*}(.*)$')" -ne 3 ] ; then
if [ "$(echo "/t { /b px, /* Pixr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 3 ] ; then
echo "failed"
exit 1;
fi
@@ -216,7 +216,7 @@ echo "ok"
# same test as above + audit
echo -n "Minimize profiles audit xtrans "
if [ "$(echo "/t { /b px, audit /* Pixr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*}(.*)$')" -ne 3 ] ; then
if [ "$(echo "/t { /b px, audit /* Pixr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 3 ] ; then
echo "failed"
exit 1;
fi
@@ -229,7 +229,7 @@ echo "ok"
# {3} (0x 0/fe17f85/0/14005)
echo -n "Minimize profiles deny xtrans "
if [ "$(echo "/t { /b px, deny /* xr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*}(.*)$')" -ne 1 ] ; then
if [ "$(echo "/t { /b px, deny /* xr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 1 ] ; then
echo "failed"
exit 1;
fi
@@ -241,7 +241,7 @@ echo "ok"
# {3} (0x 0/fe17f85/0/0)
echo -n "Minimize profiles audit deny xtrans "
if [ "$(echo "/t { /b px, audit deny /* xr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -O no-filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*}(.*)$')" -ne 0 ] ; then
if [ "$(echo "/t { /b px, audit deny /* xr, /a Cx -> foo, }" | ${APPARMOR_PARSER} -M features_files/features.nopolicydb -QT -O minimize -O no-filter-deny -D dfa-states 2>&1 | grep -v '<==' | grep -c '^{.*} 0 (.*)$')" -ne 0 ] ; then
echo "failed"
exit 1;
fi

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION network port range conditional test - missing end of range
#=EXRESULT FAIL
#
/usr/bin/foo {
network port=22-,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION network port range conditional test - missing end of range
#=EXRESULT FAIL
#
/usr/bin/foo {
network peer=(port=2222-),
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION network port range conditional test - spaces in range not allowed
#=EXRESULT FAIL
#
/usr/bin/foo {
network port=22 - 443,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION network port range conditional test - spaces in range not allowed
#=EXRESULT FAIL
#
/usr/bin/foo {
network peer=(port=22 - 443),
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION network port range conditional test - invalid "--"
#=EXRESULT FAIL
#
/usr/bin/foo {
network port=22--443,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION network port range conditional test - invalid "--"
#=EXRESULT FAIL
#
/usr/bin/foo {
network peer=(port=22--443),
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION network port range conditional test - 3 items in range
#=EXRESULT FAIL
#
/usr/bin/foo {
network port=22-443-1024,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION network port range conditional test - 3 items in range
#=EXRESULT FAIL
#
/usr/bin/foo {
network peer=(port=22-443-1024),
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION network port range conditional test - additional spaces
#=EXRESULT PASS
#
/usr/bin/foo {
network port = 22-443 ,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION network port range conditional test - additional spaces
#=EXRESULT PASS
#
/usr/bin/foo {
network peer=( port = 22-443 ),
}

View File

@@ -98,11 +98,12 @@ class AATestTemplate(unittest.TestCase, metaclass=AANoCleanupMetaClass):
except OSError as e:
return 127, str(e), ''
timeout_communicate = TimeoutFunction(sp.communicate, timeout)
out, outerr = (None, None)
try:
out, outerr = sp.communicate(input, timeout)
out, outerr = timeout_communicate(input)
rc = sp.returncode
except subprocess.TimeoutExpired:
except TimeoutFunctionException:
sp.terminate()
outerr = 'test timed out, killed'
rc = TIMEOUT_ERROR_CODE
@@ -116,6 +117,31 @@ class AATestTemplate(unittest.TestCase, metaclass=AANoCleanupMetaClass):
return rc, out, outerr
# Timeout handler using alarm() from John P. Speno's Pythonic Avocado
class TimeoutFunctionException(Exception):
"""Exception to raise on a timeout"""
class TimeoutFunction:
def __init__(self, function, timeout):
self.timeout = timeout
self.function = function
def handle_timeout(self, signum, frame):
raise TimeoutFunctionException()
def __call__(self, *args, **kwargs):
old = signal.signal(signal.SIGALRM, self.handle_timeout)
signal.alarm(self.timeout)
try:
result = self.function(*args, **kwargs)
finally:
signal.signal(signal.SIGALRM, old)
signal.alarm(0)
return result
def filesystem_time_resolution():
"""detect whether the filesystem stores subsecond timestamps"""

View File

@@ -154,12 +154,11 @@ check-logprof: test-dependencies
.PHONY: check-abstractions.d
check-abstractions.d:
@echo "*** Checking if all abstractions (with a few exceptions) contain 'include if exists <abstractions/*.d>' and 'abi <abi/4.0>,'"
@echo "*** Checking if all abstractions (with a few exceptions) contain 'include if exists <abstractions/*.d>'"
$(Q)for file in $$(find ${ABSTRACTIONS_SOURCE} ${EXTRAS_ABSTRACTIONS_SOURCE} -maxdepth 1 -type f) ; do \
case "$${file}" in */ubuntu-browsers | */ubuntu-helpers) continue ;; esac ; \
include="include if exists <abstractions/$$(basename $${file}).d>" ; \
grep -q "^ $${include}\$$" $${file} || { echo "$${file} does not contain '$${include}'"; exit 1; } ; \
grep -q "^ *abi <abi/4.0>," $${file} || { echo "$${file} does not contain 'abi <abi/4.0>,'"; exit 1; } ; \
done
.PHONY: check-tunables.d
@@ -173,10 +172,9 @@ check-tunables.d:
.PHONY: check-local
check-local:
@echo "*** Checking if all profiles contain 'include if exists <local/*>' and 'abi <abi/4.0>,'"
@echo "*** Checking if all profiles contain 'include if exists <local/*>'"
$(Q)for file in $$(find ${PROFILES_SOURCE} ${EXTRAS_SOURCE} -maxdepth 1 -type f) ; do \
case "$${file}" in */README) continue ;; esac ; \
include="include if exists <local/$$(basename $${file})>" ; \
grep -q "^ *$${include}\$$" $${file} || { echo "$${file} does not contain '$${include}'"; exit 1; } ; \
grep -q "^ *abi <abi/4.0>," $${file} || { echo "$${file} does not contain 'abi <abi/4.0>,'"; exit 1; } ; \
done

View File

@@ -10,8 +10,6 @@
#
# ------------------------------------------------------------------
abi <abi/4.0>,
# Note: executing groff and nroff themself is not included in this abstraction
# so that you can choose to ix, Px or Cx them in your profile

View File

@@ -42,6 +42,9 @@
# have open
@{run}/nscd/db* mix,
# make libnss-libvirt name resolution work.
/var/lib/libvirt/dnsmasq/* r,
# make libnss-libvirt name resolution work.
/var/lib/libvirt/dnsmasq/ r,
/var/lib/libvirt/dnsmasq/*.status r,

View File

@@ -13,19 +13,19 @@
abi <abi/4.0>,
/{usr/,}bin/ r,
/{usr/,}bin/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]} r,
/{usr/,}bin/python{2.[4-7],3,3.[0-9],3.1[0-9]} r,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]}/**.{pyc,so,so.*[0-9]} mr,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]}/**.{egg,py,pth} r,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]}/{site,dist}-packages/ r,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]}/{site,dist}-packages/**/ r,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]}/{site,dist}-packages/*.dist-info/{METADATA,namespace_packages.txt} r,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]}/{site,dist}-packages/*.VERSION r,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]}/{site,dist}-packages/*.egg-info/PKG-INFO r,
/usr/{local/,}lib{,32,64}/python{3.[0-9],3.[1-9][0-9]}/lib-dynload/*.so mr,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.1[0-9]}/**.{pyc,so,so.*[0-9]} mr,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.1[0-9]}/**.{egg,py,pth} r,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.1[0-9]}/{site,dist}-packages/ r,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.1[0-9]}/{site,dist}-packages/**/ r,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.1[0-9]}/{site,dist}-packages/*.dist-info/{METADATA,namespace_packages.txt} r,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.1[0-9]}/{site,dist}-packages/*.VERSION r,
/usr/{local/,}lib{,32,64}/python{2.[4-7],3,3.[0-9],3.1[0-9]}/{site,dist}-packages/*.egg-info/PKG-INFO r,
/usr/{local/,}lib{,32,64}/python3.{1,}[0-9]/lib-dynload/*.so mr,
# Site-wide configuration
/etc/python{2.[4-7],3.[0-9],3.[1-9][0-9]}/** r,
/etc/python{2.[4-7],3.[0-9],3.1[0-9]}/** r,
# shared python paths
/usr/share/{pyshared,pycentral,python-support}/** r,
@@ -38,12 +38,12 @@
/usr/lib/wx/python/*.pth r,
# python build configuration and headers
/usr/include/python{2.[4-7],3.[0-9],3.[1-9][0-9]}*/pyconfig.h r,
/usr/include/python{2.[4-7],3.[0-9],3.1[0-9]}*/pyconfig.h r,
owner @{HOME}/.local/lib/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]}/**.{pyc,so} mr,
owner @{HOME}/.local/lib/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]}/**.{egg,py,pth} r,
owner @{HOME}/.local/lib/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]}/{site,dist}-packages/ r,
owner @{HOME}/.local/lib/python{2.[4-7],3,3.[0-9],3.[1-9][0-9]}/{site,dist}-packages/**/ r,
owner @{HOME}/.local/lib/python{2.[4-7],3,3.[0-9],3.1[0-9]}/**.{pyc,so} mr,
owner @{HOME}/.local/lib/python{2.[4-7],3,3.[0-9],3.1[0-9]}/**.{egg,py,pth} r,
owner @{HOME}/.local/lib/python{2.[4-7],3,3.[0-9],3.1[0-9]}/{site,dist}-packages/ r,
owner @{HOME}/.local/lib/python{2.[4-7],3,3.[0-9],3.1[0-9]}/{site,dist}-packages/**/ r,
# Starting with Python 3.8, you can use the PYTHONPYCACHEPREFIX environment
# variable to define a cache directory for Python.

View File

@@ -1,5 +1,3 @@
abi <abi/4.0>,
profile snap_browsers {
include if exists <abstractions/snap_browsers.d>
include <abstractions/base>

View File

@@ -2,8 +2,6 @@
# LOGPROF-SUGGEST: no
# Author: Daniel Richard G. <skunk@iSKUNK.ORG>
abi <abi/4.0>,
include <abstractions/base>
include <abstractions/freedesktop.org>
include <abstractions/nameservice>

View File

@@ -1,37 +0,0 @@
#------------------------------------------------------------------
# Copyright (C) 2024 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License published by the Free Software Foundation.
#------------------------------------------------------------------
# vim: ft=apparmor
abi <abi/4.0>,
include <tunables/global>
profile tar /usr/bin/tar {
include <abstractions/base>
# used to extract user files as root
capability chown,
# used to compress user files as root
capability dac_override,
capability dac_read_search,
file rwl /**,
# tar can be made to filter archives through an arbitrary program
/{usr{/local,},}/{bin,sbin}/* ix,
/opt/** ix,
# tar can compress/extract files over rsh/ssh
network stream ip=127.0.0.1,
network stream ip=::1,
# Site-specific additions and overrides. See local/README for details.
include if exists <local/tar>
}

View File

@@ -4,7 +4,7 @@
abi <abi/4.0>,
include <tunables/global>
profile toybox /usr/bin/toybox flags=(unconfined) {
profile toybox /bin/toybox flags=(unconfined) {
userns,
# Site-specific additions and overrides. See local/README for details.

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