2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-09-04 16:25:10 +00:00

Compare commits

..

84 Commits

Author SHA1 Message Date
Steve Beattie
b37bd8a1aa Point makefile at correct launchpad branch to generate tarball from. 2015-04-23 12:46:45 -07:00
Christian Boltz
ead71a306a Enable testloops for nosetests
Ensure nosetests sees all tests in the tests[] tuples. This requires
some name changes because nosetests thinks all function names containing
"test" are tests. (A "not a test" docorator would be an alternative, but
that would require some try/except magic to avoid a dependency on nose.)

To avoid nosetests thinks the functions are a test,
- rename setup_all_tests() to setup_all_loops()
- rename regex_test() to _regex_test() (in test-regex_matches.py)

Also add the module_name as parameter to setup_all_loops and always run
it (not only if __name__ == '__main__').

Known issue: nosetests errors out with
    ValueError: no such test method in <class ...>: stub_test
when trying to run a single test generated out of tests[].
(debugging hint: stub_test is the name used in setup_test_loop().)
But that's still an improvement over not seeing those tests at all ;-)


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9.
2015-04-22 22:03:36 +02:00
Christian Boltz
aa45be1c10 Fix crash in serialize_profile_from_old_profiles()
Assume you have a profile like

    /bin/foo {
      /etc/ r,
      network,
      /usr/ r,
    }

(important: there must be be a non-path rule between the two path blocks)

Then run aa-logprof and add another path event. When choosing (V)iew changes,
it will crash with a misleading

  File ".../utils/apparmor/aamode.py", line 205, in split_mode
      other = mode - user
      TypeError: unsupported operand type(s) for -: 'collections.defaultdict' and 'set'

The reason for this is our beloved hasher, which is playing funny games
another time.

The patch wraps the hasher usage with a check for the parent element to
avoid auto-creation of empty childs, which then lead to the above crash.


BTW: This is another issue uncovered by the LibreOffice profile ;-)


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9.
2015-04-16 13:11:39 +02:00
Steve Beattie
c1c5192532 profiles: update postfix-common abstraction
Merge from trunk revision 3012

Update the postfix-common abstraction to cope with signal and unix
socket mediation, update the access to the sasl library locations
in a multiarch compliant way, and allow access to limited bits
of the filesystem paths under which postfix chroots itself to
(/var/spool/postfix/ on Ubuntu).

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: Tyler Hicks <tyhicks@canonical.com>
2015-04-15 23:35:52 -07:00
Christian Boltz
2b9260f27a Fix serialize_profile_from_old_profiles() to not crash on "@{var} +="
serialize_profile_from_old_profiles() calls store_list_var() with an
empty hasher. This fails for "+=" because in this case store_list_var()
expects a non-empty hasher with the variable already defined, and raises
an exception because of the empty hasher.

This patch sets "correct = False" if a "+=" operation appears, which
means the variable will be written in "clean" mode instead.

Adding proper support for "add to variable" needs big changes (like
storing a variable's "history" - where it was initially defined and what
got added where).



Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9.
2015-04-16 02:02:02 +02:00
Christian Boltz
4063647a5f fix handling of adding to variables
the LibreOffice profile uncovered that handling of @{var} += is broken:

  File ".../utils/apparmor/aa.py", line 3272, in store_list_var
    var[list_var] = set(var[list_var] + vlist)
TypeError: unsupported operand type(s) for +: 'set' and 'list'

This patch fixes it:
- change separate_vars() to use and return a set instead of a list
  (FYI: separate_vars() is only called by store_list_var())
- adoptstore_list_var() to expect a set
- remove some old comments in these functions
- explain the less-intuitive parameters of store_list_var()

Also add some tests for separate_vars() and store_list_var().
The tests were developed based on the old code, but not all of them
succeed with the old code.

As usual, the tests uncovered some interesting[tm] behaviour in
separate_vars() (see the XXX comments and tell me what the really
expected behaviour is ;-)


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9
2015-04-16 01:59:10 +02:00
Christian Boltz
b4048cf3de logparser.py: change mask only for path events
Move the code that does the c -> a and d -> w replacement in denied_mask
and requested_mask so that it only runs for path and exec events, but not
for other events (like dbus and ptrace). The validate_log_mode() and
log_str_to_mode() calls are also moved.

Technically, this means moving code from parse_event() to the path
and exec sections in add_event_to_tree().

This also means aa-logprof no longer crashes if it hits a ptrace or
dbus event in the log.

The "if dmask:" and "if rmask:" checks are removed - if a path event
doesn't have these two, it is totally broken and worth a aa-logprof
crash ;-)

Also adjust the parse_event() tests to expect the "raw" mask instead of
a set.

Note: the 2.9 branch doesn't contain test-capability.py, therefore I
skipped this part of the patch for obvious reasons ;-)

This patch fixes
https://bugs.launchpad.net/apparmor/+bug/1426651 and
https://bugs.launchpad.net/apparmor/+bug/1243932


I manually tested that
- c and d log events are still converted to a and w
- aa-logprof handles exec events correctly
- ptrace events no longer crash aa-logprof

Note: add_event_to_tree() is not covered by tests.


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9
2015-04-16 01:53:39 +02:00
Christian Boltz
8a475341e8 utils: simplify serialize_parse_profile_start()
Merge from trunk revision 3001

Change serialize_parse_profile_start() to use parse_profile_start()
instead of using duplicated code.

The behaviour is mostly kept, with the exception that the function is
more strict now and raises exceptions instead of ignoring errors.

In practise, this won't change anything because the profiles are parsed
with parse_profile() (which calls parse_profile_start()) - and that
already errors out.

The tests are updated to match the more strict behaviour.

The next step would be to drop serialize_parse_profile_start()
completely, but this isn't urgent and can/should be done when we have
test coverage for serialize_profile_from_old_profile() one day ;-)

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 19:07:47 -07:00
Christian Boltz
5ca6986b43 utils: fix is_skippable_dir() and add tests
Merge from trunk revision 3000

Fix is_skippable_dir() - the regex also matched things like
/etc/apparmor.d/dont_disable, while it should match on the full
directory name.

Also add some tests based on a real-world aa-logprof run (with "print (path)"
in is_skippable_dir()) and some additional "funny"[tm] dirs.

Needless to say that the tests
        ('dont_disable',                False),
        ('/etc/apparmor.d/cache_foo',   False),
will fail with the old is_skippable_dir().

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 19:00:47 -07:00
Christian Boltz
4b58cf3bc4 utils: replace RE_PROFILE_START
Merge from trunk revision 2999

Replace RE_PROFILE_START with RE_PROFILE_START_2 and adjust all
code sections that used RE_PROFILE_START_2.

The only real change is that test_get_flags_invalid_01 and
test_get_flags_invalid_02 now expect AppArmorException instead of
AppArmorBug.

Acked-by: Steve Beattie <steve@nxnw.org> for trunk
2015-04-13 18:58:25 -07:00
Christian Boltz
a373b4ee93 utils: implement attachment handling after rewriting set_profile_flags()
to use write_header(), and making set_profile_flags
more strict.

Merge from trunk revisions 2996, 2997, and 2998.

Changes in set_profile_flags():
- rewrite set_profile_flags to use parse_profile_start_line() and
  write_header().
- replace the silent failure for non-existing files with a proper
  exception (using lazy programming - the check is done by removing the
  "if os.path.isfile()" check, open_file_read then raises the
  exception ;-)
- comment out regex_hat_flag and the code that was supposed to handle
  hat flags, which were totally broken. We'll need another patch to fix
  it, and we also need to decide if we want to do that because it
  introduces a behaviour change (currently, aa-complain etc. don't
  change hat flags).

The tests for set_profile_flags() are also updated:
- prepend a space to comments because write_header always adds a space
  between '{' and the comment
- remove a test with superfluous quotes that are no longer kept
  (that's
  just a profile cleanup, so dropping that test is the easiest way)
- update test_set_flags_10 and test_set_flags_12 to use the correct
  profile name
- enable the tests for invalid (empty) flags
- update the test for a non-existing file

this patch makes set_profile_flags more strict:
- raise AppArmorBug if newflags contains only whitespace
- raise AppArmorBug if the file doesn't contain the specified profile or
  no profile at all

The tests are adjusted to expect AppArmorBug instead of a silent
failure. Also, some tests are added for profile=None, which means to
change the flags for all profiles in a file.
- test_set_flags_08 is now test_set_flags_invalid_04
- test_set_flags_invalid_03 is changed to only contain one reason for
  a failure, not two ;-)

Finally implement attachment handling

This patch implements attachment handling - aa-logprof now works with
profiles that have an attachment defined, instead of ignoring audit.log
entries for those profiles.

Changes:
- parse_profile_start_line(): remove workaround that merged the
  attachment into the profile name
- parse_profile_data(): store attachment when parsing a profile
- update test_parse_profile_start_03,
  test_serialize_parse_profile_start_03,
  test_set_flags_nochange_09 and some parse_profile_start_line() tests -
  they now expect correct attachment handling

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 18:53:59 -07:00
Christian Boltz
8d5569f20b utils: rewrite parse_profile_start()
Merge from trunk revision 2990

Rewrite parse_profile_start() in aa.py to a more readable version.
The behaviour remains unchanged (and is covered by tests).

Acked-by: Kshitij Gupta <kgupta8592@gmail.com>
2015-04-13 18:49:37 -07:00
Christian Boltz
5390777e45 utils: add tests for RE_PROFILE_START_2 and parse_profile_start_line()
Merge from trunk revision 2989

Also add AANamedRegexTest class that can be used to test a regex with
named match groups.

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 18:47:12 -07:00
Christian Boltz
79240e7ddd utils: test new parameters of write_header()
Merge from trunk revision 2988

Change the write_header tests so that the 'profile_keyword' and
'header_comment' parameters can be (and are) tested:
- add a None for both to the existing tests
- add some tests that come with the profile keyword and/or a comment

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 18:45:05 -07:00
Christian Boltz
494daee246 utils: extend and partially rewrite write_header()
Merge from trunk revision 2987

- add support for prof_data['header_comment'] (comment after '{')
  and prof_data['profile_keyword'] (to force the 'profile' keyword, even
  if it isn't needed) to write_header().
  (set_profile_flags() will be the only user of these two for now)

- fix a crash if depth is not an integer - for example,
      len('   ')/2   # 3 spaces = 1.5
  would cause a crash.
  Also add a test for 1.5 and 1.3 spaces.

- rewrite the handling of flags to avoid we have to maintain two
  different template lines.

- update the tests to set 'profile_keyword' and 'header_comment' to None.
  This avoids big changes in the test code. I'll send another patch that
  makes sure profile_keyword and header_comment are tested ;-)

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 18:41:59 -07:00
Christian Boltz
194cbfa94c utils: add attachment to parse_profile_start() return values
Merge from trunk revision 2986

Add the attachment to the parse_profile_start() and
serialize_parse_profile_start() return values, and adjust the functions
calling the *parse_profile_start() functions to save the attachment in
the "attachment" variable (which isn't used yet).

Also adjust the tests for the added return value.

(Sorry for not getting the resultset right from the beginning!)

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 18:36:42 -07:00
Christian Boltz
9452e1e2af utils: Add support for attachments to write_header()
Merge from trunk revision 2985

Also fix a little bug that added the profile keyword if the path needed
quotes (profile "/foo bar" - but "/foo bar" is enough). This was caused
by a regex that always matched on quoted paths (hint: "/ matches
^[^/] ;-)

Also add some tests with attachments and update the test for the bugfix
mentioned above.

Now the remaining part is to make sure that prof_data['attachment'] gets
set when parsing the profiles :-)

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 18:34:33 -07:00
Christian Boltz
1556f782e3 utils: add tests for set_profile_flags() (and some fun)
Merge from trunk commit 2983

Add various tests for set_profile_flags, and document various
interesting[tm] things I discovered while writing the tests (see
the inline comments for details).

Also adds a read_file() function to common_test.py.

The most interesting[tm] thing I found is:
    regex_hat_flag = re.compile('^([a-z]*)\s+([A-Z]*)\s*(#.*)?$')
which matches various unexpected things - but not a hat :-/
(see mailinglist for all funny details)

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 18:31:53 -07:00
Christian Boltz
7d1ff607fe utils: add and use parse_profile_start_line(); convert
serialize_parse_profile_start() to use parse_profile_start_line();
update test-aa.py to match parse_profile_start() and
get_profile_flags() changes

Merge from trunk commits 2978, 2979, and 2982

Add the parse_profile_start_line() function to regex.py, which is
a wrapper for RE_PROFILE_START_2 and returns an array with named matches.

Also change some places in aa.py from using RE_PROFILE_START to
the parse_profile_start_line() function.

Notes: - until everything is migrated to the new function, I'll
keep the old
  RE_PROFILE_START unchanged - that's the reason to add the new
  regex as RE_PROFILE_START_2
- the patch changes only aa.py sections that are covered by tests
  already (which means some users of RE_PROFILE_START are remaining)
- parse_profile_start_line() merges 'profile' and 'attachment' into
  'profile' (aka the old, broken behaviour) until aa.py can handle
  the attachment properly. The alternative would be to ignore
'attachment', which would be worse.

Convert serialize_parse_profile_start() to use
parse_profile_start_line(), and adjust a test to expect an AppArmorBug
instead of an AttributeError exception.

Also add two tests (they succeed with the old and the new code).
Note that these tests document interesting[tm] behaviour - I tend to
think that those cases should raise an exception, but I'm not sure about
this because serialize_profile_from_old_profile() is a good example for
interesting[tm] code :-/

I couldn't come up with a real-world test profile that would hit those
cases without erroring out aa-logprof earlier - maybe the (more
sane-looking) parse_profiles() / serialize_parse_profile_start()
protects us from hitting this interesting[tm] behaviour.

The previous patch slightly changed the behaviour of parse_profile_start()
and get_profile_flags() - they raise AppArmorBug instead of
AppArmorException when specifying a line that is not the start of a
profile and therefore doesn't match RE_PROFILE_START_2.

This patch updates test-aa.py to expect the correct exceptions, and adds
another test with quoted profile name to ensure that stripping the
quotes works as expected.

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 18:24:20 -07:00
Steve Beattie
242ece320a utils: merge test-only changes from trunk
Merge from trunk revisions 2976+2980, 2977, 2981, and 2984.
2015-04-13 18:03:55 -07:00
Christian Boltz
dc1d8e5253 add tests for write_header()
Merge from trunk revision 2984

Also add loop support to test-aa.py.

BTW: In case you wonder - the need to replace unittest.TestCase with
AATest is intentional. It might look annoying, but it makes sure that
a test-*.py file doesn't contain a test class where tests = [...] is
ignored because it's still unittest.TestCase.
(Technically, setup_all_tests() will error out if a test class doesn't
contain tests = [...] - either explicit or via its parent AATest.)

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 14:42:40 -07:00
Christian Boltz
0ac23ee34a add tests for serialize_parse_profile_start() to test-aa.py
to document the function's behaviour.
Merge from trunk revision 2981.

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 14:38:08 -07:00
Christian Boltz
5bc15cda41 Convert test-regex_matches.py to the new tests[] loop.
Merge from trunk revision 2977

The test behaviour is the same with and without this patch - 166 tests
run, all successful.

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 14:32:15 -07:00
Christian Boltz
9ebb1913bd add better loop support to common_test.py
Merge from trunk revisions 2976 and 2980

Add better support for looping over a tests[] array to common_test.py:
- class AATest - a base class we can use for all tests, and that will
  probably get more features in the future (for example tempdir
  handling)
- setup_all_tests() - a function that iterates over all classes in the
  given file and calls setup_test_loops() for each of them
- setup_tests_loop() - a function that creates tests based on tests[]
  in the given class. Those tests call the class' _run_test() method for
  each test specified in tests[]  (inspired by setup_regex_tests() ;-)

This means we can get rid of the manually maintained tests list in
test-regex_matches.py and just need to call setup_all_tests() once in
each file.

The patch also adds test-example.py, which is
- a demo of the code added to common_test.py
- a template file that we can copy for future test-*.py

Acked-by: Steve Beattie <steve@nxnw.org>
2015-04-13 14:28:48 -07:00
Christian Boltz
720f6624e6 write_net_rules() fixes, part 3
Thanks to the used data structure, write_net_rules() replaces bare
'network,' rules with the invalid 'network all,' when saving a profile.
This patch makes sure a correct 'network,' rule is written.

Also reset 'audit' to avoid all (remaining) rules get the audit flag
after writing an audit network rule.

Note: The first section of the function (that claims to be responsible
for bare 'network,' rules) is probably never hit - but I'm not too keen
to remove it and try it out ;-)


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9.
2015-04-11 02:21:05 +02:00
Christian Boltz
387de4458f Fix doubled arrow in exec rules
When parsing a profile with named exec rules, the exec target included
the arrow. This resulted in two arrows when writing the profile (and one
more each time the profile was updated).

Fix this by using the match group that only contains the exec target
without the arrow in parse_profile_data() and
serialize_profile_from_old_profile().

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


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9.
2015-04-11 02:16:08 +02:00
Steve Beattie
38a69f5ebc profiles: allow ubuntu-helpers to generate texlive fonts
Merge from trunk revision 3004

When evince opens a dvi file, it updates the user fonts using
texlive commands in /usr/share/texlive/texmf-dist/web2c/ (or possibly
/usr/share/texlive/texmf/web2c/ in older releases). This patch adjusts
the sanitized_helper profile to allow these tools to run.

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

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-By: Jamie Strandboge <jamie@canonical.com>
2015-04-10 09:52:36 -07:00
Christian Boltz
7d84c61b6c Fix writing network rules, part 2
write_net_rules() doesn't add a space after 'audit' in two of three
cases, leading to invalid network rules.
This patch adds the missing spaces.


Acked-by: Seth Arnold <seth.arnold@canonical.com>
for both trunk and 2.9
2015-04-10 01:53:42 +02:00
Christian Boltz
f836ebd42b Fix writing network rules
write_net_rules() creates invalid rules for network rules with one
parameter (for example "network bluetooth").
Add a trailing comma to create valid rules.


Acked-by: Seth Arnold <seth.arnold@canonical.com>
for both trunk and 2.9.
2015-04-09 13:29:09 +02:00
Christian Boltz
52b6aeb04c logparser.py: merge path handling in add_event_to_tree()
Merge path handling for 'inode_*' in add_event_to_tree() with the
handling for other path events.

The code is slightly more strict now - 'inode_' in e['operation'] is
replaced with e['operation'].startswith('inode_').

This patch is a cleanup and also a preparation to fix
https://bugs.launchpad.net/apparmor/+bug/1426651 and
https://bugs.launchpad.net/apparmor/+bug/1243932


Acked-by: Seth Arnold <seth.arnold@canonical.com>
Acked for both 2.9 and trunk.
2015-04-03 17:45:48 +02:00
Christian Boltz
475a9bc691 honor 'chmod' events in logparser.py / aa-logprof
aa-logprof doesn't ask anything for

type=AVC msg=audit(1427633461.202:281): apparmor="DENIED" operation="chmod" profile="/usr/lib64/firefox/plugin-container" name="/home/cb/.config/ibus/bus/" pid=7779 comm="plugin-containe" requested_mask="w" denied_mask="w" fsuid=1000 ouid=1000

This patch fixes this by adding 'chmod' to the list of file operation
types in logparser.py.


Acked-by: Seth Arnold <seth.arnold@canonical.com>
for both trunk and 2.9.
2015-04-03 17:44:38 +02:00
Christian Boltz
0f7bf53afb Tell python2 about math
if 3/2 == 1:
    print("python2 inside")

Add "from __future__ import division" so that python2 returns the
correct result (if needed, as float)

On related news: At least python3 knows how to calculate correctly.


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9
2015-04-02 23:40:15 +02:00
Jamie Strandboge
8dcd54e365 cherrypick from trunk:
add --include-templates-dir and --include-policy-groups-dir options to easyprof
  to support framework policy on Snappy for Ubuntu Core

  Signed-off-by: Jamie Strandboge <jamie@canonical.com>
  Acked-by: Seth Arnold <seth.arnold@canonical.com>
2015-03-31 16:24:31 -05:00
Steve Beattie
097eb4258f fix two issues for older linux releases
Merge from trunk revision 2975

The following patch addresses two issues on older releases:

1) In trunk commit 2911, the line 'undefine VERBOSE' was added to
   parser/tst/Makefile so that the equality tests would not generate
   verbose output when $VERBOSE != 1. Unfortunately, the 'undefine'
   keyword was not introduced in GNU Make until version 3.82. On
   distro releases like Ubuntu 12.04 LTS that include versions of Make
   older than that, make check and make clean abort when VERBOSE is
   not set to 1. The patch fixes that by setting VERBOSE to a zero
   length string if does not already equal 1.

2) In trunk commit 2923, a workaround for systemd as init was added
   to the pivot_root regression test. The workaround included a
   call to ps(1) to determine if systemd is pid 1. Unfortunately,
   in older versions of the procps package (such as the version in
   Ubuntu 12.04 LTS), 'ps -hp1' emits the warning

     Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html

   The patch below converts the ps call to 'ps hp1' which does not
   generate the warning.

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: John Johansen <john.johansen@canonical.com>
2015-03-31 02:50:40 -07:00
Cameron Norman
9bc15eb6b8 profiles: update dnsmasq profile for lxc support
Merge from trunk revision 2974

Patch from Cameron Norman <camerontnorman@gmail.com> based on a patch
from Christian Boltz <apparmor@cboltz.de>.

This patch allows /var/lib/misc/dnsmasq.*.leases rw and
/{,var/}run/lxc/dnsmasq.pid rw for LXC networking setup.

Acked-by: Steve Beattie <steve@nxnw.org>
2015-03-30 22:26:32 -07:00
Seth Arnold
9d6f7f53cb Add new gdm path for Xauthority file
Bruce Pieterse reports that AppArmor denied evince, among other
applications, from starting properly:
https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1432126

He tested a slight variant of the attached patch and reported success. I
propose this patch for both trunk and 2.9.

Signed-off-by: Seth Arnold <seth.arnold@canonical.com>
Acked-by: Tyler Hicks <tyhicks@canonical.com>
2015-03-25 15:32:29 -07:00
Steve Beattie
c1ae887576 tests: work around systemd mounting / shared in pivot_root tests
Merge from trunk revision 2923


The systemd init daemon mounts the / filesystem as shared [1], which
breaks pivot_root(2). The following patch adjusts the pivot_root
test script to remount / as private if it detects that its shared,
allowing the tests to run successfully, and then undoes it once the
tests are complete.

[1] http://cgit.freedesktop.org/systemd/systemd/commit/?id=b3ac5f8cb98757416d8660023d6564a7c411f0a0

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2015-03-24 17:12:18 -07:00
Steve Beattie
0ec6ce96d2 parser: fix compilation failure of deny link rules, expand equality tests
Merge from trunk commits 2909, 2910, 2911, and 2912

BugLink: http://bugs.launchpad.net/bugs/1433829

The apparmor_parser fails to compile deny rules with only link
permissions.

  Eg.
       deny /f l,
       deny l /f,
       deny link /f -> /d,

Will all fail to compile with the following assert

  apparmor_parser: aare_rules.cc:99: Node* convert_file_perms(int, uint32_t, uint32_t, bool): Assertion `perms != 0' failed.

NOTE: this is a minimal patch a bigger patch that cleans-up and separates
      and reorganizes file, link, exec, and change_profile rules is needed

parser: Expand Equality tests

This adds several new equality tests and turned up a couple of more
bugs
https://launchpad.net/bugs/1433829
https://launchpad.net/bugs/1434018

- add link/link subset tests
- add pix, Pix, cix, Cix, pux, Pux, cux, Cux and specified profile
  transitions (/f px -> b ...)
- test equality of leading and trailing permission file rules
  ie.   /foo rw, == rw /foo,
- test that specific x match overrides generic x rule. ie.
  /** ix, /foo px, is different than /** ix, /foo ix,
- test that deny removes permission
  /f[abc] r, deny /fb r,  is differnt than /f[abc] r,

In addition to adding the new tests, it changes the output of the
equality tests, so that if the $verbose variable is not set successful
tests only output a period, with failed tests outputing the full
info.  If verbose is set the full test info is output as before.

It also does:

- make the verbose output of equality.sh honor whether or not
  the environment variable VERBOSE is set
- thereby making the output verbose when 'make check V=1' or 'make
  check VERBOSE=1' is given from within the parser/ directory. This
  will make distribution packagers happy when diagnosing build
  failures caused by test failures.
- if verbose output is not emitted and the tests were successful, emit
  a newline before printing PASS.
- verify audit and audit allow is equal
- verify audit differs from deny and audit deny
- verify deny differs from audit deny
- make the verbose text a little more useful for some cases
- correct overlap exec tests to substitute in looped perms

Signed-off-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2015-03-23 12:43:57 -07:00
Christian Boltz
8c19eb5521 dovecot auth needs to read openssl.cnf
Darix' guess is that this is needed by libpq because he uses a postgresql
database with dovecot and has ssl enabled in postgresql.

Acked-by: Seth Arnold <seth.arnold@canonical.com> for trunk and 2.9
2015-03-19 13:58:11 +01:00
Steve Beattie
21a41deabe parser: fix equality and valgrind test scripts to use features file
Merge from trunk commit revision 2907

This patch fixes the equality test script and the valgrind wrapper
script to make the parser under test use the features.all features file
from the features_files/ subdirectory. Otherwise, the equality tests
will fail on systems where the not all of the current language features
are supported. The equality fix does so in a way to make the script work
correctly regardless of the directory it is run from.

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: John Johansen <john.johansen@canonical.com>
2015-03-19 04:37:25 -07:00
Steve Beattie
576e8fe33b parser: fix warning in net_find_af_name
Merge from trunk commit 2906

The fix to prevent the compiler from SEGV'ing when dumping network
rules in commit 2888 introduced the following compiler warning:

  network.c: In function ‘const char* net_find_af_name(unsigned int)’:
  network.c:331:16: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    for (i = 0; i < sizeof(network_mappings) / sizeof(*network_mappings); i++) {

The problem is that the counter i is an int, but sizeof returns size_t
which is unsigned. The following patch fixes the issue by converting the
type of i to size_t.

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: John Johansen <john.johansen@canonical.com>
2015-03-19 00:14:12 -07:00
Tyler Hicks
3c928c04e1 parser: Test the 'allow' modifier
Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
2015-03-18 12:34:49 -05:00
Tyler Hicks
37b872b155 parser: Test the 'audit allow' modifier
Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2015-03-18 12:34:44 -05:00
Tyler Hicks
5ab8b7a483 parser: Verify policies change with the audit and deny modifiers
Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2015-03-18 12:34:41 -05:00
Tyler Hicks
b813f4ba53 parser: Add ability to test the inequality of binary policies
Previously, we only had the ability to test that binary policy files
were equal. This patch allows for the testing of binary policy files
that are not equal.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2015-03-18 12:34:38 -05:00
John Johansen
05ab11fec4 Fix compilation of audit modifiers
cherry-pick: -r2901

This fixes the incorrect compilation of audit modifiers for exec and
pivot_root as detailed in

https://launchpad.net/bugs/1431717
https://launchpad.net/bugs/1432045

The permission accumulation routine on the backend was incorrectly setting
the audit mask based off of the exec type bits (info about the exec) and
not the actual exec permission.

This bug could have also caused permissions issues around overlapping exec
generic and exact match exec rules, except the encoding of EXEC_MODIFIERS
ensured that the
  exact_match_allow & AA_USER/OTHER_EXEC_TYPE
  test would never fail for a permission accumulation with the exec permission
  set.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
2015-03-18 10:22:31 -07:00
Christian Boltz
2d7ba0871f Split off serialize_parse_profile_start_line() from
serialize_profile_from_old_profile() in aa.py, as a preparation to add
tests and then switch to the upcoming RE_PROFILE_START wrapper function.

Besides moving the code, I replaced write_prof_data[profile][hat]['profile']
and write_prof_data[profile][hat]['external'] with function parameters
to avoid that I have to pass around the full write_prof_data.

Note: The "lineno" parameter is technically superfluous - I kept it to
have the parameters as close to parse_profile_start() as possible and
hope that I can merge those functions later (when we have test coverage).


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9.
2015-03-10 19:09:16 +01:00
Christian Boltz
c98b26069a tools.py: add functions to unload and reload profiles
and change the code to use them

Also add a comment to act() that it's only used by aa-cleanprof.

Note: The new functions add the --base parameter to the apparmor_parser
calls, which also means the disable directory inside the given profile
dir (and not always /etc/apparmor.d/disable) is now honored.


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9.
2015-03-07 20:32:11 +01:00
Christian Boltz
70dc81c4fd merge 'path' if conditions in logparser.py / add_event_to_tree()
logparser.py / add_event_to_tree() has 5 places to handle 'path' events.
This patch merges most if conditions to reduce that to 2 places.

It also makes the matching a bit more strict - instead of using 'in',
'xattr' has to be an exact match and 'file_' is matched with startswith().

Also, 'getattr' is added to the list of file events.


Acked-by: Steve Beattie <steve@nxnw.org> (also for 2.9)



---------- trunk only, unclear for 2.9 --------------
2015-03-07 20:26:32 +01:00
Christian Boltz
1b68baf7a3 let load_include raise an exception if an include file can't be found
instead of ignoring the error silently

Acked-by: Steve Beattie <steve@nxnw.org> for both trunk and 2.9.
2015-03-07 13:28:41 +01:00
Christian Boltz
6af7faa2b7 add read_profiles() call to cmd_disable()
Without it, aa-disable
- didn't error out when hitting a broken profile directory
- didn't find a profile if it doesn't use the default naming scheme
  (for example /bin/true profile hiding in bin.false)


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9
2015-03-07 13:27:57 +01:00
Steve Beattie
a1529a16bd profiles: add mir abstraction
Merge from trunk revision 2893

As mir has come into use in Ubuntu touch and is available for testing on
Ubuntu desktop, confined apps need access to a few mir specific things.
This patch adds a mir abstraction.

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: Tyler Hicks <tyhicks@canonical.com>
2015-03-05 11:51:16 -08:00
Steve Beattie
321a2c1dcb regression tests: fix gcc-5 inline confusion
Merge from trunk revision 2889

Parts of the regression tests that use the do_open() inline function
from changehat.h fail to build under gcc-5 like so:

  cc -g -O0 -Wall -Wstrict-prototypes    changeprofile.c  -lapparmor -o changeprofile /tmp/ccT6GE6k.o: In function `main':
    /home/ubuntu/bzr/apparmor/tests/regression/apparmor/changeprofile.c:43: undefined reference to `do_open'
    collect2: error: ld returned 1 exit status
    <builtin>: recipe for target 'changeprofile' failed

This patch converts the do_open function declaration to be static
inline, which apparently keeps gcc-5 from getting confused.

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: John Johansen <john.johansen@canonical.com>
2015-03-03 23:32:26 -08:00
Alain BENEDETTI
735ef5d32b utils/aa-status: don't crash when non-ASCII mountpoints are in use
Merge from trunk revision 2892

aa-status was crashing when parsing through /proc/mounts looking
to see if and where the securityfs synthetic file system is mounted
if there was a mount point that contained characters outside of the
charset in use in the environment of aa-status. This patch fixes the
issue by converting the read of /proc/mounts into a binary read and
then uses decode on the elements.

Patch by Alain BENEDETTI.
Acked-by: Steve Beattie <steve@nxnw.org>
2015-03-03 22:25:32 -08:00
Tyler Hicks
9428498d90 parser: Fix error checking of file opening in features_dir_cb()
The error path was being taken when openat() return 0 but openat()
returns -1 on error.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2015-03-03 20:28:22 -06:00
Christian Boltz
3ea1e541c7 Add test for disconnected path
As a follow-up to the logparser.py change that converts disconnected
path events to an error, add a testcase to test-logparser.py.


Acked-by: Steve Beattie <steve@nxnw.org> for both trunk and 2.9.
2015-03-03 22:18:24 +01:00
Philip Withnall
29b0634f34 parser: net_find_af_name: do not assume that address families are consecutive,
remove unused net_find_af_val function, and network_families array
Merge from trunk commit 2888.

net_find_af_name: do not assume that address families are consecutive

The network_families array is automatically built from AF_NAMES, which is
extracted from the defines in <bits/socket.h>. The code assumes that
network_families is indexed by the AF defines. However, since the
defines are sparse, and the gaps in the array are not packed with
zeroes, the array is shorter than expected, and the indexing is wrong.

When this function was written, the network families that were
covered might well have been consecutive, but this is no longer true:
there's a gap between AF_LLC (26) and AF_CAN (29).

This assumption caused a crash in our testing while parsing the rule
"network raw".

Remove unused net_find_af_val function, and network_families array

Like net_find_af_name, this assumed that AF_* values were consecutive.

Patches from Philip Withnall and Simon McVittie.
2015-03-03 12:04:13 -08:00
Christian Boltz
586222c94e move strip_quotes() from aa.py to regex.py
The upcoming function parse_profile_start() (which is a wrapper around
the updated RE_PROFILE_START, and will live in regex.py) needs
strip_profile(), but importing it from aa.py fails with an import loop.
Therefore this patch moves strip_quotes() from aa.py to regex.py and
re-imports it into aa.py.

As a bonus, the patch also adds some tests for strip_quotes() ;-)


Also add TestStripQuotes to the test_suite list because it won't run
otherwise.

Acked-by: Steve Beattie <steve@nxnw.org> for both trunk and 2.9
2015-03-03 20:18:30 +01:00
Christian Boltz
232b51504c let logparser.py parse_event() change disconnected path events to 'ERROR'
This means that aa-logprof will ignore the event instead of crashing with
    AppArmorException: 'Unexpected rank input: var/run/nscd/passwd'

Note that I made the check as specific as possible to be sure it doesn't
hide other events.

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



Acked-by: Steve Beattie <steve@nxnw.org> for 2.9



(This is a backport of trunk r2877, but without the test-capability.py
adjustment because that file doesn't exist in 2.9)
2015-03-03 12:34:47 +01:00
Christian Boltz
df099620dd aa.py: split off parse_profile_start() from parse_profile_data() and add tests
Move the code for parsing the profile start ("/foo {") from aa.py
parse_profile_data() to a separate function parse_profile_start().

Most of the changes are just moving around code, with some small
exceptions:
- instead of handing over profile_data to parse_profile_start() to
  modify it, it sets two variables (pps_set_profile and
  pps_set_hat_external) as part of its return value, which are then
  used in parse_profile_data() to set the flags in profile_data.
- existing_profiles[profile] = file   is executed later, which means
  it used the strip_quotes() version of profile now
- whitespace / tab level changes

The patch also adds some tests for the parse_profile_start() function.



Acked-by: Steve Beattie <steve@nxnw.org> for 2.9 as well.
2015-03-02 21:46:45 +01:00
Christian Boltz
22d647ecb1 Add some tests for aa.py get_profile_flags().
Also adds a check to get_profile_flags() to catch an invalid syntax:
    /foo (  ) {
was accepted by get_profile_flags, while
    /foo () {
failed.

When testing with the parser, both result in a syntax error, therefore
the patch makes sure it also fails in get_profile_flags().


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9
2015-03-02 19:38:34 +01:00
Tyler Hicks
07b0886796 parser: Fix "PDEBUG" redefined warning
Only present when building with DEBUG=1.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2015-03-02 09:59:17 -06:00
Tyler Hicks
9da31bf281 parser: Fix -Wformat-extra-args warning
Only present when building with DEBUG=1.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
2015-03-02 09:59:14 -06:00
Tyler Hicks
c5ff27a91b parser: Send PDEBUG() to stderr
PDEBUG() and PERROR() should both go to stderr.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2015-03-02 09:59:11 -06:00
Tyler Hicks
cf4afcb860 parser: Fix return value of dirat_for_each()
Seth pointed out that dirat_for_each() didn't correctly handle the
return value from readdir_r(). On error, it directly returns a positive
errno value. This would have resulted in that positive errno value being
returned, with an undefined errno value set, from dirat_for_each().
However, the dirat_for_each() documentation states that -1 is returned,
with errno set, on error.

This patch results in readdir_r()'s return value being handled
appropriately. In addition, it ensures that 0 is always returned on
success.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Reported-by: Seth Arnold <seth.arnold@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
2015-03-02 09:54:57 -06:00
Cameron Norman
75a186fa9f profiles: add geary email client to ubuntu-email abstraction
Merge from trunk revision 2876

Merge from Cameron Norman <camerontnorman@gmail.com>
Acked-by: Steve Beattie <steve@nxnw.org>
2015-02-27 23:28:05 -08:00
Felix Geyer
05bef291d7 profiles: allow aspell access to /usr/share/aspell/
Merge from trunk revision 2875

From: Felix Geyer <debfx@ubuntu.com>

At least Debian/Ubuntu started shipping some aspell files in
/usr/share/aspell/.
For example:
/usr/share/aspell/iso-8859-1.cmap
/usr/share/aspell/iso-8859-1.cset

The abstraction should allow read access to these files.

Acked-by: Steve Beattie <steve@nxnw.org>
2015-02-27 23:16:32 -08:00
Steve Beattie
76f71f7d84 profiles: add support for /etc/pki/ in ssl certs abstraction
Merge from trunk revision 2874.

These appear to be related to the update-ca-trust tool.

Thanks to Gregor Dschung <dschung@cs.uni-kl.de>
2015-02-27 22:57:41 -08:00
Christian Boltz
34f2c1c6ea cleanup aa-disable handling in tools.py
Remove the check if the disable directory exists. If it's really
missing, it will be auto-created by create_symlink(), so we
automagically fix things instead of annoying the user with an
error message ;-)

Acked-by: Steve Beattie <steve@nxnw.org> for both trunk and 2.9.
2015-02-28 00:25:45 +01:00
Christian Boltz
67dae2f1cf Fix the minitools (aa-audit, aa-complain, aa-enforce, aa-autodep)
to work with multiple profiles at once.

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

Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9.
2015-02-27 14:21:05 +01:00
Seth Arnold
bbaaa00249 parser: fix dbus.cc issue when building with gcc 5
Merge from trunk revision 2868.

This should fix a gcc 5 build failure (untested) with os << .. << os

This build failure was discovered by doko's archive rebuild:
http://people.ubuntuwire.org/~wgrant/rebuild-ftbfs-test/test-rebuild-20150202-gcc5-vivid.html

Acked-by: Steve Beattie <steve@nxnw.org>
2015-02-26 16:18:15 -08:00
Steve Beattie
9ed8789918 parser: fix more gcc 5 compilation problems
Merge from trunk revision 2871

Don't pass an ostream reference into another ostream via <<.

Signed-off-by: Steve Beattie <steve@nxnw.org>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2015-02-26 15:20:19 -08:00
Christian Boltz
f45628d749 delete traces of program-chunks directory from apparmor.d(5)
Acked-by: Tyler Hicks <tyhicks@canonical.com>
2015-02-26 18:45:41 +01:00
Christian Boltz
602decfbfc Update is_skippable_file() to match all extensions that are listed in
libapparmor _aa_is_blacklisted() - some extensions were missing in the
python code.

Also make the code more readable and add some testcases.

Notes:
- the original code additionally ignored *.swp. I didn't include that -
  *.swp looks like vim swap files which are also dot files
- the python code ignores README files, but the C code doesn't
  (do we need to add README in the C code?)


Acked-by: Kshitij Gupta <kgupta8592@gmail.com> for 2.9 and trunk
Acked-by: Steve Beattie <steve@nxnw.org>
2015-02-04 13:18:47 +01:00
Christian Boltz
9aa1efd744 Fix aa-unconfined to work with profile names that don't start with / or null
Reported by u on the debian pkg-apparmor-team ML.


Acked-by: John Johansen <john.johansen@canonical.com>
2015-02-02 20:53:29 +01:00
Jamie Strandboge
c51a68eaaf Description: Allow writes to /{,var}/run/systemd/journal/dev-log, the systemd
journal socket. On Debian and Ubuntu systems, /dev/log is a symlink to
 /run/systemd/journal/dev-log, so this access is now required in the base
 abstraction to maintain current behavior.
Bug: https://bugs.launchpad.net/apparmor/+bug/1413232

Acked-By: Jamie Strandboge <jamie@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
2015-01-21 13:37:49 -06:00
Christian Boltz
49b739b184 Add some tests for logparser.py based on the log lines from
https://bugs.launchpad.net/apparmor/+bug/1399027

Also move some existing tests from aa_test.py to test-logparser.py and
adds checks for RE_LOG_v2_6_audit and RE_LOG_v2_6_syslog to them.


Acked-by: Steve Beattie <steve@nxnw.org> for trunk and 2.9
2015-01-18 14:57:10 +01:00
Christian Boltz
53d071adf5 update logparser.py to support the changed syslog format by adding
(audit:\s+)?   to RE_LOG_v2_6_syslog

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


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

Acked-by: Steve Beattie <steve@nxnw.org> for 2.9 as well
2015-01-17 14:35:38 +01:00
Christian Boltz
70cda06789 Fix the dnsmasq profile to allow executing bash to run the --dhcp-script
argument. Also fixed /usr/lib -> /usr/{lib,lib64} to get libvirt
leasehelper script to run even on x86_64.

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

Patch by "Cédric Bosdonnat" <cbosdonnat@suse.com>

Note: the original patch used {lib,lib64} - I changed it to lib{,64} to
match the style we typically use.

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

(backport of trunk r2841)
2014-12-22 17:57:40 +01:00
Christian Boltz
e8ffc1c4e8 update and cleanup usr.sbin.dovecot profile
Add #include <abstractions/dovecot-common> to the usr.sbin.dovecot
profile. Effectively this adds "deny capability block_suspend," which
is the only missing part from
https://bugs.launchpad.net/apparmor/+bug/1296667/

Also remove "capability setgid," (covered by
abstractions/dovecot-common) and "@{PROC}/filesystems r," (part of
abstractions/base).

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

(backport of trunk r2840)
2014-12-22 17:51:02 +01:00
Christian Boltz
09c93be47c Add some missing /run/dovecot/* to usr.lib.dovecot.imap{, -login}
Add the needed permissions as reported in
https://bugs.launchpad.net/apparmor/+bug/1296667/ comment #1
to the usr.lib.dovecot.imap and imap-login profiles.

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

(backport of trunk r2839)
2014-12-22 17:43:54 +01:00
Christian Boltz
ac8d886645 update the mysqld profile in the extras directory to
something that works on my servers ;-)

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

(backport of trunk r2838)
2014-12-22 17:39:29 +01:00
Christian Boltz
ec1dda24d0 fix network rule description in apparmor.d.pod
(backport from trunk r2837)

Acked-by: John Johansen <john.johansen@canonical.com> (for trunk)

Acked-by: Steve Beattie <steve@nxnw.org> (for 2.9)
2014-12-19 13:57:12 +01:00
Steve Beattie
e7e9053598 Update version in preparation for 2.9.2 development. 2014-12-16 13:37:58 -08:00
898 changed files with 4507 additions and 25172 deletions

View File

@@ -18,13 +18,13 @@ parser/*.8
parser/*.7.html
parser/*.5.html
parser/*.8.html
parser/common
parser/apparmor_parser
parser/libapparmor_re/regexp.cc
parser/techdoc.aux
parser/techdoc.log
parser/techdoc.pdf
parser/techdoc.toc
profiles/apparmor.d/local/*.*
libraries/libapparmor/Makefile
libraries/libapparmor/Makefile.in
libraries/libapparmor/aclocal.m4
@@ -41,32 +41,20 @@ libraries/libapparmor/install-sh
libraries/libapparmor/libtool
libraries/libapparmor/ltmain.sh
libraries/libapparmor/missing
libraries/libapparmor/test-driver
libraries/libapparmor/ylwrap
libraries/libapparmor/doc/Makefile
libraries/libapparmor/doc/Makefile.in
libraries/libapparmor/doc/*.2
libraries/libapparmor/doc/aa_*.3
libraries/libapparmor/include/Makefile
libraries/libapparmor/include/Makefile.in
libraries/libapparmor/include/sys/Makefile
libraries/libapparmor/include/sys/Makefile.in
libraries/libapparmor/src/.deps
libraries/libapparmor/src/.libs
libraries/libapparmor/src/Makefile
libraries/libapparmor/src/Makefile.in
libraries/libapparmor/src/af_protos.h
libraries/libapparmor/src/change_hat.lo
libraries/libapparmor/src/features.lo
libraries/libapparmor/src/grammar.lo
libraries/libapparmor/src/kernel.lo
libraries/libapparmor/src/kernel_interface.lo
libraries/libapparmor/src/libaalogparse.lo
libraries/libapparmor/src/libimmunix_warning.lo
libraries/libapparmor/src/policy_cache.lo
libraries/libapparmor/src/private.lo
libraries/libapparmor/src/scanner.lo
libraries/libapparmor/src/libapparmor.pc
libraries/libapparmor/src/libapparmor.la
libraries/libapparmor/src/libimmunix.la
libraries/libapparmor/src/grammar.c
@@ -82,20 +70,12 @@ libraries/libapparmor/swig/perl/Makefile
libraries/libapparmor/swig/perl/Makefile.PL
libraries/libapparmor/swig/perl/Makefile.in
libraries/libapparmor/swig/perl/Makefile.perl
libraries/libapparmor/swig/perl/Makefile.perle
libraries/libapparmor/swig/perl/MYMETA.json
libraries/libapparmor/swig/perl/MYMETA.yml
libraries/libapparmor/swig/perl/blib
libraries/libapparmor/swig/perl/libapparmor_wrap.c
libraries/libapparmor/swig/perl/pm_to_blib
libraries/libapparmor/swig/python/__init__.py
libraries/libapparmor/swig/python/build/
libraries/libapparmor/swig/python/libapparmor_wrap.c
libraries/libapparmor/swig/python/Makefile
libraries/libapparmor/swig/python/Makefile.in
libraries/libapparmor/swig/python/setup.py
libraries/libapparmor/swig/python/test/Makefile
libraries/libapparmor/swig/python/test/Makefile.in
libraries/libapparmor/swig/ruby/Makefile
libraries/libapparmor/swig/ruby/Makefile.in
libraries/libapparmor/testsuite/.deps
@@ -114,6 +94,10 @@ libraries/libapparmor/testsuite/libaalogparse.test/Makefile
libraries/libapparmor/testsuite/libaalogparse.test/Makefile.in
libraries/libapparmor/testsuite/test_multi/out
changehat/mod_apparmor/.libs
changehat/mod_apparmor/common
changehat/pam_apparmor/common
changehat/tomcat_apparmor/common
utils/common
utils/*.8
utils/*.8.html
utils/*.5
@@ -181,5 +165,4 @@ tests/regression/apparmor/unix_fd_server
tests/regression/apparmor/unlink
tests/regression/apparmor/xattrs
tests/regression/apparmor/coredump
**/__pycache__/
*.orig
./utils/apparmor/__pycache__

View File

@@ -1,12 +1,8 @@
#
#
.PHONY: all
all:
@echo "*** See README for information how to build AppArmor ***"
exit 1
OVERRIDE_TARBALL=yes
COMMONDIR=common
include ${COMMONDIR}/Make.rules
include common/Make.rules
DIRS=parser \
profiles \
@@ -18,7 +14,7 @@ DIRS=parser \
#REPO_URL?=lp:apparmor
# --per-file-timestamps is failing over SSH, https://bugs.launchpad.net/bzr/+bug/1257078
REPO_URL?=https://code.launchpad.net/~apparmor-dev/apparmor/2.10
REPO_URL?=https://code.launchpad.net/~apparmor-dev/apparmor/2.9
# alternate possibilities to export from
#REPO_URL=.
#REPO_URL="bzr+ssh://bazaar.launchpad.net/~sbeattie/+junk/apparmor-dev/"

View File

@@ -17,7 +17,13 @@ NAME:=apache2-mod_apparmor
all:
COMMONDIR=../../common/
include $(COMMONDIR)/Make.rules
include common/Make.rules
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
ifeq ($(COMMONDIR_EXISTS), true)
common/Make.rules: $(COMMONDIR)/Make.rules
ln -sf $(COMMONDIR) .
endif
TARGET:=mod_apparmor.so
MANPAGES=mod_apparmor.8
@@ -86,9 +92,9 @@ install: ${TARGET} ${MANPAGES}
make install_manpages DESTDIR=${DESTDIR}
.PHONY: clean
clean: pod_clean
clean: _clean
rm -rf .libs
rm -f *.la *.lo *.so *.o *.slo
rm -f *.la *.lo *.so *.o *.slo Make.rules
.PHONY: check
check: check_pod_files

View File

@@ -137,7 +137,7 @@ aa_enter_hat(request_rec *r)
ap_get_module_config(r->server->module_config, &apparmor_module);
const char *aa_hat_array[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
int i = 0;
char *aa_label, *aa_mode, *aa_hat;
char *aa_con, *aa_mode, *aa_hat;
const char *vhost_uri;
debug_dump_uri(r);
@@ -201,14 +201,14 @@ aa_enter_hat(request_rec *r)
/* Check to see if a defined AAHatName or AADefaultHatName would
* apply, but wasn't the hat we landed up in; report a warning if
* that's the case. */
aa_ret = aa_getcon(&aa_label, &aa_mode);
aa_ret = aa_getcon(&aa_con, &aa_mode);
if (aa_ret < 0) {
ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno, r, "aa_getcon call failed");
} else {
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
"AA checks: aa_getcon result is '%s', mode '%s'", aa_label, aa_mode);
"AA checks: aa_getcon result is '%s', mode '%s'", aa_con, aa_mode);
/* TODO: use libapparmor get hat_name fn here once it is implemented */
aa_hat = strstr(aa_label, "//");
aa_hat = strstr(aa_con, "//");
if (aa_hat != NULL && strcmp(aa_mode, "enforce") == 0) {
aa_hat += 2; /* skip "//" */
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
@@ -226,7 +226,7 @@ aa_enter_hat(request_rec *r)
scfg->hat_name);
}
}
free(aa_label);
free(aa_con);
}
return OK;

View File

@@ -16,8 +16,15 @@
NAME=pam_apparmor
all:
COMMONDIR=../../common/
MAKE_RULES=common/Make.rules
include $(COMMONDIR)/Make.rules
include ${MAKE_RULES}
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
ifeq ($(COMMONDIR_EXISTS), true)
common/Make.rules: $(COMMONDIR)/Make.rules
ln -sf $(COMMONDIR) .
endif
ifdef USE_SYSTEM
LIBAPPARMOR = $(shell if pkg-config --exists libapparmor ; then \
@@ -53,7 +60,7 @@ libapparmor by adding USE_SYSTEM=1 to your make command.${nl}\
AA_LINK_FLAGS = -L$(LIBAPPARMOR_PATH)
AA_LDLIBS = -lapparmor
endif
EXTRA_CFLAGS=$(CFLAGS) $(CPPFLAGS) -fPIC -shared -Wall $(LIBAPPARMOR_INCLUDE)
EXTRA_CFLAGS=$(CFLAGS) -fPIC -shared -Wall $(LIBAPPARMOR_INCLUDE)
LINK_FLAGS=-Xlinker -x $(AA_LINK_FLAGS)
LIBS=-lpam $(AA_LDLIBS)
OBJECTS=${NAME}.o get_options.o
@@ -80,5 +87,7 @@ install: $(NAME).so
install -m 555 $(NAME).so $(SECDIR)/
.PHONY: clean
clean:
clean: ${MAKE_RULES}
rm -f core core.* *.so *.o *.s *.a *~
rm -f ${NAME}-*.tar.gz Make.rules

View File

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

View File

@@ -17,7 +17,13 @@ NAME = tomcat_apparmor
all:
COMMONDIR=../../../common/
include $(COMMONDIR)/Make.rules
include common/Make.rules
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
ifeq ($(COMMONDIR_EXISTS), true)
common/Make.rules: $(COMMONDIR)/Make.rules
ln -sf $(COMMONDIR) .
endif
LIB = lib
CATALINA_HOME = /usr/share/tomcat5
@@ -28,6 +34,7 @@ all:
clean:
ant clean
rm -f tomcat_apparmor.spec ${NAME}-*.tar.gz Make.rules
install:
install: $(SPECFILE)
ant -Dversion=$(VERSION) -Drelease=$(MAN_RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni

View File

@@ -17,7 +17,13 @@ NAME = tomcat_apparmor
all:
COMMONDIR=../../../common/
include $(COMMONDIR)/Make.rules
include common/Make.rules
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
ifeq ($(COMMONDIR_EXISTS), true)
common/Make.rules: $(COMMONDIR)/Make.rules
ln -sf $(COMMONDIR) .
endif
LIB = lib
CATALINA_HOME = /usr/share/tomcat55
@@ -28,6 +34,7 @@ all:
clean:
ant clean
rm -f tomcat_apparmor.spec ${NAME}-*.tar.gz Make.rules
install:
install: $(SPECFILE)
ant -Dversion=$(VERSION) -Drelease=$(MAN_RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni

View File

@@ -1,7 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2005 Novell/SUSE
# Copyright (C) 2010-2015 Canonical, Ltd.
# Copyright (C) 2010 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
@@ -15,21 +15,17 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, contact Novell, Inc.
# ------------------------------------------------------------------
# Make.rules - common make targets and variables for building AppArmor
# Make.rules - common make targets and variables for building the SHASS
# product.
#
# NOTES:
# Before including this file in your Makefile, you should
# - define COMMONDIR (the location of the common/ directory)
# - define the default rule (usually 'all:'). (Note: you can redefine
# it later in your Makefile)
.PHONY: common_Make.rules_is_a_bad_target
common_Make.rules_is_a_bad_target:
@echo "*** default target in common/Make.rules hit - either you did something strange, or something is broken... ***"
exit 1
# - must define the package NAME before including this file.
# - After checking in to cvs, you'll need to delele the hardlinked
# Make.rules files that already exist in the individual application
# directories
DISTRIBUTION=AppArmor
VERSION=$(shell cat $(COMMONDIR)/Version)
VERSION=$(shell cat common/Version)
# Convenience functions
pathsearch = $(firstword $(wildcard $(addsuffix /$(1),$(subst :, ,$(PATH)))))
@@ -45,8 +41,106 @@ define nl
endef
# OVERRIDABLE variables
# Set these variables before including Make.rules to change its behavior
# SPECFILE - for packages that have a non-standard specfile name
# EXTERNAL_PACKAGE - for packages that have upstream versions that
# we're locally modifying (e.g. imnxcerttool/gnutls).
#
# use 'make BUILDIR=/some/where/else/' to override the /usr/src/redhat
# location -- it *should* pick out the right thing to do based on the
# .rpmmacros file, but you can still use BUILDDIR to override that.
TESTBUILDDIR=$(shell [ -f ${HOME}/.rpmmacros ] && awk '/^%_topdir/ {print $$2}' ${HOME}/.rpmmacros)
ifndef BUILDDIR
BUILDDIR=$(shell if [ -d "${TESTBUILDDIR}" ] ; then \
echo ${TESTBUILDDIR} | sed "s^/$$^^" ; \
elif [ -d "/usr/src/redhat" ] ; then \
echo "/usr/src/redhat" ; \
elif [ -d "/usr/src/packages" ] ; then \
echo "/usr/src/packages" ; \
else \
echo "/tmp/${NAME}" ; \
fi ;)
endif
ifndef DISTRO
DISTRO=$(shell if [ -f /etc/slackware-version ] ; then \
echo slackware ; \
elif [ -f /etc/debian_version ] ; then \
echo debian ;\
elif which rpm > /dev/null ; then \
if [ "$(rpm --eval '0%{?suse_version}')" != "0" ] ; then \
echo suse ;\
elif [ "$(rpm --eval '%{_host_vendor}')" = redhat ] ; then \
echo rhel4 ;\
elif [ "$(rpm --eval '0%{?fedora}')" != "0" ] ; then \
echo rhel4 ;\
else \
echo unknown ;\
fi ;\
else \
echo unknown ;\
fi)
endif
RPMARG=--define "_topdir $(BUILDDIR:/=)" \
--define "vendor NOVELL, Inc." \
--define "distribution ${DISTRIBUTION}" \
--define "debug_package %{nil}" \
--define "immunix_version ${VERSION}" \
$(shell [ -d ${BUILDDIR}/BUILDROOT ] && echo --define \"buildroot $(BUILDDIR:/=)/BUILDROOT\") \
$(shell [ -n "${DISTRO}" ] && echo --define \"distro ${DISTRO}\")
REPO_VERSION_CMD=([ -x /usr/bin/bzr ] && /usr/bin/bzr version-info . 2> /dev/null || awk '{ print "revno: "$2 }' common/.stamp_rev) | awk '/^revno:/ { print $2 }'
ifdef EXTERNAL_PACKAGE
RPMARG+=--define "_sourcedir $(shell pwd)"
endif
ifndef SPECFILE
SPECFILE = $(NAME).spec
endif
RELEASE_DIR = $(NAME)-$(VERSION)
TAR = /bin/tar czvp -h --exclude .svn --exclude .bzr --exclude .bzrignore --exclude ${RELEASE_DIR}/${RELEASE_DIR} $(shell test -f ${NAME}.exclude && echo "-X ${NAME}.exclude")
LDCONFIG = /sbin/ldconfig
RPMSUBDIRS=SOURCES SPECS BUILD BUILDROOT SRPMS RPMS/i386 RPMS/i586 \
RPMS/i686 RPMS/athlon RPMS/noarch RPMS/x86_64
BUILDRPMSUBDIRS=$(foreach subdir, $(RPMSUBDIRS), $(BUILDDIR:/=)/$(subdir))
ifdef EXTERNAL_PACKAGE
.PHONY: rpm
rpm: clean $(BUILDRPMSUBDIRS)
rpmbuild -ba ${RPMARG} ${SPECFILE}
else
.PHONY: rpm
rpm: clean $(BUILDRPMSUBDIRS)
__REPO_VERSION=`$(value REPO_VERSION_CMD)` ; \
__TARBALL=$(NAME)-$(VERSION)-$${__REPO_VERSION}.tar.gz ; \
make $${__TARBALL} ; \
cp $${__TARBALL} $(BUILDDIR)/SOURCES/
cp ${SPECFILE} $(BUILDDIR)/SPECS/
rpmbuild -ba ${RPMARG} ${SPECFILE}
.PHONY: ${SPECFILE}
${SPECFILE}: ${SPECFILE}.in
__REPO_VERSION=`$(value REPO_VERSION_CMD)` ; \
sed -e "s/@@immunix_version@@/${VERSION}/g" \
-e "s/@@repo_version@@/$${__REPO_VERSION}/g" $< > $@
%.tar.gz: clean ${SPECFILE}
-rm -rf $(RELEASE_DIR)
mkdir $(RELEASE_DIR)
$(TAR) --exclude $@ . | tar xz -C $(RELEASE_DIR)
$(TAR) --exclude $@ -f $@ $(RELEASE_DIR)
rm -rf $(RELEASE_DIR)
ifndef OVERRIDE_TARBALL
.PHONY: tarball
tarball: clean $(TARBALL)
endif
endif
ifndef PYTHON_VERSIONS
PYTHON_VERSIONS = $(call map, pathsearch, python2 python3)
endif
@@ -61,18 +155,24 @@ pyalldo=set -e; $(foreach py, $(PYTHON_VERSIONS), $(py) $(1);)
.PHONY: version
.SILENT: version
version:
echo $(VERSION)
rpm -q --define "_sourcedir ." ${RPMARG} --specfile ${SPECFILE}
.PHONY: repo_version
.SILENT: repo_version
repo_version:
$(value REPO_VERSION_CMD)
.PHONY: pod_clean
ifndef VERBOSE
.SILENT: pod_clean
endif
pod_clean:
.PHONY: build_dir
build_dir: $(BUILDRPMSUBDIRS)
$(BUILDRPMSUBDIRS):
mkdir -p $(BUILDRPMSUBDIRS)
.PHONY: _clean
.SILENT: _clean
_clean:
-[ -z "${NAME}" ] || rm -f ${NAME}-${VERSION}-*.tar.gz
-rm -f ${MANPAGES} *.[0-9].gz ${HTMLMANPAGES} pod2htm*.tmp
# =====================
@@ -82,7 +182,7 @@ pod_clean:
# =====================
# emits defined capabilities in a simple list, e.g. "CAP_NAME CAP_NAME2"
CAPABILITIES=$(shell echo "\#include <linux/capability.h>" | cpp -dM | LC_ALL=C sed -n -e '/CAP_EMPTY_SET/d' -e 's/^\#define[ \t]\+CAP_\([A-Z0-9_]\+\)[ \t]\+\([0-9xa-f]\+\)\(.*\)$$/CAP_\1/p' | LC_ALL=C sort)
CAPABILITIES=$(shell echo "\#include <linux/capability.h>" | cpp -dM | LC_ALL=C sed -n -e '/CAP_EMPTY_SET/d' -e 's/^\#define[ \t]\+CAP_\([A-Z0-9_]\+\)[ \t]\+\([0-9xa-f]\+\)\(.*\)$$/CAP_\1/p' | sort)
.PHONY: list_capabilities
list_capabilities: /usr/include/linux/capability.h
@@ -98,7 +198,7 @@ list_capabilities: /usr/include/linux/capability.h
# to mediate. We use PF_ here since that is what is required in
# bits/socket.h, but we will rewrite these as AF_.
FILTER_FAMILIES=PF_UNIX
FILTER_FAMILIES=PF_UNSPEC PF_UNIX
__FILTER=$(shell echo $(strip $(FILTER_FAMILIES)) | sed -e 's/ /\\\|/g')

View File

@@ -1 +1 @@
2.10.1
2.9.2

View File

@@ -24,7 +24,13 @@ NAME = apparmor-utils
all:
COMMONDIR=../../common/
include $(COMMONDIR)/Make.rules
include common/Make.rules
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
ifeq ($(COMMONDIR_EXISTS), true)
common/Make.rules: $(COMMONDIR)/Make.rules
ln -sf $(COMMONDIR) .
endif
MODDIR = Immunix
PERLTOOLS = aa-genprof aa-logprof aa-autodep aa-audit aa-complain aa-enforce \
@@ -50,8 +56,9 @@ install:
ifndef VERBOSE
.SILENT: clean
endif
clean: pod_clean
clean: _clean
rm -f core core.* *.o *.s *.a *~
rm -f Make.rules
rm -rf staging/ build/
.PHONY: check

Binary file not shown.

View File

@@ -4,4 +4,22 @@ SRCDIR = src
SUBDIRS = doc src include swig testsuite
REPO_VERSION=$(shell if [ -x /usr/bin/svn ] ; then \
/usr/bin/svn info . 2> /dev/null | grep "^Last Changed Rev:" | sed "s/^Last Changed Rev: //" ; \
fi)
REPO_URL=$(shell if [ -x /usr/bin/svn ] ; then \
/usr/bin/svn info . 2> /dev/null | grep "^URL:" | sed "s/^URL: //" ; \
fi)
RELEASE_DIR = $(NAME)-$(VERSION)-${REPO_VERSION}
SVNTARBALL = $(NAME)-$(VERSION)-${REPO_VERSION}.tar.gz
SVNTAR = /bin/tar czvp -h --exclude .svn --exclude CVS --exclude .cvsignore --exclude ${SVNTARBALL} --exclude ${RELEASE_DIR}/${RELEASE_DIR} $(shell test -f ${NAME}.exclude && echo "-X ${NAME}.exclude")
distball: clean
rm -rf $(RELEASE_DIR)
svn export -r $(REPO_VERSION) $(REPO_URL) $(RELEASE_DIR)
$(SVNTAR) -f $(SVNTARBALL) $(RELEASE_DIR)
rm -rf $(RELEASE_DIR)
EXTRA_DIST = AUTHORS ChangeLog COPYING.LGPL INSTALL NEWS README

View File

@@ -14,14 +14,6 @@ PKG_PROG_PKG_CONFIG
AC_PATH_PROG([SWIG], [swig])
AC_MSG_CHECKING([whether the libapparmor debug output should be enabled])
AC_ARG_ENABLE([debug_output],
[AS_HELP_STRING([--enable-debug-output], [generate the libapparmor debug output [[default=no]]])],
[AC_MSG_RESULT([$enableval])],
[enable_debug_output=no]
[AC_MSG_RESULT([$enable_debug_output])])
AS_IF([test "$enable_debug_output" = "yes"], [AC_DEFINE([ENABLE_DEBUG_OUTPUT], [1], [debug output])])
AC_MSG_CHECKING([whether the libapparmor man pages should be generated])
AC_ARG_ENABLE(man_pages,
[AS_HELP_STRING([--enable-man-pages], [generate the libapparmor man pages [[default=yes]]])],
@@ -79,19 +71,14 @@ AM_CONDITIONAL(HAVE_PERL, test x$with_perl = xyes)
AM_CONDITIONAL(HAVE_RUBY, test x$with_ruby = xyes)
AC_HEADER_STDC
AC_CHECK_HEADERS(unistd.h stdint.h syslog.h)
AC_CHECK_HEADERS(unistd.h stdint.h)
AC_CHECK_FUNCS([asprintf __secure_getenv secure_getenv])
AC_CHECK_FUNCS(asprintf)
AM_PROG_CC_C_O
AC_C_CONST
AM_PROG_LIBTOOL
AC_PROG_CC_C99
if test "$ac_cv_prog_cc_c99" = "no"; then
AC_MSG_ERROR([C99 mode is required to build libapparmor])
fi
AC_OUTPUT(
Makefile
doc/Makefile

View File

@@ -5,14 +5,14 @@ PODCHECKER = podchecker
if ENABLE_MAN_PAGES
man_MANS = aa_change_hat.2 aa_change_profile.2 aa_getcon.2 aa_find_mountpoint.2 aa_splitcon.3 aa_query_label.2 aa_features.3 aa_kernel_interface.3 aa_policy_cache.3
man_MANS = aa_change_hat.2 aa_change_profile.2 aa_getcon.2 aa_find_mountpoint.2
PODS = $(subst .2,.pod,$(man_MANS)) $(subst .3,.pod,$(man_MANS))
PODS = $(subst .2,.pod,$(man_MANS))
EXTRA_DIST = $(man_MANS) $(PODS)
## delete man pages at make clean
CLEANFILES = $(man_MANS)
## delete man pages at maintainer-clean
BUILT_SOURCES = $(man_MANS)
%.2: %.pod
$(PODCHECKER) -warnings -warnings $<
@@ -23,13 +23,4 @@ CLEANFILES = $(man_MANS)
--stderr \
$< > $@
%.3: %.pod
$(PODCHECKER) -warnings -warnings $<
$(POD2MAN) \
--section=3 \
--release="AppArmor $(VERSION)" \
--center="AppArmor" \
--stderr \
$< > $@
endif

View File

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

View File

@@ -1,148 +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 adhere 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_features - an opaque object representing a set of AppArmor kernel features
aa_features_new - create a new aa_features object based on a path
aa_features_new_from_string - create a new aa_features object based on a string
aa_features_new_from_kernel - create a new aa_features object based on the current kernel
aa_features_ref - increments the ref count of an aa_features object
aa_features_unref - decrements the ref count and frees the aa_features object when 0
aa_features_write_to_file - write a string representation of an aa_features object to a file
aa_features_is_equal - equality test for two aa_features objects
aa_features_supports - provides aa_features object support status
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
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_string(aa_features **features, const char *string, size_t size);>
B<int aa_features_new_from_kernel(aa_features **features);>
B<aa_features *aa_features_ref(aa_features *features);>
B<void aa_features_unref(aa_features *features);>
B<int aa_features_write_to_file(aa_features *features, int dirfd, const char *path);>
B<bool aa_features_is_equal(aa_features *features1, aa_features *features2);>
B<bool aa_features_supports(aa_features *features, const char *str);>
Link with B<-lapparmor> when compiling.
=head1 DESCRIPTION
The I<aa_features> object contains information about the AppArmor features
supported by a kernel. The feature support information is based upon the files
AppArmor represents in securityfs, which is typically found at
/sys/kernel/security/apparmor/features/. That information may be parsed and
turned into a string or flat file in order to represent a set of features of a
kernel that is not currently running.
The aa_features_new() function creates an I<aa_features> object based upon a
directory file descriptor and path. The I<path> can point to a file or
directory. See the openat(2) man page for examples of I<dirfd> and I<path>. The
allocated I<features> object must be freed using aa_features_unref().
The aa_features_new_from_string() function is similar except that it accepts a
NUL-terminated string representation of the AppArmor features as the I<string>
argument. The length of the features string, not counting the NUL-terminator,
must be specified as the I<size> argument. The allocated I<features> object
must be freed using aa_features_unref().
The aa_features_new_from_kernel() function creates an I<aa_features> object
from the current running kernel. The allocated I<features> object must be freed
using aa_features_unref().
aa_features_ref() increments the reference count on the I<features> object.
aa_features_unref() decrements the reference count on the I<features> object
and releases all corresponding resources when the reference count reaches zero.
The aa_features_write_to_file() function writes a string representation of the
I<features> object to the file specified by the I<dirfd> and I<path>
combination.
aa_features_is_equal() can be used to detect if the I<features1> and
I<features2> objects are equal. The definition of equality is private to
libapparmor and may be changed in ways that do not break backward
compatibility.
The aa_features_supports() function can be used to query the I<features> object
to determine if a feature is supported. The I<str> argument should be equal to
the path, relative to the "apparmor/features/" directory of securityfs, of the
feature to query. For example, to test if policy version 6 is supported, I<str>
would be "policy/versions/v6".
=head1 RETURN VALUE
The aa_features_new() family of functions return 0 on success and I<*features>
will point to an I<aa_features> object that must be freed by
aa_features_unref(). -1 is returned on error, with errno set appropriately, and
I<*features> will be set to NULL.
aa_features_ref() returns the value of I<features>.
aa_features_write_to_file() returns 0 on success. -1 is returned on error, with
errno set appropriately.
aa_features_is_equal() returns true if I<features1> and I<features2> are equal
and false if they are not equal.
aa_features_supports() returns true if the feature represented by I<str> is
supported and false if it is not supported.
=head1 ERRORS
The errno value will be set according to the underlying error in the
I<aa_features> family of functions that return -1 on error.
=head1 NOTES
All aa_features functions described above are present in libapparmor version
2.10 and newer.
=head1 BUGS
None known. If you find any, please report them at
L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
openat(2) and L<http://wiki.apparmor.net>.
=cut

View File

@@ -34,38 +34,27 @@ B<#include E<lt>sys/apparmor.hE<gt>>
B<int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len, char **mode);>
B<int aa_getprocattr(pid_t tid, const char *attr, char **label, char **mode);>
B<int aa_getprocattr(pid_t tid, const char *attr, char **con, char **mode);>
B<int aa_gettaskcon(pid_t target, char **label, char **mode);>
B<int aa_gettaskcon(pid_t target, char **con, char **mode);>
B<int aa_getcon(char **label, char **mode);>
B<int aa_getcon(char **con, char **mode);>
B<int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode);>
B<int aa_getpeercon(int fd, char **label, char **mode);>
B<int aa_getpeercon(int fd, char **con, char **mode);>
Link with B<-lapparmor> when compiling.
=head1 DESCRIPTION
The aa_getcon function gets the current AppArmor confinement context for the
current task. The confinement context consists of a label and a mode. The label
is usually just the name of the AppArmor profile restricting the task, but it
may include the profile namespace or in some cases a set of profile names
(known as a stack of profiles). The mode is a string that describes how the
kernel is enforcing the policy defined in the profile. Profiles loaded in
"enforce" mode will result in enforcement of the policy defined in the profile
as well as reporting policy violation attempts. Profiles in "complain" mode
will not enforce policy but instead report policy violation attempts.
Some examples of possible returned *label strings are "unconfined", "/sbin/dhclient",
and "Firefox". The string can consist of any non-NUL characters but it will be
NUL-terminated. The *label string must be freed using free().
The possible *mode strings are "enforce" and "complain". Additionally, *mode may
be NULL when *label is "unconfined". B<The *mode string must not be freed>. The
*label and *mode strings come from a single buffer allocation and are separated
by a NUL character.
current task. The confinement context is usually just the name of the AppArmor
profile restricting the task, but it may include the profile namespace or in
some cases a set of profile names (known as a stack of profiles). The returned
string *con should be freed using free(), but the returned string *mode should
not be freed. The *con and *mode strings come from a single buffer allocation
and are separated by a NUL character.
The aa_gettaskcon function is like the aa_getcon function except it will work
for any arbitrary task in the system.
@@ -131,7 +120,7 @@ L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2),
aa_splitcon(3) and L<http://wiki.apparmor.net>.
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2) and
L<http://wiki.apparmor.net>.
=cut

View File

@@ -1,162 +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 adhere 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_kernel_interface - an opaque object representing the AppArmor kernel interface for policy loading, replacing, and removing
aa_kernel_interface_new - create a new aa_kernel_interface object from an optional path
aa_kernel_interface_ref - increments the ref count of an aa_kernel_interface object
aa_kernel_interface_unref - decrements the ref count and frees the aa_kernel_interface object when 0
aa_kernel_interface_load_policy - load a policy from a buffer into the kernel
aa_kernel_interface_load_policy_from_file - load a policy from a file into the kernel
aa_kernel_interface_load_policy_from_fd - load a policy from a file descriptor into the kernel
aa_kernel_interface_replace_policy - replace a policy in the kernel with a policy from a buffer
aa_kernel_interface_replace_policy_from_file - replace a policy in the kernel with a policy from a file
aa_kernel_interface_replace_policy_from_fd - replace a policy in the kernel with a policy from a file descriptor
aa_kernel_interface_remove_policy - remove a policy from the kernel
aa_kernel_interface_write_policy - write a policy to a file descriptor
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
B<typedef struct aa_kernel_interface aa_kernel_interface;>
B<int aa_kernel_interface_new(aa_kernel_interface **kernel_interface, aa_features *kernel_features, const char *apparmorfs);>
B<aa_kernel_interface *aa_kernel_interface_ref(aa_kernel_interface *kernel_interface);>
B<void aa_kernel_interface_unref(aa_kernel_interface *kernel_interface);>
B<int aa_kernel_interface_load_policy(aa_kernel_interface *kernel_interface, const char *buffer, size_t size);>
B<int aa_kernel_interface_load_policy_from_file(aa_kernel_interface *kernel_interface, int dirfd, const char *path);>
B<int aa_kernel_interface_load_policy_from_fd(aa_kernel_interface *kernel_interface, int fd);>
B<int aa_kernel_interface_replace_policy(aa_kernel_interface *kernel_interface, const char *buffer, size_t size);>
B<int aa_kernel_interface_replace_policy_from_file(aa_kernel_interface *kernel_interface, int dirfd, const char *path);>
B<int aa_kernel_interface_replace_policy_from_fd(aa_kernel_interface *kernel_interface, int fd);>
B<int aa_kernel_interface_remove_policy(aa_kernel_interface *kernel_interface, const char *fqname);>
B<int aa_kernel_interface_write_policy(int fd, const char *buffer, size_t size);>
Link with B<-lapparmor> when compiling.
=head1 DESCRIPTION
The I<aa_kernel_interface> object contains information about the AppArmor
kernel interface for policy loading, replacing, and removing.
The aa_kernel_interface_new() function creates an I<aa_kernel_interface> object
based on an optional I<aa_features> object and an optional path to the apparmor
directory of securityfs, which is typically found at
"/sys/kernel/security/apparmor/". If I<kernel_features> is NULL, then the
features of the current kernel are used. When specifying a valid
I<kernel_features> object, it must be compatible with the features of the
currently running kernel. If I<apparmorfs> is NULL, then the default location
is used. The allocated I<kernel_interface> object must be freed using
aa_kernel_interface_unref().
aa_kernel_interface_ref() increments the reference count on the
I<kernel_interface> object.
aa_kernel_interface_unref() decrements the reference count on the
I<kernel_interface> object and releases all corresponding resources when the
reference count reaches zero.
The aa_kernel_interface_load() family of functions load a policy into the
kernel. The operation will fail if a policy of the same name is already loaded.
Use the aa_kernel_interface_replace() family of functions if you wish to
replace a previously loaded policy with a new policy of the same name. The
aa_kernel_interface_replace() functions can also be used to load a policy that
does not correspond to a previously loaded policy.
When loading or replacing from a buffer, the I<buffer> will contain binary
data. The I<size> argument must specify the size of the I<buffer> argument.
When loading or replacing from a file, the I<dirfd> and I<path> combination are
used to specify the location of the file. See the openat(2) man page for
examples of I<dirfd> and I<path>.
It is also possible to load or replace from a file descriptor specified by the
I<fd> argument. The file must be open for reading and the file offset must be
set appropriately.
The aa_kernel_interface_remove_policy() function can be used to unload a
previously loaded policy. The fully qualified policy name must be specified
with the I<fqname> argument. The operation will fail if a policy matching
I<fqname> is not found.
The aa_kernel_interface_write_policy() function allows for a policy, which is
stored in I<buffer> and consists of I<size> bytes, to be written to a file
descriptor. The I<fd> must be open for writing and the file offset must be set
appropriately.
=head1 RETURN VALUE
The aa_kernel_interface_new() function returns 0 on success and
I<*kernel_interface> will point to an I<aa_kernel_interface> object that must
be freed by aa_kernel_interface_unref(). -1 is returned on error, with errno
set appropriately, and I<*kernel_interface> will be set to NULL.
aa_kernel_features_ref() returns the value of I<kernel_features>.
The aa_kernel_interface_load() family of functions, the
aa_kernel_interface_replace() family of functions,
aa_kernel_interface_remove(), and aa_kernel_interface_write_policy()
return 0 on success. -1 is returned on error, with errno set appropriately.
=head1 ERRORS
The errno value will be set according to the underlying error in the
I<aa_kernel_interface> family of functions that return -1 on error.
=head1 NOTES
All aa_kernel_interface functions described above are present in libapparmor
version 2.10 and newer.
=head1 BUGS
None known. If you find any, please report them at
L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
aa_features(3), openat(2) and L<http://wiki.apparmor.net>.
=cut

View File

@@ -1,125 +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 adhere 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_policy_cache - an opaque object representing an AppArmor policy cache
aa_policy_cache_new - create a new aa_policy_cache object from a path
aa_policy_cache_ref - increments the ref count of an aa_policy_cache object
aa_policy_cache_unref - decrements the ref count and frees the aa_policy_cache object when 0
aa_policy_cache_remove - removes all policy cache files under a path
aa_policy_cache_replace_all - performs a kernel policy replacement of all cached policies
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
B<typedef struct aa_policy_cache aa_policy_cache;>
B<int aa_policy_cache_new(aa_policy_cache **policy_cache, aa_features *kernel_features, int dirfd, const char *path, uint16_t max_caches);>
B<aa_policy_cache *aa_policy_cache_ref(aa_policy_cache *policy_cache);>
B<void aa_policy_cache_unref(aa_policy_cache *policy_cache);>
B<int aa_policy_cache_remove(int dirfd, const char *path);>
B<int aa_policy_cache_replace_all(aa_policy_cache *policy_cache, aa_kernel_interface *kernel_interface);>
Link with B<-lapparmor> when compiling.
=head1 DESCRIPTION
The I<aa_policy_cache> object contains information about a set of AppArmor
policy cache files. The policy cache files are the binary representation of a
human-readable AppArmor profile. The binary representation is the form that is
loaded into the kernel.
The aa_policy_cache_new() function creates an I<aa_policy_cache> object based
upon a directory file descriptor and path. The I<path> must point to a
directory. See the openat(2) man page for examples of I<dirfd> and I<path>. If
I<kernel_features> is NULL, then the features of the current kernel are used.
When specifying a valid I<kernel_features> object, it must be the compatible
with the features of the kernel of interest. The value of I<max_caches> should
be equal to the number of caches that should be allowed before old caches are
automatically reaped. The definition of what is considered to be an old cache
is private to libapparmor. Specifying 0 means that no new caches should be
created and only existing, valid caches may be used. Specifying UINT16_MAX
means that a new cache may be created and that the reaping of old caches is
disabled. The allocated I<aa_policy_cache> object must be freed using
aa_policy_cache_unref().
aa_policy_cache_ref() increments the reference count on the I<policy_cache>
object.
aa_policy_cache_unref() decrements the reference count on the I<policy_cache>
object and releases all corresponding resources when the reference count
reaches zero.
The aa_policy_cache_remove() function deletes all of the policy cache files
based upon a directory file descriptor and path. The I<path> must point to a
directory. See the openat(2) man page for examples of I<dirfd> and I<path>.
The aa_policy_cache_replace_all() function can be used to perform a policy
replacement of all of the cache policies in the cache directory represented by
the I<policy_cache> object. If I<kernel_interface> is NULL, then the current
kernel interface is used. When specifying a valid I<kernel_interface> object,
it must be the interface of the currently running kernel.
=head1 RETURN VALUE
The aa_policy_cache_new() function returns 0 on success and I<*policy_cache>
will point to an I<aa_policy_cache> object that must be freed by
aa_policy_cache_unref(). -1 is returned on error, with errno set appropriately,
and I<*policy_cache> will be set to NULL.
aa_policy_cache_ref() returns the value of I<policy_cache>.
aa_policy_cache_remove() and aa_policy_cache_replace_all() return 0 on success.
-1 is returned on error, with errno set appropriately.
=head1 ERRORS
The errno value will be set according to the underlying error in the
I<aa_policy_cache> family of functions that return -1 on error.
=head1 NOTES
All aa_policy_cache functions described above are present in libapparmor
version 2.10 and newer.
=head1 BUGS
None known. If you find any, please report them at
L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
aa_features(3), aa_kernel_interface(3), openat(2) and
L<http://wiki.apparmor.net>.
=cut

View File

@@ -1,137 +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 adhere 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_query_label - query access permission associated with a label
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
B<int aa_query_label((uint32_t mask, char *query, size_t size,
int *allowed, int *audited);>
B<int aa_query_file_path((uint32_t mask, const char *label, size_t label_len,
const char *path, int *allowed, int *audited);>
B<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);>
B<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);>
B<int aa_query_link_path(const char *label, const char *target,
const char *link, int *allowed, int *audited);>
Link with B<-lapparmor> when compiling.
=head1 DESCRIPTION
The aa_query_label function fetches the current permissions granted by the
specified I<label> in the I<query> string.
The query is a raw binary formatted query, containing the label and
permission query to make. The returned I<allowed> and I<audited> values are
interpreted boolean values, simple stating whether the query is allowed and
if it is audited.
The mask of the query string is a bit mask of permissions to query and is
class type dependent (see AA_CLASS_xxx) entries in I<sys/apparmor.h>.
The format of the query string is also dependent on the B<AA_CLASS> and as
such the the aa_query_xxx helper functions should usually be used instead
of directly using I<aa_query_label>. If directly using the interface the
I<query> string is required to have a header of B<AA_QUERY_CMD_LABEL_SIZE>
that will be used by I<aa_query_label>.
The B<aa_query_file_path> and B<aa_query_file_path_len> functions are helper
function that assemble a properly formatted file path query for the
B<aa_query_label> function. The I<label> is a valid apparmor label as
returned by I<aa_splitcon> with I<label_len> being the length of the I<label>.
The I<path> is any valid filesystem path to query permissions for. For the
B<aa_query_file_path_len> variant the I<path_len> parameter specifies the
number of bytes in the I<path> to use as part of the query.
The B<aa_query_link_path> and B<aa_query_link_path_len> functions are helper
functions that assemble a properly formatted link path query for the
B<aa_query_label> function. The I<link_len> and I<target_len> parameters
specify the number of bytes in the I<link> and I<target> to use as part of
the query.
=head1 RETURN VALUE
On success 0 is returned, and the I<allowed> and I<audited> parameters
contain a boolean value of 0 not allowed/audited or 1 allowed/audited. On
error, -1 is returned, and errno(3) is set appropriately.
=head1 ERRORS
=over 4
=item B<EINVAL>
The requested I<mask> is empty.
The I<size> of the query is E<lt> the query B<AA_QUER?Y_CMD_LABEL_SIZE>
The apparmor kernel module is not loaded or the he kernel interface access
interface is not available
=item B<ENOMEM>
Insufficient memory was available.
=item B<EACCES>
Access to the specified I<label> or query interface was denied.
=item B<ENOENT>
The specified I<label> does not exist or is not visible.
=item B<ERANGE>
The confinement data is too large to fit in the supplied buffer.
=back
=head1 NOTES
The label permissions returned are only valid for the time of the
query and can change at any point in the future.
=head1 BUGS
None known. If you find any, please report them at
L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_getcon(2), aa_splitcon(3)
and L<http://wiki.apparmor.net>.
=cut

View File

@@ -1,72 +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 adhere 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_splitcon - split the confinement context into a label and mode
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
B<char *aa_splitcon(char *con, char **mode);>
Link with B<-lapparmor> when compiling.
=head1 DESCRIPTION
The aa_splitcon() function splits a confinement context into separate label
and mode strings. The @con string is modified so that the label portion is NUL
terminated. The enforcement mode is also NUL terminated and the parenthesis
surrounding the mode are removed. If @mode is non-NULL, it will point to the
first character in the enforcement mode string on success.
The Linux kernel's /proc/E<lt>PIDE<gt>/attr/current interface appends a
trailing newline character to AppArmor contexts that are read from that file.
If @con contains a single trailing newline character, it will be stripped by
aa_splitcon() prior to all other processing.
=head1 RETURN VALUE
Returns a pointer to the first character in the label string. NULL is returned
on error.
=head1 EXAMPLE
Context Label Mode
----------------------------- ------------------ -------
unconfined unconfined NULL
unconfined\n unconfined NULL
/bin/ping (enforce) /bin/ping enforce
/bin/ping (enforce)\n /bin/ping enforce
/usr/sbin/rsyslogd (complain) /usr/sbin/rsyslogd complain
=head1 BUGS
None known. If you find any, please report them at
L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
aa_getcon(2) and L<http://wiki.apparmor.net>.
=cut

View File

@@ -1,3 +1,3 @@
apparmor_hdrdir = $(includedir)/sys
apparmor_hdr_HEADERS = apparmor.h apparmor_private.h
apparmor_hdr_HEADERS = apparmor.h

View File

@@ -18,7 +18,6 @@
#ifndef _SYS_APPARMOR_H
#define _SYS_APPARMOR_H 1
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
@@ -27,31 +26,10 @@ __BEGIN_DECLS
/*
* Class of public mediation types in the AppArmor policy db
*/
#define AA_CLASS_FILE 2
#define AA_CLASS_DBUS 32
/* 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
/* Permission flags for the AA_CLASS_DBUS mediation class */
#define AA_DBUS_SEND (1 << 1)
#define AA_DBUS_RECEIVE (1 << 2)
@@ -79,18 +57,16 @@ 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 char *aa_splitcon(char *con, char **mode);
/* Protypes for introspecting task confinement
* Please see the aa_getcon(2) manpage for information
*/
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 **label,
char **mode);
extern int aa_gettaskcon(pid_t target, char **label, char **mode);
extern int aa_getcon(char **label, char **mode);
extern int aa_getprocattr(pid_t tid, const char *attr, char **con, char **mode);
extern int aa_gettaskcon(pid_t target, char **con, char **mode);
extern int aa_getcon(char **con, char **mode);
extern int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode);
extern int aa_getpeercon(int fd, char **label, char **mode);
extern int aa_getpeercon(int fd, char **con, char **mode);
/* A NUL character is used to separate the query command prefix string from the
* rest of the query string. The query command sizes intentionally include the
@@ -101,17 +77,6 @@ extern int aa_getpeercon(int fd, char **label, char **mode);
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);
#define __macroarg_counter(Y...) __macroarg_count1 ( , ##Y)
#define __macroarg_count1(Y...) __macroarg_count2 (Y, 16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
@@ -137,58 +102,6 @@ extern int aa_query_link_path(const char *label, const char *target,
#define aa_change_hat_vargs(T, X...) \
(aa_change_hat_vargs)(T, __macroarg_counter(X), X)
typedef struct aa_features aa_features;
extern int aa_features_new(aa_features **features, int dirfd, const char *path);
extern int aa_features_new_from_string(aa_features **features,
const char *string, size_t size);
extern int aa_features_new_from_kernel(aa_features **features);
extern aa_features *aa_features_ref(aa_features *features);
extern void aa_features_unref(aa_features *features);
extern int aa_features_write_to_file(aa_features *features,
int dirfd, const char *path);
extern bool aa_features_is_equal(aa_features *features1,
aa_features *features2);
extern bool aa_features_supports(aa_features *features, const char *str);
typedef struct aa_kernel_interface aa_kernel_interface;
extern int aa_kernel_interface_new(aa_kernel_interface **kernel_interface,
aa_features *kernel_features,
const char *apparmorfs);
extern aa_kernel_interface *aa_kernel_interface_ref(aa_kernel_interface *kernel_interface);
extern void aa_kernel_interface_unref(aa_kernel_interface *kernel_interface);
extern int aa_kernel_interface_load_policy(aa_kernel_interface *kernel_interface,
const char *buffer, size_t size);
extern int aa_kernel_interface_load_policy_from_file(aa_kernel_interface *kernel_interface,
int dirfd,
const char *path);
extern int aa_kernel_interface_load_policy_from_fd(aa_kernel_interface *kernel_interface,
int fd);
extern int aa_kernel_interface_replace_policy(aa_kernel_interface *kernel_interface,
const char *buffer, size_t size);
extern int aa_kernel_interface_replace_policy_from_file(aa_kernel_interface *kernel_interface,
int dirfd,
const char *path);
extern int aa_kernel_interface_replace_policy_from_fd(aa_kernel_interface *kernel_interface,
int fd);
extern int aa_kernel_interface_remove_policy(aa_kernel_interface *kernel_interface,
const char *fqname);
extern int aa_kernel_interface_write_policy(int fd, const char *buffer,
size_t size);
typedef struct aa_policy_cache aa_policy_cache;
extern int aa_policy_cache_new(aa_policy_cache **policy_cache,
aa_features *kernel_features,
int dirfd, const char *path,
uint16_t max_caches);
extern aa_policy_cache *aa_policy_cache_ref(aa_policy_cache *policy_cache);
extern void aa_policy_cache_unref(aa_policy_cache *policy_cache);
extern int aa_policy_cache_remove(int dirfd, const char *path);
extern int aa_policy_cache_replace_all(aa_policy_cache *policy_cache,
aa_kernel_interface *kernel_interface);
__END_DECLS
#endif /* sys/apparmor.h */

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2014 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SYS_APPARMOR_PRIVATE_H
#define _SYS_APPARMOR_PRIVATE_H 1
#include <stdio.h>
#include <sys/stat.h>
__BEGIN_DECLS
int _aa_is_blacklisted(const char *name);
void _aa_autofree(void *p);
void _aa_autoclose(int *fd);
void _aa_autofclose(FILE **f);
int _aa_asprintf(char **strp, const char *fmt, ...);
int _aa_dirat_for_each(int dirfd, const char *name, void *data,
int (* cb)(int, const char *, struct stat *, void *));
__END_DECLS
#endif /* sys/apparmor_private.h */

View File

@@ -26,9 +26,9 @@ INCLUDES = $(all_includes)
# For more information, see:
# http://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
#
AA_LIB_CURRENT = 4
AA_LIB_REVISION = 0
AA_LIB_AGE = 3
AA_LIB_CURRENT = 3
AA_LIB_REVISION = 1
AA_LIB_AGE = 2
SUFFIXES = .pc.in .pc
@@ -46,9 +46,9 @@ af_protos.h: /usr/include/netinet/in.h
LC_ALL=C sed -n -e "/IPPROTO_MAX/d" -e "s/^\#define[ \\t]\\+IPPROTO_\\([A-Z0-9_]\\+\\)\\(.*\\)$$/AA_GEN_PROTO_ENT(\\UIPPROTO_\\1, \"\\L\\1\")/p" $< > $@
lib_LTLIBRARIES = libapparmor.la
noinst_HEADERS = grammar.h parser.h scanner.h af_protos.h private.h
noinst_HEADERS = grammar.h parser.h scanner.h af_protos.h
libapparmor_la_SOURCES = grammar.y libaalogparse.c kernel.c scanner.c private.c features.c kernel_interface.c policy_cache.c
libapparmor_la_SOURCES = grammar.y libaalogparse.c kernel_interface.c scanner.c
libapparmor_la_LDFLAGS = -version-info $(AA_LIB_CURRENT):$(AA_LIB_REVISION):$(AA_LIB_AGE) -XCClinker -dynamic -pthread \
-Wl,--version-script=$(top_srcdir)/src/libapparmor.map
@@ -63,15 +63,7 @@ CLEANFILES = libapparmor.pc
tst_aalogmisc_SOURCES = tst_aalogmisc.c
tst_aalogmisc_LDADD = .libs/libapparmor.a
tst_features_SOURCES = tst_features.c
tst_features_LDADD = .libs/libapparmor.a
tst_kernel_SOURCES = tst_kernel.c
tst_kernel_LDADD = .libs/libapparmor.a
tst_kernel_LDFLAGS = -pthread
check_PROGRAMS = tst_aalogmisc tst_features tst_kernel
check_PROGRAMS = tst_aalogmisc
TESTS = $(check_PROGRAMS)
EXTRA_DIST = grammar.y scanner.l libapparmor.map libapparmor.pc

View File

@@ -1,561 +0,0 @@
/*
* Copyright (c) 2014
* Canonical, Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#include <errno.h>
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/apparmor.h>
#include "private.h"
#define FEATURES_FILE "/sys/kernel/security/apparmor/features"
#define STRING_SIZE 8192
struct aa_features {
unsigned int ref_count;
char string[STRING_SIZE];
};
struct features_struct {
char *buffer;
int size;
char *pos;
};
struct component {
const char *str;
size_t len;
};
static int features_snprintf(struct features_struct *fst, const char *fmt, ...)
{
va_list args;
int i, remaining = fst->size - (fst->pos - fst->buffer);
if (remaining < 0) {
errno = EINVAL;
PERROR("Invalid features buffer offset\n");
return -1;
}
va_start(args, fmt);
i = vsnprintf(fst->pos, remaining, fmt, args);
va_end(args);
if (i < 0) {
errno = EIO;
PERROR("Failed to write to features buffer\n");
return -1;
} else if (i >= remaining) {
errno = ENOBUFS;
PERROR("Feature buffer full.");
return -1;
}
fst->pos += i;
return 0;
}
/* load_features_file - opens and reads a file into @buffer and then NUL-terminates @buffer
* @dirfd: a directory file descriptory or AT_FDCWD (see openat(2))
* @path: name of the file
* @buffer: the buffer to read the features file into (will be NUL-terminated on success)
* @size: the size of @buffer
*
* Returns: The number of bytes copied into @buffer on success (not counting
* the NUL-terminator), else -1 and errno is set. Note that @size must be
* larger than the size of the file or -1 will be returned with errno set to
* ENOBUFS indicating that @buffer was not large enough to contain all of the
* file contents.
*/
static int load_features_file(int dirfd, const char *path,
char *buffer, size_t size)
{
autoclose int file = -1;
char *pos = buffer;
ssize_t len;
file = openat(dirfd, path, O_RDONLY);
if (file < 0) {
PDEBUG("Could not open '%s'\n", path);
return -1;
}
PDEBUG("Opened features \"%s\"\n", path);
if (!size) {
errno = ENOBUFS;
return -1;
}
/* Save room for a NUL-terminator at the end of @buffer */
size--;
do {
len = read(file, pos, size);
if (len > 0) {
size -= len;
pos += len;
}
} while (len > 0 && size);
/**
* Possible error conditions:
* - len == -1: read failed and errno is already set so return -1
* - len > 0: read() never returned 0 (EOF) meaning that @buffer was
* too small to contain all of the file contents so set
* errno to ENOBUFS and return -1
*/
if (len) {
if (len > 0)
errno = ENOBUFS;
PDEBUG("Error reading features file '%s': %m\n", path);
return -1;
}
/* NUL-terminate @buffer */
*pos = 0;
return pos - buffer;
}
static int features_dir_cb(int dirfd, const char *name, struct stat *st,
void *data)
{
struct features_struct *fst = (struct features_struct *) data;
/* skip dot files and files with no name */
if (*name == '.' || !strlen(name))
return 0;
if (features_snprintf(fst, "%s {", name) == -1)
return -1;
if (S_ISREG(st->st_mode)) {
int len;
int remaining = fst->size - (fst->pos - fst->buffer);
len = load_features_file(dirfd, name, fst->pos, remaining);
if (len < 0)
return -1;
fst->pos += len;
} else if (S_ISDIR(st->st_mode)) {
if (_aa_dirat_for_each(dirfd, name, fst, features_dir_cb))
return -1;
}
if (features_snprintf(fst, "}\n") == -1)
return -1;
return 0;
}
static int load_features_dir(int dirfd, const char *path,
char *buffer, int size)
{
struct features_struct fst = { buffer, size, buffer };
if (_aa_dirat_for_each(dirfd, path, &fst, features_dir_cb)) {
PDEBUG("Failed evaluating %s\n", path);
return -1;
}
return 0;
}
static bool islbrace(int c)
{
return c == '{';
}
static bool isrbrace(int c)
{
return c == '}';
}
static bool isnul(int c)
{
return c == '\0';
}
static bool isbrace(int c)
{
return islbrace(c) || isrbrace(c);
}
static bool isbrace_or_nul(int c)
{
return isbrace(c) || isnul(c);
}
static bool isbrace_space_or_nul(int c)
{
return isbrace(c) || isspace(c) || isnul(c);
}
static size_t tokenize_path_components(const char *str,
struct component *components,
size_t max_components)
{
size_t i = 0;
memset(components, 0, sizeof(*components) * max_components);
if (!str)
return 0;
while (*str && i < max_components) {
const char *fwdslash = strchrnul(str, '/');
/* Save the token if it is not "/" */
if (fwdslash != str) {
components[i].str = str;
components[i].len = fwdslash - str;
i++;
}
if (isnul(*fwdslash))
break;
str = fwdslash + 1;
}
return i;
}
/**
* walk_one - walk one component of a features string
* @str: a pointer to the current position in a features string
* @component: the component to walk
* @is_top_level: true if component is a top-level component
*
* Imagine a features string such as:
*
* "feat1 { subfeat1.1 subfeat1.2 } feat2 { subfeat2.1 { subfeat2.1.1 } }"
*
* You want to know if "feat2/subfeat2.1/subfeat2.8" is valid. It will take 3
* invocations of this function to determine if that string is valid. On the
* first call, *@str will point to the beginning of the features string,
* component->str will be "feat2", and is_top_level will be true since feat2 is
* a top level feature. The function will return true and *@str will now point
* at the the left brace after "feat2" in the features string. You can call
* this function again with component->str being equal to "subfeat2.1" and it
* will return true and *@str will now point at the left brace after
* "subfeat2.1" in the features string. A third call to the function, with
* component->str equal to "subfeat2.8", will return false and *@str will not
* be changed.
*
* Returns true if the walk was successful and false otherwise. If the walk was
* successful, *@str will point to the first encountered brace after the walk.
* If the walk was unsuccessful, *@str is not updated.
*/
static bool walk_one(const char **str, const struct component *component,
bool is_top_level)
{
const char *cur;
uint32_t brace_count = 0;
size_t i = 0;
/* NULL pointers and empty strings are not accepted */
if (!str || !*str || !component || !component->str || !component->len)
return false;
cur = *str;
/**
* If @component is not top-level, the first character in the string to
* search MUST be '{'
*/
if (!is_top_level) {
if (!islbrace(*cur))
return false;
cur++;
}
/**
* This loop tries to find the @component in *@str. When this loops
* completes, cur will either point one character past the end of the
* matched @component or to the NUL terminator of *@str.
*/
while(!isnul(*cur) && i < component->len) {
if (!isascii(*cur)) {
/* Only ASCII is expected */
return false;
} else if (islbrace(*cur)) {
/* There's a limit to the number of left braces */
if (brace_count == UINT32_MAX)
return false;
brace_count++;
} else if (isrbrace(*cur)) {
/* Check for unexpected right braces */
if (brace_count == 0)
return false;
brace_count--;
}
/**
* Move to the next character in @component if we're not inside
* of braces and we have a character match
*/
if (brace_count == 0 && *cur == component->str[i])
i++;
else
i = 0;
cur++;
}
/* Return false if a full match was not found */
if (i != component->len) {
return false;
} else if (!isbrace_space_or_nul(*cur))
return false;
/**
* This loop eats up valid (ASCII) characters until a brace or NUL
* character is found so that *@str is properly set to call back into
* this function
*/
while (!isbrace_or_nul(*cur)) {
if (!isascii(*cur))
return false;
cur++;
}
*str = cur;
return true;
}
/**
* aa_features_new - create a new aa_features object based on a path
* @features: will point to the address of an allocated and initialized
* aa_features object upon success
* @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
* @path: path to a features file or directory
*
* Returns: 0 on success, -1 on error with errno set and *@features pointing to
* NULL
*/
int aa_features_new(aa_features **features, int dirfd, const char *path)
{
struct stat stat_file;
aa_features *f;
int retval;
*features = NULL;
if (fstatat(dirfd, path, &stat_file, 0) == -1)
return -1;
f = calloc(1, sizeof(*f));
if (!f) {
errno = ENOMEM;
return -1;
}
aa_features_ref(f);
retval = S_ISDIR(stat_file.st_mode) ?
load_features_dir(dirfd, path, f->string, STRING_SIZE) :
load_features_file(dirfd, path, f->string, STRING_SIZE);
if (retval == -1) {
int save = errno;
aa_features_unref(f);
errno = save;
return -1;
}
*features = f;
return 0;
}
/**
* aa_features_new_from_string - create a new aa_features object based on a string
* @features: will point to the address of an allocated and initialized
* aa_features object upon success
* @string: a NUL-terminated string representation of features
* @size: the size of @string, not counting the NUL-terminator
*
* Returns: 0 on success, -1 on error with errno set and *@features pointing to
* NULL
*/
int aa_features_new_from_string(aa_features **features,
const char *string, size_t size)
{
aa_features *f;
*features = NULL;
/* Require size to be less than STRING_SIZE so there's room for a NUL */
if (size >= STRING_SIZE)
return ENOBUFS;
f = calloc(1, sizeof(*f));
if (!f) {
errno = ENOMEM;
return -1;
}
aa_features_ref(f);
memcpy(f->string, string, size);
f->string[size] = '\0';
*features = f;
return 0;
}
/**
* aa_features_new_from_kernel - create a new aa_features object based on the current kernel
* @features: will point to the address of an allocated and initialized
* aa_features object upon success
*
* Returns: 0 on success, -1 on error with errno set and *@features pointing to
* NULL
*/
int aa_features_new_from_kernel(aa_features **features)
{
return aa_features_new(features, -1, FEATURES_FILE);
}
/**
* aa_features_ref - increments the ref count of an aa_features object
* @features: the features
*
* Returns: the features
*/
aa_features *aa_features_ref(aa_features *features)
{
atomic_inc(&features->ref_count);
return features;
}
/**
* aa_features_unref - decrements the ref count and frees the aa_features object when 0
* @features: the features (can be NULL)
*/
void aa_features_unref(aa_features *features)
{
if (features && atomic_dec_and_test(&features->ref_count))
free(features);
}
/**
* aa_features_write_to_file - write a string representation of an aa_features object to a file
* @features: the features
* @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
* @path: the path to write to
*
* Returns: 0 on success, -1 on error with errno set
*/
int aa_features_write_to_file(aa_features *features,
int dirfd, const char *path)
{
autoclose int fd = -1;
size_t size;
ssize_t retval;
char *string;
fd = openat(dirfd, path,
O_WRONLY | O_CREAT | O_TRUNC | O_SYNC | O_CLOEXEC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd == -1)
return -1;
string = features->string;
size = strlen(string);
do {
retval = write(fd, string, size);
if (retval == -1)
return -1;
size -= retval;
string += retval;
} while (size);
return 0;
}
/**
* aa_features_is_equal - equality test for two aa_features objects
* @features1: the first features (can be NULL)
* @features2: the second features (can be NULL)
*
* Returns: true if they're equal, false if they're not or either are NULL
*/
bool aa_features_is_equal(aa_features *features1, aa_features *features2)
{
return features1 && features2 &&
strcmp(features1->string, features2->string) == 0;
}
/**
* aa_features_supports - provides aa_features object support status
* @features: the features
* @str: the string representation of a feature to check
*
* Example @str values are "dbus/mask/send", "caps/mask/audit_read", and
* "policy/versions/v7".
*
* Returns: a bool specifying the support status of @str feature
*/
bool aa_features_supports(aa_features *features, const char *str)
{
const char *features_string = features->string;
struct component components[32];
size_t i, num_components;
/* Empty strings are not accepted. Neither are leading '/' chars. */
if (!str || str[0] == '/')
return false;
/**
* Break @str into an array of components. For example,
* "mount/mask/mount" would turn into { "mount", 5 } as the first
* component, { "mask", 4 } as the second, and { "mount", 5 } as the
* third
*/
num_components = tokenize_path_components(str, components,
sizeof(components) / sizeof(*components));
/* At least one valid token is required */
if (!num_components)
return false;
/* Ensure that all components are valid and found */
for (i = 0; i < num_components; i++) {
if (!walk_one(&features_string, &components[i], i == 0))
return false;
}
return true;
}

View File

@@ -166,15 +166,9 @@ aa_record_event_type lookup_aa_event(unsigned int type)
%token TOK_SYSLOG_KERNEL
%token TOK_SYSLOG_USER
%destructor { free($$); } TOK_QUOTED_STRING TOK_ID TOK_MODE TOK_DMESG_STAMP
%destructor { free($$); } TOK_AUDIT_DIGITS TOK_DATE_MONTH TOK_DATE TOK_TIME
%destructor { free($$); } TOK_HEXSTRING TOK_TYPE_OTHER TOK_MSG_REST
%destructor { free($$); } TOK_IP_ADDR
%%
log_message: audit_type
| dmesg_type
| syslog_type
| audit_dispatch
;
@@ -205,10 +199,6 @@ other_audit: TOK_TYPE_OTHER audit_msg TOK_MSG_REST
}
;
dmesg_type: TOK_DMESG_STAMP TOK_AUDIT TOK_COLON key_type audit_id key_list
{ ret_record->version = AA_RECORD_SYNTAX_V2; free($1); }
;
syslog_type:
syslog_date TOK_ID TOK_SYSLOG_KERNEL audit_id key_list
{ ret_record->version = AA_RECORD_SYNTAX_V2; free($2); }
@@ -428,7 +418,7 @@ _parse_yacc(char *str)
int parser_return;
ret_record = NULL;
ret_record = malloc(sizeof(aa_log_record));
ret_record = (aa_log_record *) malloc(sizeof(aa_log_record));
_init_log_record(ret_record);

View File

@@ -1,982 +0,0 @@
/*
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2010 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <mntent.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/apparmor.h>
#include "private.h"
/* some non-Linux systems do not define a static value */
#ifndef PATH_MAX
# define PATH_MAX 4096
#endif
#define symbol_version(real, name, version) \
__asm__ (".symver " #real "," #name "@" #version)
#define default_symbol_version(real, name, version) \
__asm__ (".symver " #real "," #name "@@" #version)
#define UNCONFINED "unconfined"
#define UNCONFINED_SIZE strlen(UNCONFINED)
/**
* aa_find_mountpoint - find where the apparmor interface filesystem is mounted
* @mnt: returns buffer with the mountpoint string
*
* Returns: 0 on success else -1 on error
*
* NOTE: this function only supports versions of apparmor using securityfs
*/
int aa_find_mountpoint(char **mnt)
{
struct stat statbuf;
struct mntent *mntpt;
FILE *mntfile;
int rc = -1;
if (!mnt) {
errno = EINVAL;
return -1;
}
mntfile = setmntent("/proc/mounts", "r");
if (!mntfile)
return -1;
while ((mntpt = getmntent(mntfile))) {
char *proposed = NULL;
if (strcmp(mntpt->mnt_type, "securityfs") != 0)
continue;
if (asprintf(&proposed, "%s/apparmor", mntpt->mnt_dir) < 0)
/* ENOMEM */
break;
if (stat(proposed, &statbuf) == 0) {
*mnt = proposed;
rc = 0;
break;
}
free(proposed);
}
endmntent(mntfile);
if (rc == -1)
errno = ENOENT;
return rc;
}
/**
* aa_is_enabled - determine if apparmor is enabled
*
* Returns: 1 if enabled else reason it is not, or 0 on error
*
* ENOSYS - no indication apparmor is present in the system
* ENOENT - enabled but interface could not be found
* ECANCELED - disabled at boot
* ENOMEM - out of memory
*/
int aa_is_enabled(void)
{
int serrno, fd, rc, size;
char buffer[2];
char *mnt;
/* if the interface mountpoint is available apparmor is enabled */
rc = aa_find_mountpoint(&mnt);
if (rc == 0) {
free(mnt);
return 1;
}
/* determine why the interface mountpoint isn't available */
fd = open("/sys/module/apparmor/parameters/enabled", O_RDONLY);
if (fd == -1) {
if (errno == ENOENT)
errno = ENOSYS;
return 0;
}
size = read(fd, &buffer, 2);
serrno = errno;
close(fd);
errno = serrno;
if (size > 0) {
if (buffer[0] == 'Y')
errno = ENOENT;
else
errno = ECANCELED;
}
return 0;
}
static inline pid_t aa_gettid(void)
{
#ifdef SYS_gettid
return syscall(SYS_gettid);
#else
return getpid();
#endif
}
static char *procattr_path(pid_t pid, const char *attr)
{
char *path = NULL;
if (asprintf(&path, "/proc/%d/attr/%s", pid, attr) > 0)
return path;
return NULL;
}
/**
* parse_unconfined - check for the unconfined label
* @con: the confinement context
* @size: size of the confinement context (not including the NUL terminator)
*
* Returns: True if the con is the unconfined label or false otherwise
*/
static bool parse_unconfined(char *con, int size)
{
return size == UNCONFINED_SIZE &&
strncmp(con, UNCONFINED, UNCONFINED_SIZE) == 0;
}
/**
* splitcon - split the confinement context into a label and mode
* @con: the confinement context
* @size: size of the confinement context (not including the NUL terminator)
* @strip_newline: true if a trailing newline character should be stripped
* @mode: if non-NULL and a mode is present, will point to mode string in @con
* on success
*
* Modifies the @con string to split it into separate label and mode strings.
* If @strip_newline is true and @con contains a single trailing newline, it
* will be stripped on success (it will not be stripped on error). The @mode
* argument is optional. If @mode is NULL, @con will still be split between the
* label and mode (if present) but @mode will not be set.
*
* Returns: a pointer to the label string or NULL on error
*/
static char *splitcon(char *con, int size, bool strip_newline, char **mode)
{
char *label = NULL;
char *mode_str = NULL;
char *newline = NULL;
if (size == 0)
goto out;
if (strip_newline && con[size - 1] == '\n') {
newline = &con[size - 1];
size--;
}
if (parse_unconfined(con, size)) {
label = con;
goto out;
}
if (size > 3 && con[size - 1] == ')') {
int pos = size - 2;
while (pos > 0 && !(con[pos] == ' ' && con[pos + 1] == '('))
pos--;
if (pos > 0) {
con[pos] = 0; /* overwrite ' ' */
con[size - 1] = 0; /* overwrite trailing ) */
mode_str = &con[pos + 2]; /* skip '(' */
label = con;
}
}
out:
if (label && strip_newline && newline)
*newline = 0; /* overwrite '\n', if requested, on success */
if (mode)
*mode = mode_str;
return label;
}
/**
* aa_splitcon - split the confinement context into a label and mode
* @con: the confinement context
* @mode: if non-NULL and a mode is present, will point to mode string in @con
* on success
*
* Modifies the @con string to split it into separate label and mode strings. A
* single trailing newline character will be stripped from @con, if found. The
* @mode argument is optional. If @mode is NULL, @con will still be split
* between the label and mode (if present) but @mode will not be set.
*
* Returns: a pointer to the label string or NULL on error
*/
char *aa_splitcon(char *con, char **mode)
{
return splitcon(con, strlen(con), true, mode);
}
/**
* aa_getprocattr_raw - get the contents of @attr for @tid into @buf
* @tid: tid of task to query
* @attr: which /proc/<tid>/attr/<attr> to query
* @buf: buffer to store the result in
* @len: size of the buffer
* @mode: if non-NULL and a mode is present, will point to mode string in @buf
*
* Returns: size of data read or -1 on error, and sets errno
*/
int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
char **mode)
{
int rc = -1;
int fd, ret;
char *tmp = NULL;
int size = 0;
if (!buf || len <= 0) {
errno = EINVAL;
goto out;
}
tmp = procattr_path(tid, attr);
if (!tmp)
goto out;
fd = open(tmp, O_RDONLY);
free(tmp);
if (fd == -1) {
goto out;
}
tmp = buf;
do {
ret = read(fd, tmp, len);
if (ret <= 0)
break;
tmp += ret;
size += ret;
len -= ret;
if (len < 0) {
errno = ERANGE;
goto out2;
}
} while (ret > 0);
if (ret < 0) {
int saved;
if (ret != -1) {
errno = EPROTO;
}
saved = errno;
(void)close(fd);
errno = saved;
goto out;
} else if (size > 0 && buf[size - 1] != 0) {
/* check for null termination */
if (buf[size - 1] != '\n') {
if (len == 0) {
errno = ERANGE;
goto out2;
} else {
buf[size] = 0;
size++;
}
}
if (splitcon(buf, size, true, mode) != buf) {
errno = EINVAL;
goto out2;
}
}
rc = size;
out2:
(void)close(fd);
out:
return rc;
}
#define INITIAL_GUESS_SIZE 128
/**
* aa_getprocattr - get the contents of @attr for @tid into @label and @mode
* @tid: tid of task to query
* @attr: which /proc/<tid>/attr/<attr> to query
* @label: allocated buffer the label is stored in
* @mode: if non-NULL and a mode is present, will point to mode string in @label
*
* Returns: size of data read or -1 on error, and sets errno
*
* Guarantees that @label and @mode are null terminated. The length returned
* is for all data including both @label and @mode, and maybe > than
* strlen(@label) even if @mode is NULL
*
* Caller is responsible for freeing the buffer returned in @label. @mode is
* always contained within @label's buffer and so NEVER do free(@mode)
*/
int aa_getprocattr(pid_t tid, const char *attr, char **label, char **mode)
{
int rc, size = INITIAL_GUESS_SIZE/2;
char *buffer = NULL;
if (!label) {
errno = EINVAL;
return -1;
}
do {
char *tmp;
size <<= 1;
tmp = realloc(buffer, size);
if (!tmp) {
free(buffer);
return -1;
}
buffer = tmp;
memset(buffer, 0, size);
rc = aa_getprocattr_raw(tid, attr, buffer, size, mode);
} while (rc == -1 && errno == ERANGE);
if (rc == -1) {
free(buffer);
*label = NULL;
if (mode)
*mode = NULL;
} else
*label = buffer;
return rc;
}
static int setprocattr(pid_t tid, const char *attr, const char *buf, int len)
{
int rc = -1;
int fd, ret;
char *ctl = NULL;
if (!buf) {
errno = EINVAL;
goto out;
}
ctl = procattr_path(tid, attr);
if (!ctl)
goto out;
fd = open(ctl, O_WRONLY);
if (fd == -1) {
goto out;
}
ret = write(fd, buf, len);
if (ret != len) {
int saved;
if (ret != -1) {
errno = EPROTO;
}
saved = errno;
(void)close(fd);
errno = saved;
goto out;
}
rc = 0;
(void)close(fd);
out:
if (ctl) {
free(ctl);
}
return rc;
}
int aa_change_hat(const char *subprofile, unsigned long token)
{
int rc = -1;
int len = 0;
char *buf = NULL;
const char *fmt = "changehat %016lx^%s";
/* both may not be null */
if (!(token || subprofile)) {
errno = EINVAL;
goto out;
}
if (subprofile && strnlen(subprofile, PATH_MAX + 1) > PATH_MAX) {
errno = EPROTO;
goto out;
}
len = asprintf(&buf, fmt, token, subprofile ? subprofile : "");
if (len < 0) {
goto out;
}
rc = setprocattr(aa_gettid(), "current", buf, len);
out:
if (buf) {
/* clear local copy of magic token before freeing */
memset(buf, '\0', len);
free(buf);
}
return rc;
}
/* original change_hat interface */
int __change_hat(char *subprofile, unsigned int token)
{
return aa_change_hat(subprofile, (unsigned long) token);
}
int aa_change_profile(const char *profile)
{
char *buf = NULL;
int len;
int rc;
if (!profile) {
errno = EINVAL;
return -1;
}
len = asprintf(&buf, "changeprofile %s", profile);
if (len < 0)
return -1;
rc = setprocattr(aa_gettid(), "current", buf, len);
free(buf);
return rc;
}
int aa_change_onexec(const char *profile)
{
char *buf = NULL;
int len;
int rc;
if (!profile) {
errno = EINVAL;
return -1;
}
len = asprintf(&buf, "exec %s", profile);
if (len < 0)
return -1;
rc = setprocattr(aa_gettid(), "exec", buf, len);
free(buf);
return rc;
}
/* create an alias for the old change_hat@IMMUNIX_1.0 symbol */
extern typeof((__change_hat)) __old_change_hat __attribute__((alias ("__change_hat")));
symbol_version(__old_change_hat, change_hat, IMMUNIX_1.0);
default_symbol_version(__change_hat, change_hat, APPARMOR_1.0);
int aa_change_hatv(const char *subprofiles[], unsigned long token)
{
int size, totallen = 0, hatcount = 0;
int rc = -1;
const char **hats;
char *pos, *buf = NULL;
const char *cmd = "changehat";
/* both may not be null */
if (!token && !(subprofiles && *subprofiles)) {
errno = EINVAL;
goto out;
}
/* validate hat lengths and while we are at it count how many and
* mem required */
if (subprofiles) {
for (hats = subprofiles; *hats; hats++) {
int len = strnlen(*hats, PATH_MAX + 1);
if (len > PATH_MAX) {
errno = EPROTO;
goto out;
}
totallen += len + 1;
hatcount++;
}
}
/* allocate size of cmd + space + token + ^ + vector of hats */
size = strlen(cmd) + 18 + totallen + 1;
buf = malloc(size);
if (!buf) {
goto out;
}
/* setup command string which is of the form
* changehat <token>^hat1\0hat2\0hat3\0..\0
*/
sprintf(buf, "%s %016lx^", cmd, token);
pos = buf + strlen(buf);
if (subprofiles) {
for (hats = subprofiles; *hats; hats++) {
strcpy(pos, *hats);
pos += strlen(*hats) + 1;
}
} else
/* step pos past trailing \0 */
pos++;
rc = setprocattr(aa_gettid(), "current", buf, pos - buf);
out:
if (buf) {
/* clear local copy of magic token before freeing */
memset(buf, '\0', size);
free(buf);
}
return rc;
}
/**
* change_hat_vargs - change_hatv but passing the hats as fn arguments
* @token: the magic token
* @nhat: the number of hats being passed in the arguments
* ...: a argument list of const char * being passed
*
* change_hat_vargs can be called directly but it is meant to be called
* through its macro wrapper of the same name. Which automatically
* fills in the nhats arguments based on the number of parameters
* passed.
* to call change_hat_vargs direction do
* (change_hat_vargs)(token, nhats, hat1, hat2...)
*/
int (aa_change_hat_vargs)(unsigned long token, int nhats, ...)
{
va_list ap;
const char *argv[nhats+1];
int i;
va_start(ap, nhats);
for (i = 0; i < nhats ; i++) {
argv[i] = va_arg(ap, char *);
}
argv[nhats] = NULL;
va_end(ap);
return aa_change_hatv(argv, token);
}
/**
* aa_gettaskcon - get the confinement context for task @target in an allocated buffer
* @target: task to query
* @label: pointer to returned buffer with the label
* @mode: if non-NULL and a mode is present, will point to mode string in @label
*
* Returns: length of confinement context or -1 on error and sets errno
*
* Guarantees that @label and @mode are null terminated. The length returned
* is for all data including both @label and @mode, and maybe > than
* strlen(@label) even if @mode is NULL
*
* Caller is responsible for freeing the buffer returned in @label. @mode is
* always contained within @label's buffer and so NEVER do free(@mode)
*/
int aa_gettaskcon(pid_t target, char **label, char **mode)
{
return aa_getprocattr(target, "current", label, mode);
}
/**
* aa_getcon - get the confinement context for current task in an allocated buffer
* @label: pointer to return buffer with the label if successful
* @mode: if non-NULL and a mode is present, will point to mode string in @label
*
* Returns: length of confinement context or -1 on error and sets errno
*
* Guarantees that @label and @mode are null terminated. The length returned
* is for all data including both @label and @mode, and may > than
* strlen(@label) even if @mode is NULL
*
* Caller is responsible for freeing the buffer returned in @label. @mode is
* always contained within @label's buffer and so NEVER do free(@mode)
*/
int aa_getcon(char **label, char **mode)
{
return aa_gettaskcon(aa_gettid(), label, mode);
}
#ifndef SO_PEERSEC
#define SO_PEERSEC 31
#endif
/**
* aa_getpeercon_raw - get the confinement context of the socket's peer (other end)
* @fd: socket to get peer confinement context for
* @buf: buffer to store the result in
* @len: initially contains size of the buffer, returns size of data read
* @mode: if non-NULL and a mode is present, will point to mode string in @buf
*
* Returns: length of confinement context including null termination or -1 on
* error if errno == ERANGE then @len will hold the size needed
*/
int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode)
{
socklen_t optlen = *len;
int rc;
if (optlen <= 0 || buf == NULL) {
errno = EINVAL;
return -1;
}
rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &optlen);
if (rc == -1 || optlen <= 0)
goto out;
/* check for null termination */
if (buf[optlen - 1] != 0) {
if (optlen < *len) {
buf[optlen] = 0;
optlen++;
} else {
/* buf needs to be bigger by 1 */
rc = -1;
errno = ERANGE;
optlen++;
goto out;
}
}
if (splitcon(buf, optlen - 1, false, mode) != buf) {
rc = -1;
errno = EINVAL;
goto out;
}
rc = optlen;
out:
*len = optlen;
return rc;
}
/**
* aa_getpeercon - get the confinement context of the socket's peer (other end)
* @fd: socket to get peer confinement context for
* @label: pointer to allocated buffer with the label
* @mode: if non-NULL and a mode is present, will point to mode string in @label
*
* Returns: length of confinement context including null termination or -1 on error
*
* Guarantees that @label and @mode are null terminated. The length returned
* is for all data including both @label and @mode, and maybe > than
* strlen(@label) even if @mode is NULL
*
* Caller is responsible for freeing the buffer returned in @label. @mode is
* always contained within @label's buffer and so NEVER do free(@mode)
*/
int aa_getpeercon(int fd, char **label, char **mode)
{
int rc, last_size, size = INITIAL_GUESS_SIZE;
char *buffer = NULL;
if (!label) {
errno = EINVAL;
return -1;
}
do {
char *tmp;
last_size = size;
tmp = realloc(buffer, size);
if (!tmp) {
free(buffer);
return -1;
}
buffer = tmp;
memset(buffer, 0, size);
rc = aa_getpeercon_raw(fd, buffer, &size, mode);
/* size should contain actual size needed if errno == ERANGE */
} while (rc == -1 && errno == ERANGE && size > last_size);
if (rc == -1) {
free(buffer);
*label = NULL;
if (mode)
*mode = NULL;
size = -1;
} else
*label = buffer;
return size;
}
static pthread_once_t aafs_access_control = PTHREAD_ONCE_INIT;
static char *aafs_access = NULL;
static void aafs_access_init_once(void)
{
char *aafs;
int ret;
ret = aa_find_mountpoint(&aafs);
if (ret < 0)
return;
ret = asprintf(&aafs_access, "%s/.access", aafs);
if (ret < 0)
aafs_access = NULL;
free(aafs);
}
/* "allow 0x00000000\ndeny 0x00000000\naudit 0x00000000\nquiet 0x00000000\n" */
#define QUERY_LABEL_REPLY_LEN 67
/**
* aa_query_label - query the access(es) of a label
* @mask: permission bits to query
* @query: binary query string, must be offset by AA_QUERY_CMD_LABEL_SIZE
* @size: size of the query string must include AA_QUERY_CMD_LABEL_SIZE
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
* @audited: upon successful return, will be 1 if query should be audited and 0
* if not
*
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
* ENOENT, the subject label in the query string is unknown to the
* kernel.
*/
int query_label(uint32_t mask, char *query, size_t size, int *allowed,
int *audited)
{
char buf[QUERY_LABEL_REPLY_LEN];
uint32_t allow, deny, audit, quiet;
int fd, ret, saved;
if (!mask || size <= AA_QUERY_CMD_LABEL_SIZE) {
errno = EINVAL;
return -1;
}
ret = pthread_once(&aafs_access_control, aafs_access_init_once);
if (ret) {
errno = EINVAL;
return -1;
} else if (!aafs_access) {
errno = ENOMEM;
return -1;
}
fd = open(aafs_access, O_RDWR);
if (fd == -1) {
if (errno == ENOENT)
errno = EPROTONOSUPPORT;
return -1;
}
memcpy(query, AA_QUERY_CMD_LABEL, AA_QUERY_CMD_LABEL_SIZE);
errno = 0;
ret = write(fd, query, size);
if (ret != size) {
if (ret >= 0)
errno = EPROTO;
/* IMPORTANT: This is the only valid error path that can have
* errno set to ENOENT. It indicates that the subject label
* could not be found by the kernel.
*/
(void)close(fd);
return -1;
}
ret = read(fd, buf, QUERY_LABEL_REPLY_LEN);
saved = errno;
(void)close(fd);
errno = saved;
if (ret != QUERY_LABEL_REPLY_LEN) {
errno = EPROTO;
return -1;
}
ret = sscanf(buf, "allow 0x%8" SCNx32 "\n"
"deny 0x%8" SCNx32 "\n"
"audit 0x%8" SCNx32 "\n"
"quiet 0x%8" SCNx32 "\n",
&allow, &deny, &audit, &quiet);
if (ret != 4) {
errno = EPROTONOSUPPORT;
return -1;
}
*allowed = mask & ~(allow & ~deny) ? 0 : 1;
if (!(*allowed))
audit = 0xFFFFFFFF;
*audited = mask & ~(audit & ~quiet) ? 0 : 1;
return 0;
}
/* export multiple aa_query_label symbols to compensate for downstream
* releases with differing symbol versions. */
extern typeof((query_label)) __aa_query_label __attribute__((alias ("query_label")));
symbol_version(__aa_query_label, aa_query_label, APPARMOR_1.1);
default_symbol_version(query_label, aa_query_label, APPARMOR_2.9);
/**
* aa_query_file_path_len - query access permissions for a file @path
* @mask: permission bits to query
* @label: apparmor label
* @label_len: length of @label (does not include any terminating nul byte)
* @path: file path to query permissions for
* @path_len: length of @path (does not include any terminating nul byte)
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
* @audited: upon successful return, will be 1 if query should be audited and 0
* if not
*
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
* ENOENT, the subject label in the query string is unknown to the
* kernel.
*/
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)
{
autofree char *query = NULL;
/* + 1 for null separator */
size_t size = AA_QUERY_CMD_LABEL_SIZE + label_len + 1 + path_len;
query = malloc(size + 1);
if (!query)
return -1;
memcpy(query + AA_QUERY_CMD_LABEL_SIZE, label, label_len);
/* null separator */
query[AA_QUERY_CMD_LABEL_SIZE + label_len] = 0;
query[AA_QUERY_CMD_LABEL_SIZE + label_len + 1] = AA_CLASS_FILE;
memcpy(query + AA_QUERY_CMD_LABEL_SIZE + label_len + 2, path, path_len);
return aa_query_label(mask, query, size , allowed, audited);
}
/**
* aa_query_file_path - query access permissions for a file @path
* @mask: permission bits to query
* @label: apparmor label
* @path: file path to query permissions for
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
* @audited: upon successful return, will be 1 if query should be audited and 0
* if not
*
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
* ENOENT, the subject label in the query string is unknown to the
* kernel.
*/
int aa_query_file_path(uint32_t mask, const char *label, const char *path,
int *allowed, int *audited)
{
return aa_query_file_path_len(mask, label, strlen(label), path,
strlen(path), allowed, audited);
}
/**
* aa_query_link_path_len - query access permissions for a hard link @link
* @label: apparmor label
* @label_len: length of @label (does not include any terminating nul byte)
* @target: file path that hard link will point to
* @target_len: length of @target (does not include any terminating nul byte)
* @link: file path of hard link
* @link_len: length of @link (does not include any terminating nul byte)
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
* @audited: upon successful return, will be 1 if query should be audited and 0
* if not
*
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
* ENOENT, the subject label in the query string is unknown to the
* kernel.
*/
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)
{
autofree char *query = NULL;
int rc;
/* + 1 for null separators */
size_t size = AA_QUERY_CMD_LABEL_SIZE + label_len + 1 + target_len +
1 + link_len;
size_t pos = AA_QUERY_CMD_LABEL_SIZE;
query = malloc(size);
if (!query)
return -1;
memcpy(query + pos, label, label_len);
/* null separator */
pos += label_len;
query[pos] = 0;
query[++pos] = AA_CLASS_FILE;
memcpy(query + pos + 1, link, link_len);
/* The kernel does the query in two parts we could similate this
* doing the following, however as long as policy is compiled
* correctly this isn't requied, and it requires and extra round
* trip to the kernel and adds a race on policy replacement between
* the two queries.
*
rc = aa_query_label(AA_MAY_LINK, query, size, allowed, audited);
if (rc || !*allowed)
return rc;
*/
pos += 1 + link_len;
query[pos] = 0;
memcpy(query + pos + 1, target, target_len);
return aa_query_label(AA_MAY_LINK, query, size, allowed, audited);
}
/**
* aa_query_link_path - query access permissions for a hard link @link
* @label: apparmor label
* @target: file path that hard link will point to
* @link: file path of hard link
* @allowed: upon successful return, will be 1 if query is allowed and 0 if not
* @audited: upon successful return, will be 1 if query should be audited and 0
* if not
*
* Returns: 0 on success else -1 and sets errno. If -1 is returned and errno is
* ENOENT, the subject label in the query string is unknown to the
* kernel.
*/
int aa_query_link_path(const char *label, const char *target, const char *link,
int *allowed, int *audited)
{
return aa_query_link_path_len(label, strlen(label), target,
strlen(target), link, strlen(link),
allowed, audited);
}

File diff suppressed because it is too large Load Diff

View File

@@ -51,49 +51,3 @@ APPARMOR_2.9 {
local:
*;
} APPARMOR_1.1;
APPARMOR_2.10 {
global:
aa_query_file_path;
aa_query_file_path_len;
aa_query_link_path;
aa_query_link_path_len;
aa_features_new;
aa_features_new_from_string;
aa_features_new_from_kernel;
aa_features_ref;
aa_features_unref;
aa_features_write_to_file;
aa_features_is_equal;
aa_features_supports;
aa_kernel_interface_new;
aa_kernel_interface_ref;
aa_kernel_interface_unref;
aa_kernel_interface_load_policy;
aa_kernel_interface_load_policy_from_file;
aa_kernel_interface_load_policy_from_fd;
aa_kernel_interface_replace_policy;
aa_kernel_interface_replace_policy_from_file;
aa_kernel_interface_replace_policy_from_fd;
aa_kernel_interface_remove_policy;
aa_kernel_interface_write_policy;
aa_policy_cache_new;
aa_policy_cache_ref;
aa_policy_cache_unref;
aa_policy_cache_remove;
aa_policy_cache_replace_all;
aa_splitcon;
local:
*;
} APPARMOR_2.9;
PRIVATE {
global:
_aa_is_blacklisted;
_aa_autofree;
_aa_autoclose;
_aa_autofclose;
_aa_dirat_for_each;
local:
*;
};

View File

@@ -1,277 +0,0 @@
/*
* Copyright (c) 2014
* Canonical, Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/apparmor.h>
#include "private.h"
#define CACHE_FEATURES_FILE ".features"
struct aa_policy_cache {
unsigned int ref_count;
aa_features *features;
aa_features *kernel_features;
int dirfd;
};
static int clear_cache_cb(int dirfd, const char *path, struct stat *st,
void *data unused)
{
/* remove regular files */
if (S_ISREG(st->st_mode))
return unlinkat(dirfd, path, 0);
/* do nothing with other file types */
return 0;
}
static int create_cache(aa_policy_cache *policy_cache, aa_features *features)
{
if (aa_policy_cache_remove(policy_cache->dirfd, "."))
return -1;
if (aa_features_write_to_file(features, policy_cache->dirfd,
CACHE_FEATURES_FILE) == -1)
return -1;
aa_features_unref(policy_cache->features);
policy_cache->features = aa_features_ref(features);
return 0;
}
static int init_cache_features(aa_policy_cache *policy_cache,
aa_features *kernel_features, bool create)
{
bool call_create_cache = false;
if (aa_features_new(&policy_cache->features, policy_cache->dirfd,
CACHE_FEATURES_FILE)) {
policy_cache->features = NULL;
if (!create || errno != ENOENT)
return -1;
/* The cache directory needs to be created */
call_create_cache = true;
} else if (!aa_features_is_equal(policy_cache->features,
kernel_features)) {
if (!create) {
errno = EEXIST;
return -1;
}
/* The cache directory needs to be refreshed */
call_create_cache = true;
}
return call_create_cache ?
create_cache(policy_cache, kernel_features) : 0;
}
struct replace_all_cb_data {
aa_policy_cache *policy_cache;
aa_kernel_interface *kernel_interface;
};
static int replace_all_cb(int dirfd unused, const char *name, struct stat *st,
void *cb_data)
{
int retval = 0;
if (!S_ISDIR(st->st_mode) && !_aa_is_blacklisted(name)) {
struct replace_all_cb_data *data;
data = (struct replace_all_cb_data *) cb_data;
retval = aa_kernel_interface_replace_policy_from_file(data->kernel_interface,
data->policy_cache->dirfd,
name);
}
return retval;
}
/**
* aa_policy_cache_new - create a new aa_policy_cache object from a path
* @policy_cache: will point to the address of an allocated and initialized
* aa_policy_cache_new object upon success
* @kernel_features: features representing a kernel (may be NULL if you want to
* use the features of the currently running kernel)
* @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
* @path: path to the policy cache
* @max_caches: The maximum number of policy caches, one for each unique set of
* kernel features, before older caches are auto-reaped. 0 means
* that no new caches should be created (existing, valid caches
* will be used) and auto-reaping is disabled. UINT16_MAX means
* that a cache can be created and auto-reaping is disabled.
*
* Returns: 0 on success, -1 on error with errno set and *@policy_cache
* pointing to NULL
*/
int aa_policy_cache_new(aa_policy_cache **policy_cache,
aa_features *kernel_features,
int dirfd, const char *path, uint16_t max_caches)
{
aa_policy_cache *pc;
bool create = max_caches > 0;
*policy_cache = NULL;
if (!path) {
errno = EINVAL;
return -1;
}
if (max_caches > 1) {
errno = ENOTSUP;
return -1;
}
pc = calloc(1, sizeof(*pc));
if (!pc) {
errno = ENOMEM;
return -1;
}
pc->dirfd = -1;
aa_policy_cache_ref(pc);
open:
pc->dirfd = openat(dirfd, path, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
if (pc->dirfd < 0) {
int save;
/* does the dir exist? */
if (create && errno == ENOENT) {
if (mkdirat(dirfd, path, 0700) == 0)
goto open;
PERROR("Can't create cache directory '%s': %m\n", path);
} else if (create) {
PERROR("Can't update cache directory '%s': %m\n", path);
} else {
PDEBUG("Cache directory '%s' does not exist\n", path);
}
save = errno;
aa_policy_cache_unref(pc);
errno = save;
return -1;
}
if (kernel_features) {
aa_features_ref(kernel_features);
} else if (aa_features_new_from_kernel(&kernel_features) == -1) {
int save = errno;
aa_policy_cache_unref(pc);
errno = save;
return -1;
}
pc->kernel_features = kernel_features;
if (init_cache_features(pc, kernel_features, create)) {
int save = errno;
aa_policy_cache_unref(pc);
errno = save;
return -1;
}
*policy_cache = pc;
return 0;
}
/**
* aa_policy_cache_ref - increments the ref count of an aa_policy_cache object
* @policy_cache: the policy_cache
*
* Returns: the policy_cache
*/
aa_policy_cache *aa_policy_cache_ref(aa_policy_cache *policy_cache)
{
atomic_inc(&policy_cache->ref_count);
return policy_cache;
}
/**
* aa_policy_cache_unref - decrements the ref count and frees the aa_policy_cache object when 0
* @policy_cache: the policy_cache (can be NULL)
*/
void aa_policy_cache_unref(aa_policy_cache *policy_cache)
{
if (policy_cache && atomic_dec_and_test(&policy_cache->ref_count)) {
aa_features_unref(policy_cache->features);
aa_features_unref(policy_cache->kernel_features);
if (policy_cache->dirfd != -1)
close(policy_cache->dirfd);
free(policy_cache);
}
}
/**
* aa_policy_cache_remove - removes all policy cache files under a path
* @dirfd: directory file descriptor or AT_FDCWD (see openat(2))
* @path: the path to a policy cache directory
*
* Returns: 0 on success, -1 on error with errno set
*/
int aa_policy_cache_remove(int dirfd, const char *path)
{
return _aa_dirat_for_each(dirfd, path, NULL, clear_cache_cb);
}
/**
* aa_policy_cache_replace_all - performs a kernel policy replacement of all cached policies
* @policy_cache: the policy_cache
* @kernel_interface: the kernel interface to use when doing the replacement
* (may be NULL if the currently running kernel features
* were used when calling aa_policy_cache_new())
*
* Returns: 0 on success, -1 on error with errno set and features pointing to
* NULL
*/
int aa_policy_cache_replace_all(aa_policy_cache *policy_cache,
aa_kernel_interface *kernel_interface)
{
struct replace_all_cb_data cb_data;
int retval;
if (kernel_interface) {
aa_kernel_interface_ref(kernel_interface);
} else if (aa_kernel_interface_new(&kernel_interface,
policy_cache->kernel_features,
NULL) == -1) {
kernel_interface = NULL;
return -1;
}
cb_data.policy_cache = policy_cache;
cb_data.kernel_interface = kernel_interface;
retval = _aa_dirat_for_each(policy_cache->dirfd, ".", &cb_data,
replace_all_cb);
aa_kernel_interface_unref(kernel_interface);
return retval;
}

View File

@@ -1,252 +0,0 @@
/*
* Copyright 2014 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "private.h"
/**
* Allow libapparmor to build on older systems where secure_getenv() is still
* named __secure_getenv(). This snippet was taken from the glibc wiki
* (https://sourceware.org/glibc/wiki/Tips_and_Tricks/secure_getenv).
*/
#ifndef HAVE_SECURE_GETENV
#ifdef HAVE___SECURE_GETENV
#define secure_getenv __secure_getenv
#else
#error neither secure_getenv nor __secure_getenv is available
#endif
#endif
struct ignored_suffix_t {
const char * text;
int len;
int silent;
};
static struct ignored_suffix_t ignored_suffixes[] = {
/* Debian packging files, which are in flux during install
should be silently ignored. */
{ ".dpkg-new", 9, 1 },
{ ".dpkg-old", 9, 1 },
{ ".dpkg-dist", 10, 1 },
{ ".dpkg-bak", 9, 1 },
/* RPM packaging files have traditionally not been silently
ignored */
{ ".rpmnew", 7, 0 },
{ ".rpmsave", 8, 0 },
/* patch file backups/conflicts */
{ ".orig", 5, 0 },
{ ".rej", 4, 0 },
/* Backup files should be mentioned */
{ "~", 1, 0 },
{ NULL, 0, 0 }
};
#define DEBUG_ENV_VAR "LIBAPPARMOR_DEBUG"
void print_error(bool honor_env_var, const char *ident, const char *fmt, ...)
{
va_list args;
int openlog_options = 0;
if (honor_env_var && secure_getenv(DEBUG_ENV_VAR))
openlog_options |= LOG_PERROR;
openlog(ident, openlog_options, LOG_ERR);
va_start(args, fmt);
vsyslog(LOG_ERR, fmt, args);
va_end(args);
closelog();
}
void print_debug(const char *fmt, ...)
{
va_list args;
if (!secure_getenv(DEBUG_ENV_VAR))
return;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
void atomic_inc(unsigned int *v)
{
__sync_add_and_fetch(v, 1);
}
bool atomic_dec_and_test(unsigned int *v)
{
return __sync_sub_and_fetch(v, 1) == 0;
}
int _aa_is_blacklisted(const char *name)
{
size_t name_len = strlen(name);
struct ignored_suffix_t *suffix;
/* skip dot files and files with no name */
if (!name_len || *name == '.' || !strcmp(name, "README"))
return 1;
/* skip blacklisted suffixes */
for (suffix = ignored_suffixes; suffix->text; suffix++) {
char *found;
if ( (found = strstr((char *) name, suffix->text)) &&
found - name + suffix->len == name_len ) {
if (!suffix->silent)
return -1;
return 1;
}
}
return 0;
}
/* automaticly free allocated variables tagged with autofree on fn exit */
void _aa_autofree(void *p)
{
void **_p = (void**)p;
free(*_p);
}
void _aa_autoclose(int *fd)
{
if (*fd != -1) {
/* if close was interrupted retry */
while(close(*fd) == -1 && errno == EINTR);
*fd = -1;
}
}
void _aa_autofclose(FILE **f)
{
if (*f) {
fclose(*f);
*f = NULL;
}
}
int _aa_asprintf(char **strp, const char *fmt, ...)
{
va_list args;
int rc;
va_start(args, fmt);
rc = vasprintf(strp, fmt, args);
va_end(args);
if (rc == -1)
*strp = NULL;
return rc;
}
static int dot_or_dot_dot_filter(const struct dirent *ent)
{
if (strcmp(ent->d_name, ".") == 0 ||
strcmp(ent->d_name, "..") == 0)
return 0;
return 1;
}
/**
* _aa_dirat_for_each: iterate over a directory calling cb for each entry
* @dirfd: already opened directory
* @name: name of the directory (NOT NULL)
* @data: data pointer to pass to the callback fn (MAY BE NULL)
* @cb: the callback to pass entry too (NOT NULL)
*
* Iterate over the entries in a directory calling cb for each entry.
* The directory to iterate is determined by a combination of @dirfd and
* @name.
*
* See the openat section of the open(2) man page for details on valid @dirfd
* and @name combinations. This function does accept AT_FDCWD as @dirfd if
* @name should be considered relative to the current working directory.
*
* Pass "." for @name if @dirfd is the directory to iterate over.
*
* The cb function is called with the DIR in use and the name of the
* file in that directory. If the file is to be opened it should
* use the openat, fstatat, and related fns.
*
* Returns: 0 on success, else -1 and errno is set to the error code
*/
int _aa_dirat_for_each(int dirfd, const char *name, void *data,
int (* cb)(int, const char *, struct stat *, void *))
{
autofree struct dirent **namelist = NULL;
autoclose int cb_dirfd = -1;
int i, num_dirs, rc;
if (!cb || !name) {
errno = EINVAL;
return -1;
}
cb_dirfd = openat(dirfd, name, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
if (cb_dirfd == -1) {
PDEBUG("could not open directory '%s': %m\n", name);
return -1;
}
num_dirs = scandirat(cb_dirfd, ".", &namelist,
dot_or_dot_dot_filter, NULL);
if (num_dirs == -1) {
PDEBUG("scandirat of directory '%s' failed: %m\n", name);
return -1;
}
for (rc = 0, i = 0; i < num_dirs; i++) {
/* Must cycle through all dirs so that each one is autofreed */
autofree struct dirent *dir = namelist[i];
struct stat my_stat;
if (rc)
continue;
if (fstatat(cb_dirfd, dir->d_name, &my_stat, 0)) {
PDEBUG("stat failed for '%s': %m\n", dir->d_name);
rc = -1;
continue;
}
if (cb(cb_dirfd, dir->d_name, &my_stat, data)) {
PDEBUG("dir_for_each callback failed for '%s'\n",
dir->d_name);
rc = -1;
continue;
}
}
return rc;
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2014 Canonical Ltd.
*
* The libapparmor library is licensed under the terms of the GNU
* Lesser General Public License, version 2.1. Please see the file
* COPYING.LGPL.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _AA_PRIVATE_H
#define _AA_PRIVATE_H 1
#include <stdbool.h>
#include <sys/apparmor_private.h>
#define autofree __attribute((cleanup(_aa_autofree)))
#define autoclose __attribute((cleanup(_aa_autoclose)))
#define autofclose __attribute((cleanup(_aa_autofclose)))
#define unused __attribute__ ((unused))
#define asprintf _aa_asprintf
#if ENABLE_DEBUG_OUTPUT
#define PERROR(fmt, args...) print_error(true, "libapparmor", fmt, ## args)
#define PDEBUG(fmt, args...) print_debug("libapparmor: " fmt, ## args)
#else /* ENABLE_DEBUG_OUTPUT */
#define PERROR(fmt, args...) print_error(false, "libapparmor", fmt, ## args)
#define PDEBUG(fmt, args...) /* do nothing */
#endif /* ENABLE_DEBUG_OUTPUT */
#define MY_TEST(statement, error) \
if (!(statement)) { \
fprintf(stderr, "FAIL: %s\n", error); \
rc = 1; \
}
void print_error(bool honor_env_var, const char *ident, const char *fmt, ...);
void print_debug(const char *fmt, ...);
void atomic_inc(unsigned int *v);
bool atomic_dec_and_test(unsigned int *v);
#endif /* _AA_PRIVATE_H */

View File

@@ -355,7 +355,6 @@ yy_flex_debug = 0;
{syslog_time} { yylval->t_str = strdup(yytext); BEGIN(hostname); return(TOK_TIME); }
{audit} { yy_push_state(audit_id, yyscanner); return(TOK_AUDIT); }
{dmesg_timestamp} { yylval->t_str = strdup(yytext); return(TOK_DMESG_STAMP); }
. { /* ignore any non-matched input */ BEGIN(unknown_message); yyless(0); }

View File

@@ -20,7 +20,13 @@
#include <stdio.h>
#include <aalogparse.h>
#include "parser.h"
#include "private.h"
#define MY_TEST(statement, error) \
if (!(statement)) { \
fprintf(stderr, "FAIL: %s\n", error); \
rc = 1; \
}
int main(void)
{
@@ -34,25 +40,19 @@ int main(void)
retstr = hex_to_string("2F746D702F646F6573206E6F74206578697374");
MY_TEST(retstr, "basic allocation");
MY_TEST(strcmp(retstr, "/tmp/does not exist") == 0, "basic dehex 1");
free(retstr);
retstr = hex_to_string("61");
MY_TEST(strcmp(retstr, "a") == 0, "basic dehex 2");
free(retstr);
retstr = hex_to_string("");
MY_TEST(strcmp(retstr, "") == 0, "empty string");
free(retstr);
/* ipproto_to_string() tests */
retstr = ipproto_to_string((unsigned) 99999);
MY_TEST(strcmp(retstr, "unknown(99999)") == 0, "invalid protocol test");
free(retstr);
retstr = ipproto_to_string((unsigned) 6);
MY_TEST(strcmp(retstr, "tcp") == 0, "protocol=tcp");
free(retstr);
return rc;
}

View File

@@ -1,247 +0,0 @@
/*
* Copyright (c) 2015
* Canonical, Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#include <stdio.h>
#include <string.h>
#include "private.h"
#include "features.c"
static int test_tokenize_path_components(void)
{
struct component components[32];
size_t max = sizeof(components) / sizeof(*components);
size_t num;
int rc = 0;
num = tokenize_path_components(NULL, components, max);
MY_TEST(num == 0, "basic NULL test");
num = tokenize_path_components("", components, max);
MY_TEST(num == 0, "basic empty string test");
num = tokenize_path_components("a", components, 0);
MY_TEST(num == 0, "basic empty array test");
num = tokenize_path_components("a", components, 1);
MY_TEST(num == 1, "one component full test (num)");
MY_TEST(!strncmp(components[0].str, "a", components[0].len),
"one component full test (components[0])");
num = tokenize_path_components("a/b", components, 2);
MY_TEST(num == 2, "two component full test (num)");
MY_TEST(!strncmp(components[0].str, "a", components[0].len),
"two component full test (components[0])");
MY_TEST(!strncmp(components[1].str, "b", components[0].len),
"two component full test (components[1])");
num = tokenize_path_components("a/b/c", components, 1);
MY_TEST(num == 1, "not enough components full test (num)");
MY_TEST(!strncmp(components[0].str, "a/b/c", components[0].len),
"not enough components full test (components[0])");
num = tokenize_path_components("/", components, max);
MY_TEST(num == 0, "no valid components #1 (num)");
num = tokenize_path_components("////////", components, max);
MY_TEST(num == 0, "no valid components #2 (num)");
num = tokenize_path_components("////////////foo////", components, max);
MY_TEST(num == 1, "many invalid components (num)");
MY_TEST(!strncmp(components[0].str, "foo", components[0].len),
"many invalid components (components[0])");
num = tokenize_path_components("file", components, max);
MY_TEST(num == 1, "file (num)");
MY_TEST(!strncmp(components[0].str, "file", components[0].len),
"file (components[0])");
num = tokenize_path_components("/policy///versions//v7/", components, max);
MY_TEST(num == 3, "v7 (num)");
MY_TEST(!strncmp(components[0].str, "policy", components[0].len),
"v7 (components[0])");
MY_TEST(!strncmp(components[1].str, "versions", components[1].len),
"v7 (components[1])");
MY_TEST(!strncmp(components[2].str, "v7", components[2].len),
"v7 (components[2])");
num = tokenize_path_components("dbus/mask/send", components, max);
MY_TEST(num == 3, "dbus send (num)");
MY_TEST(!strncmp(components[0].str, "dbus", components[0].len),
"dbus send (components[0])");
MY_TEST(!strncmp(components[1].str, "mask", components[1].len),
"dbus send (components[1])");
MY_TEST(!strncmp(components[2].str, "send", components[2].len),
"dbus send (components[2])");
return rc;
}
static int do_test_walk_one(const char **str, const struct component *component,
bool is_top_level, bool expect_walk, const char *e1,
const char *e2, const char *e3)
{
const char *save = str ? *str : NULL;
bool walked = walk_one(str, component, is_top_level);
int rc = 0;
/* Check if the result of the walk matches the expected result*/
MY_TEST(expect_walk == walked, e1);
if (save) {
/**
* If a walk was expected, @*str should have changed. It
* shouldn't change if a walk was unexpected.
*/
if (expect_walk) {
MY_TEST(*str != save, e2);
} else {
MY_TEST(*str == save, e3);
}
}
return rc;
}
#define MY_WALK_TEST(str, component, is_top_level, expect_walk, error) \
if (do_test_walk_one(str, component, is_top_level, \
expect_walk, \
error " (walk check)", \
error " (str didn't change)", \
error " (str changed)")) { \
rc = 1; \
}
#define MY_GOOD_WALK_TEST(str, component, is_top_level, error) \
MY_WALK_TEST(str, component, is_top_level, true, error)
#define MY_BAD_WALK_TEST(str, component, is_top_level, error) \
MY_WALK_TEST(str, component, is_top_level, false, error)
static int test_walk_one(void)
{
struct component c;
const char *str;
int rc = 0;
MY_BAD_WALK_TEST(NULL, &c, true, "basic NULL str test");
str = NULL;
MY_BAD_WALK_TEST(&str, &c, true, "basic NULL *str test");
str = "test { a b }";
MY_BAD_WALK_TEST(&str, NULL, true, "basic NULL component test");
str = "test { a b }";
c = (struct component) { NULL, 8 };
MY_BAD_WALK_TEST(&str, &c, true, "basic NULL c.str test");
str = "test { a b }";
c = (struct component) { "", 0 };
MY_BAD_WALK_TEST(&str, &c, true, "basic empty c.str test");
str = "test";
c = (struct component) { "test", 4 };
MY_GOOD_WALK_TEST(&str, &c, true, "single component");
str = "testX";
c = (struct component) { "test", 4 };
MY_BAD_WALK_TEST(&str, &c, true, "single component bad str");
str = "test";
c = (struct component) { "testX", 5 };
MY_BAD_WALK_TEST(&str, &c, true, "single component bad c.str");
str = "test { }";
c = (struct component) { "test", 4 };
MY_GOOD_WALK_TEST(&str, &c, true, "single component empty braces #1");
str = "test {\n\t}";
c = (struct component) { "test", 4 };
MY_GOOD_WALK_TEST(&str, &c, true, "single component empty braces #2");
str = "test{}";
c = (struct component) { "test", 4 };
MY_GOOD_WALK_TEST(&str, &c, true, "single component empty braces #3");
str = "test\t{}\n ";
c = (struct component) { "test", 4 };
MY_GOOD_WALK_TEST(&str, &c, true, "single component empty braces #4");
str = "test {}";
c = (struct component) { "test", 4 };
MY_BAD_WALK_TEST(&str, &c, false, "single component bad is_top_level");
str = "front{back";
c = (struct component) { "frontback", 9};
MY_BAD_WALK_TEST(&str, &c, true, "brace in the middle #1");
MY_BAD_WALK_TEST(&str, &c, false, "brace in the middle #2");
str = "ardvark { bear cat { deer } }";
c = (struct component) { "ardvark", 7 };
MY_GOOD_WALK_TEST(&str, &c, true, "animal walk good ardvark");
c = (struct component) { "deer", 4 };
MY_BAD_WALK_TEST(&str, &c, false, "animal walk bad deer");
MY_BAD_WALK_TEST(&str, &c, true, "animal walk bad top-level deer");
c = (struct component) { "bear", 4 };
MY_BAD_WALK_TEST(&str, &c, true, "animal walk bad bear");
c = (struct component) { "cat", 3 };
MY_GOOD_WALK_TEST(&str, &c, false, "animal walk good cat");
c = (struct component) { "ardvark", 7 };
MY_BAD_WALK_TEST(&str, &c, true, "animal walk bad ardvark");
c = (struct component) { "deer", 4 };
MY_GOOD_WALK_TEST(&str, &c, false, "animal walk good deer");
str = "dbus {mask {acquire send receive\n}\n}\nsignal {mask {hup int\n}\n}";
c = (struct component) { "hup", 3 };
MY_BAD_WALK_TEST(&str, &c, true, "dbus/signal bad hup #1");
MY_BAD_WALK_TEST(&str, &c, false, "dbus/signal bad hup #2");
c = (struct component) { "signal", 6 };
MY_BAD_WALK_TEST(&str, &c, false, "dbus/signal bad signal");
MY_GOOD_WALK_TEST(&str, &c, true, "dbus/signal good signal");
c = (struct component) { "mask", 4 };
MY_BAD_WALK_TEST(&str, &c, true, "dbus/signal bad mask");
MY_GOOD_WALK_TEST(&str, &c, false, "dbus/signal good mask");
c = (struct component) { "hup", 3 };
MY_GOOD_WALK_TEST(&str, &c, false, "dbus/signal good hup");
str = "policy {set_load {yes\n}\nversions {v7 {yes\n}\nv6 {yes\n}";
c = (struct component) { "policy", 6 };
MY_GOOD_WALK_TEST(&str, &c, true, "policy good");
c = (struct component) { "versions", 8 };
MY_GOOD_WALK_TEST(&str, &c, false, "versions good");
c = (struct component) { "v7", 2 };
MY_GOOD_WALK_TEST(&str, &c, false, "v7 good");
return rc;
}
int main(void)
{
int retval, rc = 0;
retval = test_tokenize_path_components();
if (retval)
rc = retval;
retval = test_walk_one();
if (retval)
rc = retval;
return rc;
}

View File

@@ -1,228 +0,0 @@
/*
* Copyright (c) 2015
* Canonical, Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#include <stdio.h>
#include <string.h>
#include "kernel.c"
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);
}
static int do_test_splitcon(char *con, int size, bool strip_nl, char **mode,
const char *expected_label,
const char *expected_mode, const char *error)
{
char *label;
int rc = 0;
label = splitcon(con, size, strip_nl, mode);
if (nullcmp_and_strcmp(label, expected_label)) {
fprintf(stderr, "FAIL: %s: label \"%s\" != \"%s\"\n",
error, label, expected_label);
rc = 1;
}
if (mode && nullcmp_and_strcmp(*mode, expected_mode)) {
fprintf(stderr, "FAIL: %s: mode \"%s\" != \"%s\"\n",
error, *mode, expected_mode);
rc = 1;
}
return rc;
}
static int do_test_aa_splitcon(char *con, char **mode,
const char *expected_label,
const char *expected_mode, const char *error)
{
char *label;
int rc = 0;
label = aa_splitcon(con, mode);
if (nullcmp_and_strcmp(label, expected_label)) {
fprintf(stderr, "FAIL: %s: label \"%s\" != \"%s\"\n",
error, label, expected_label);
rc = 1;
}
if (mode && nullcmp_and_strcmp(*mode, expected_mode)) {
fprintf(stderr, "FAIL: %s: mode \"%s\" != \"%s\"\n",
error, *mode, expected_mode);
rc = 1;
}
return rc;
}
#define TEST_SPLITCON(con, size, strip_nl, expected_label, \
expected_mode, error) \
do { \
char c1[] = con; \
char c2[] = con; \
size_t sz = size < 0 ? strlen(con) : size; \
char *mode; \
\
if (do_test_splitcon(c1, sz, strip_nl, &mode, \
expected_label, expected_mode, \
"splitcon: " error)) { \
rc = 1; \
} else if (do_test_splitcon(c2, sz, strip_nl, NULL, \
expected_label, NULL, \
"splitcon: " error " (NULL mode)")) { \
rc = 1; \
} \
} while (0)
#define TEST_AA_SPLITCON(con, expected_label, expected_mode, error) \
do { \
char c1[] = con; \
char c2[] = con; \
char c3[] = con "\n"; \
char *mode; \
\
if (do_test_aa_splitcon(c1, &mode, expected_label, \
expected_mode, "aa_splitcon: " error)) {\
rc = 1; \
} else if (do_test_aa_splitcon(c2, NULL, expected_label,\
NULL, \
"aa_splitcon: " error " (NULL mode)")) {\
rc = 1; \
} else if (do_test_aa_splitcon(c3, &mode, \
expected_label, expected_mode, \
"aa_splitcon: " error " (newline)")) { \
rc = 1; \
} \
} while (0)
static int test_splitcon(void)
{
int rc = 0;
/**
* NOTE: the TEST_SPLITCON() macro automatically generates
* corresponding tests with a NULL mode pointer.
*/
TEST_SPLITCON("", 0, true, NULL, NULL, "empty string test #1");
TEST_SPLITCON("", 0, false, NULL, NULL, "empty string test #2");
TEST_SPLITCON("unconfined", -1, true, "unconfined", NULL,
"unconfined #1");
TEST_SPLITCON("unconfined", -1, false, "unconfined", NULL,
"unconfined #2");
TEST_SPLITCON("unconfined\n", -1, true, "unconfined", NULL,
"unconfined #3");
TEST_SPLITCON("unconfined\n", -1, false, NULL, NULL,
"unconfined #4");
TEST_SPLITCON("label (mode)", -1, true, "label", "mode",
"basic split #1");
TEST_SPLITCON("label (mode)", -1, false, "label", "mode",
"basic split #2");
TEST_SPLITCON("label (mode)\n", -1, true, "label", "mode",
"basic split #3");
TEST_SPLITCON("label (mode)\n", -1, false, NULL, NULL,
"basic split #4");
TEST_SPLITCON("/a/b/c (enforce)", -1, true, "/a/b/c", "enforce",
"path enforce split #1");
TEST_SPLITCON("/a/b/c (enforce)", -1, false, "/a/b/c", "enforce",
"path enforce split #2");
TEST_SPLITCON("/a/b/c (enforce)\n", -1, true, "/a/b/c", "enforce",
"path enforce split #3");
TEST_SPLITCON("/a/b/c (enforce)\n", -1, false, NULL, NULL,
"path enforce split #4");
return rc;
}
static int test_aa_splitcon(void)
{
int rc = 0;
/**
* NOTE: the TEST_AA_SPLITCON() macro automatically generates
* corresponding tests with a NULL mode pointer and contexts with
* trailing newline characters.
*/
TEST_AA_SPLITCON("label (mode)", "label", "mode", "basic split");
TEST_AA_SPLITCON("/a/b/c (enforce)", "/a/b/c", "enforce",
"path enforce split");
TEST_AA_SPLITCON("/a/b/c (complain)", "/a/b/c", "complain",
"path complain split");
TEST_AA_SPLITCON("profile_name (enforce)", "profile_name", "enforce",
"name enforce split");
TEST_AA_SPLITCON("profile_name (complain)", "profile_name", "complain",
"name complain split");
TEST_AA_SPLITCON("unconfined", "unconfined", NULL, "unconfined");
TEST_AA_SPLITCON("(odd) (enforce)", "(odd)", "enforce",
"parenthesized label #1");
TEST_AA_SPLITCON("(odd) (enforce) (enforce)", "(odd) (enforce)",
"enforce", "parenthesized label #2");
TEST_AA_SPLITCON("/usr/bin/😺 (enforce)", "/usr/bin/😺", "enforce",
"non-ASCII path");
TEST_AA_SPLITCON("👍 (enforce)", "👍", "enforce",
"non-ASCII profile name");
/* Negative tests */
TEST_AA_SPLITCON("", NULL, NULL, "empty string test");
TEST_AA_SPLITCON("profile\t(enforce)", NULL, NULL,
"invalid tab separator");
TEST_AA_SPLITCON("profile(enforce)", NULL, NULL,
"invalid missing separator");
return rc;
}
int main(void)
{
int retval, rc = 0;
retval = test_splitcon();
if (retval)
rc = retval;
retval = test_aa_splitcon();
if (retval)
rc = retval;
return rc;
}

View File

@@ -3,30 +3,12 @@
%{
#include <aalogparse.h>
#include <sys/apparmor.h>
#include <sys/apparmor_private.h>
%}
%include "typemaps.i"
%include <aalogparse.h>
/**
* swig doesn't like the macro magic we do in apparmor.h and apparmor_private.h
* so the function prototypes must be manually inserted.
*
* Functions that return a negative int and set errno upon error use a special
* %exception directive and must be listed after the %exception below. All
* other functions go here.
*/
/* apparmor.h */
extern char *aa_splitcon(char *con, char **mode);
/* apparmor_private.h */
extern int _aa_is_blacklisted(const char *name);
#ifdef SWIGPYTHON
%exception {
$action
@@ -37,9 +19,9 @@ extern int _aa_is_blacklisted(const char *name);
}
#endif
/* Functions that return a negative int and set errno upon error go here. */
/* apparmor.h */
/* swig doesn't like the macro magic we do in apparmor.h so the fn prototypes
* are manually inserted here
*/
extern int aa_is_enabled(void);
extern int aa_find_mountpoint(char **mnt);
@@ -51,22 +33,11 @@ extern int aa_change_hat_vargs(unsigned long token, int count, ...);
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_gettaskcon(pid_t target, char **con, char **mode);
extern int aa_getcon(char **con, char **mode);
extern int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode);
extern int aa_getpeercon(int fd, char **label, char **mode);
extern int aa_getpeercon(int fd, char **con, char **mode);
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);
%exception;

View File

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

View File

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

View File

@@ -1 +0,0 @@
Sep 9 12:51:36 ubuntu-desktop kernel: [ 97.492562] audit: type=1400 audit(1431116353.523:77): apparmor="DENIED" operation="change_profile" profile="/tests/regression/apparmor/changeprofile" pid=3459 comm="changeprofile" target="/tests/regression/apparmor/rename"

View File

@@ -1,11 +0,0 @@
START
File: testcase_changeprofile_01.in
Event type: AA_RECORD_DENIED
Audit ID: 1431116353.523:77
Operation: change_profile
Profile: /tests/regression/apparmor/changeprofile
Command: changeprofile
Name2: /tests/regression/apparmor/rename
PID: 3459
Epoch: 1431116353
Audit subid: 77

View File

@@ -1 +0,0 @@
[ 1612.746129] audit: type=1400 audit(1284061910.975:672): apparmor="DENIED" operation="capable" parent=2663 profile="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/syscall_setpriority" pid=7292 comm="syscall_setprio" capability=23 capname="sys_nice"

View File

@@ -1,12 +0,0 @@
START
File: testcase_dmesg_capability.in
Event type: AA_RECORD_DENIED
Audit ID: 1284061910.975:672
Operation: capable
Profile: /home/ubuntu/bzr/apparmor/tests/regression/apparmor/syscall_setpriority
Name: sys_nice
Command: syscall_setprio
Parent: 2663
PID: 7292
Epoch: 1284061910
Audit subid: 672

View File

@@ -1 +0,0 @@
[ 1597.774866] audit: type=1400 audit(1284061896.005:28): apparmor="DENIED" operation="change_hat" info="unconfined" error=-1 pid=2698 comm="syscall_ptrace"

View File

@@ -1,11 +0,0 @@
START
File: testcase_dmesg_changehat_negative_error.in
Event type: AA_RECORD_DENIED
Audit ID: 1284061896.005:28
Operation: change_hat
Command: syscall_ptrace
Info: unconfined
ErrorCode: 1
PID: 2698
Epoch: 1284061896
Audit subid: 28

View File

@@ -1 +0,0 @@
[ 97.492562] audit: type=1400 audit(1431116353.523:77): apparmor="DENIED" operation="change_profile" profile="/tests/regression/apparmor/changeprofile" pid=3459 comm="changeprofile" target="/tests/regression/apparmor/rename"

View File

@@ -1,11 +0,0 @@
START
File: testcase_dmesg_changeprofile_01.in
Event type: AA_RECORD_DENIED
Audit ID: 1431116353.523:77
Operation: change_profile
Profile: /tests/regression/apparmor/changeprofile
Command: changeprofile
Name2: /tests/regression/apparmor/rename
PID: 3459
Epoch: 1431116353
Audit subid: 77

View File

@@ -1 +0,0 @@
[ 2010.738449] audit: type=1400 audit(1284062308.965:276251): apparmor="DENIED" operation="link" parent=19088 profile="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/link" name="/tmp/sdtest.19088-12382-HWH57d/linkfile" pid=19142 comm="link" requested_mask="l" denied_mask="l" fsuid=0 ouid=0 target="/tmp/sdtest.19088-12382-HWH57d/target"

View File

@@ -1,17 +0,0 @@
START
File: testcase_dmesg_link_01.in
Event type: AA_RECORD_DENIED
Audit ID: 1284062308.965:276251
Operation: link
Mask: l
Denied Mask: l
fsuid: 0
ouid: 0
Profile: /home/ubuntu/bzr/apparmor/tests/regression/apparmor/link
Name: /tmp/sdtest.19088-12382-HWH57d/linkfile
Command: link
Name2: /tmp/sdtest.19088-12382-HWH57d/target
Parent: 19088
PID: 19142
Epoch: 1284062308
Audit subid: 276251

View File

@@ -1 +0,0 @@
[45334.755142] audit: type=1503 audit(1282671283.411:2199): operation="mkdir" pid=4786 parent=4708 profile="/usr/sbin/sshd//ubuntu" requested_mask="c::" denied_mask="c::" fsuid=1000 ouid=1000 name="/tmp/ssh-gRozJw4786/"

View File

@@ -1,15 +0,0 @@
START
File: testcase_dmesg_mkdir.in
Event type: AA_RECORD_DENIED
Audit ID: 1282671283.411:2199
Operation: mkdir
Mask: c::
Denied Mask: c::
fsuid: 1000
ouid: 1000
Profile: /usr/sbin/sshd//ubuntu
Name: /tmp/ssh-gRozJw4786/
Parent: 4708
PID: 4786
Epoch: 1282671283
Audit subid: 2199

View File

@@ -1 +0,0 @@
[ 878.663418] audit: type=1502 audit(1282626827.320:413): operation="rename_dest" pid=1881 parent=650 profile="/usr/sbin/sshd" requested_mask="wc::" denied_mask="wc::" fsuid=0 ouid=0 name="/var/run/motd"

View File

@@ -1,15 +0,0 @@
START
File: testcase_dmesg_rename_dest.in
Event type: AA_RECORD_ALLOWED
Audit ID: 1282626827.320:413
Operation: rename_dest
Mask: wc::
Denied Mask: wc::
fsuid: 0
ouid: 0
Profile: /usr/sbin/sshd
Name: /var/run/motd
Parent: 650
PID: 1881
Epoch: 1282626827
Audit subid: 413

View File

@@ -1 +0,0 @@
[ 878.663410] audit: type=1502 audit(1282626827.320:412): operation="rename_src" pid=1881 parent=650 profile="/usr/sbin/sshd" requested_mask="r::" denied_mask="r::" fsuid=0 ouid=0 name="/var/run/motd.new"

View File

@@ -1,15 +0,0 @@
START
File: testcase_dmesg_rename_src.in
Event type: AA_RECORD_ALLOWED
Audit ID: 1282626827.320:412
Operation: rename_src
Mask: r::
Denied Mask: r::
fsuid: 0
ouid: 0
Profile: /usr/sbin/sshd
Name: /var/run/motd.new
Parent: 650
PID: 1881
Epoch: 1282626827
Audit subid: 412

View File

@@ -1 +0,0 @@
[ 2143.902340] audit: type=1400 audit(1283989336.064:272335): apparmor="STATUS" info="failed to unpack profile" error=-71 pid=4958 comm="apparmor_parser" name="/home/jj/master/tests/regression/apparmor/net_raw" offset=159

View File

@@ -1,11 +0,0 @@
START
File: testcase_dmesg_status_offset.in
Event type: AA_RECORD_STATUS
Audit ID: 1283989336.064:272335
Name: /home/jj/master/tests/regression/apparmor/net_raw
Command: apparmor_parser
Info: failed to unpack profile
ErrorCode: 71
PID: 4958
Epoch: 1283989336
Audit subid: 272335

View File

@@ -1 +0,0 @@
[ 878.662172] audit: type=1503 audit(1282626827.320:411): operation="truncate" pid=1957 parent=1 profile="/etc/update-motd.d/91-release-upgrade" requested_mask="w::" denied_mask="w::" fsuid=0 ouid=0 name="/var/lib/update-notifier/release-upgrade-available"

View File

@@ -1,15 +0,0 @@
START
File: testcase_dmesg_truncate.in
Event type: AA_RECORD_DENIED
Audit ID: 1282626827.320:411
Operation: truncate
Mask: w::
Denied Mask: w::
fsuid: 0
ouid: 0
Profile: /etc/update-motd.d/91-release-upgrade
Name: /var/lib/update-notifier/release-upgrade-available
Parent: 1
PID: 1957
Epoch: 1282626827
Audit subid: 411

View File

@@ -1 +0,0 @@
Jul 25 15:02:00 redacted kernel: [ 296.524447] audit: type=1400 audit(1437850920.403:64): apparmor="ALLOWED" operation="open" profile="/usr/sbin/vsftpd" name="/home/bane/foo" pid=1811 comm="vsftpd" requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000

View File

@@ -1,15 +0,0 @@
START
File: testcase_syslog_read.in
Event type: AA_RECORD_ALLOWED
Audit ID: 1437850920.403:64
Operation: open
Mask: r
Denied Mask: r
fsuid: 1000
ouid: 1000
Profile: /usr/sbin/vsftpd
Name: /home/bane/foo
Command: vsftpd
PID: 1811
Epoch: 1437850920
Audit subid: 64

View File

@@ -18,7 +18,13 @@ NAME=apparmor-parser
all:
COMMONDIR=../common/
include $(COMMONDIR)/Make.rules
include common/Make.rules
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
ifeq ($(COMMONDIR_EXISTS), true)
common/Make.rules: $(COMMONDIR)/Make.rules
ln -sf $(COMMONDIR) .
endif
DESTDIR=/
APPARMOR_BIN_PREFIX=${DESTDIR}/lib/apparmor
@@ -50,7 +56,7 @@ CFLAGS = -g -pg -fprofile-arcs -ftest-coverage
endif
endif #CFLAGS
EXTRA_CXXFLAGS = ${CFLAGS} ${CPPFLAGS} ${CXX_WARNINGS} -std=gnu++0x -D_GNU_SOURCE
EXTRA_CXXFLAGS = ${CFLAGS} ${CXX_WARNINGS} -std=gnu++0x -D_GNU_SOURCE
EXTRA_CFLAGS = ${EXTRA_CXXFLAGS} ${CPP_WARNINGS}
#LEXLIB := -lfl
@@ -75,10 +81,9 @@ SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
parser_alias.c common_optarg.c lib.c network.c \
mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \
af_rule.cc af_unix.cc policy_cache.c
af_rule.cc af_unix.cc
HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h \
rule.h common_optarg.h signal.h ptrace.h network.h af_rule.h af_unix.h \
policy_cache.h
rule.h common_optarg.h signal.h ptrace.h network.h af_rule.h af_unix.h
TOOLS = apparmor_parser
OBJECTS = $(patsubst %.cc, %.o, $(SRCS:.c=.o))
@@ -115,11 +120,9 @@ TEST_OBJECTS = $(filter-out \
parser_lex.o \
parser_yacc.o \
common_optarg.o \
parser_main.o \
policy_cache.o, ${OBJECTS}) \
parser_main.o, ${OBJECTS}) \
$(AAREOBJECTS)
TEST_LDFLAGS = $(AARE_LDFLAGS)
TEST_LDLIBS = $(AALIB)
ifdef V
VERBOSE = 1
@@ -140,7 +143,7 @@ po/${NAME}.pot: ${SRCS} ${HDRS}
$(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${SRCS} ${HDRS}"
techdoc.pdf: techdoc.tex
timestamp=$(shell date --utc "+%Y%m%d%H%M%S%z" -r $< );\
timestamp=$(shell date "+%Y%m%d%H%M%S+02'00'" -r $< );\
while pdflatex "\def\fixedpdfdate{$$timestamp}\input $<" ${BUILD_OUTPUT} || exit 1 ; \
grep -q "Label(s) may have changed" techdoc.log; \
do :; done
@@ -189,7 +192,7 @@ apparmor_parser: $(OBJECTS) $(AAREOBJECTS) $(LIBAPPARMOR_A)
parser_yacc.c parser_yacc.h: parser_yacc.y parser.h profile.h
$(YACC) $(YFLAGS) -o parser_yacc.c parser_yacc.y
parser_lex.c: parser_lex.l parser_yacc.h parser.h profile.h mount.h dbus.h policy_cache.h
parser_lex.c: parser_lex.l parser_yacc.h parser.h profile.h mount.h dbus.h
$(LEX) ${LEXFLAGS} -o$@ $<
parser_lex.o: parser_lex.c parser.h parser_yacc.h
@@ -201,7 +204,7 @@ parser_misc.o: parser_misc.c parser.h parser_yacc.h profile.h cap_names.h $(APPA
parser_yacc.o: parser_yacc.c parser_yacc.h $(APPARMOR_H)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_main.o: parser_main.c parser.h parser_version.h policy_cache.h libapparmor_re/apparmor_re.h $(APPARMOR_H)
parser_main.o: parser_main.c parser.h parser_version.h libapparmor_re/apparmor_re.h $(APPARMOR_H)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_interface.o: parser_interface.c parser.h profile.h libapparmor_re/apparmor_re.h
@@ -213,7 +216,7 @@ parser_include.o: parser_include.c parser.h parser_include.h
parser_merge.o: parser_merge.c parser.h profile.h
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_regex.o: parser_regex.c parser.h profile.h libapparmor_re/apparmor_re.h libapparmor_re/aare_rules.h $(APPARMOR_H)
parser_regex.o: parser_regex.c parser.h profile.h libapparmor_re/apparmor_re.h $(APPARMOR_H)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_symtab.o: parser_symtab.c parser.h
@@ -237,9 +240,6 @@ mount.o: mount.cc mount.h parser.h immunix.h rule.h
common_optarg.o: common_optarg.c common_optarg.h parser.h libapparmor_re/apparmor_re.h
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
policy_cache.o: policy_cache.c policy_cache.h parser.h lib.h
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
lib.o: lib.c lib.h parser.h
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
@@ -285,9 +285,9 @@ cap_names.h: /usr/include/linux/capability.h
echo "$(CAPABILITIES)" | LC_ALL=C sed -n -e "s/[ \\t]\\?CAP_\\([A-Z0-9_]\\+\\)/\{\"\\L\\1\", \\UCAP_\\1\},\\n/pg" > $@
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)
$(CXX) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS)
tst_%: parser_%.c parser.h $(filter-out parser_%.o, ${TEST_OBJECTS})
$(CXX) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS) $(TEST_LDLIBS)
$(CXX) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS)
.SILENT: check
.PHONY: check
@@ -334,27 +334,6 @@ install-debian:
install-unknown:
INSTALLDEPS=arch
ifndef DISTRO
DISTRO=$(shell if [ -f /etc/slackware-version ] ; then \
echo slackware ; \
elif [ -f /etc/debian_version ] ; then \
echo debian ;\
elif which rpm > /dev/null ; then \
if [ "$(rpm --eval '0%{?suse_version}')" != "0" ] ; then \
echo suse ;\
elif [ "$(rpm --eval '%{_host_vendor}')" = redhat ] ; then \
echo rhel4 ;\
elif [ "$(rpm --eval '0%{?fedora}')" != "0" ] ; then \
echo rhel4 ;\
else \
echo unknown ;\
fi ;\
else \
echo unknown ;\
fi)
endif
ifdef DISTRO
INSTALLDEPS+=install-$(DISTRO)
endif
@@ -378,11 +357,9 @@ install-indep:
$(MAKE) -C po install NAME=${NAME} DESTDIR=${DESTDIR}
$(MAKE) install_manpages DESTDIR=${DESTDIR}
ifndef VERBOSE
.SILENT: clean
endif
.PHONY: clean
clean: pod_clean
clean: _clean
rm -f core core.* *.o *.s *.a *~ *.gcda *.gcno
rm -f gmon.out
rm -f $(TOOLS) $(TESTS)
@@ -396,4 +373,9 @@ clean: pod_clean
$(MAKE) -s -C $(AAREDIR) clean
$(MAKE) -s -C po clean
$(MAKE) -s -C tst clean
rm -f common
.SILENT: dist_clean
dist_clean:
@$(MAKE) clean
@rm -f $(LEX_C_FILES) $(YACC_C_FILES)

View File

@@ -243,7 +243,7 @@ bool unix_rule::write_addr(std::ostringstream &buffer, const char *addr)
buffer << "\\x01";
} else {
/* skip leading @ */
ptype = convert_aaregex_to_pcre(addr + 1, 0, glob_null, buf, &pos);
ptype = convert_aaregex_to_pcre(addr + 1, 0, buf, &pos);
if (ptype == ePatternInvalid)
return false;
/* kernel starts abstract with \0 */
@@ -267,7 +267,7 @@ bool unix_rule::write_label(std::ostringstream &buffer, const char *label)
if (label) {
int pos;
ptype = convert_aaregex_to_pcre(label, 0, glob_default, buf, &pos);
ptype = convert_aaregex_to_pcre(label, 0, buf, &pos);
if (ptype == ePatternInvalid)
return false;
/* kernel starts abstract with \0 */

View File

@@ -44,78 +44,44 @@ to the policy; this behaviour is modelled after cpp(1).
=over 4
B<PROFILE FILE> = ( [ I<PREAMBLE> ] [ I<PROFILE> ] )*
B<PREAMBLE> = ( I<COMMENT> | I<VARIABLE ASSIGNMENT> | I<INCLUDE> )* (variable assignment must come before the profile)
B<INCLUDE> = '#include' ( I<ABS PATH> | I<MAGIC PATH> )
B<ABS PATH> = '"' path '"' (the path is passed to open(2))
B<MAGIC PATH> = 'E<lt>' relative path 'E<gt>' (the path is relative to F</etc/apparmor.d/>)
B<COMMENT> = '#' I<TEXT> [ '\r' ] '\n'
B<COMMENT> = '#' I<TEXT>
B<TEXT> = any characters
B<PROFILE> = ( I<PROFILE HEAD> ) [ I<ATTACHMENT SPECIFICATION> ] [ I<PROFILE FLAG CONDS> ] '{' ( I<RULES> )* '}'
B<PROFILE> = [ I<COMMENT> ... ] [ I<VARIABLE ASSIGNMENT> ... ] ( '"' I<PROGRAM> '"' | I<PROGRAM> ) [ 'flags=(complain)' ]'{' [ ( I<RESOURCE RULE> | I<COMMENT> | I<INCLUDE> | I<SUBPROFILE> | 'capability ' I<CAPABILITY> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<DBUS RULE> | I<UNIX RULE> I<FILE RULE> | 'change_profile -E<gt> ' I<PROGRAMCHILD> ) ... ] '}'
B<PROFILE HEAD> = [ 'profile' ] I<FILEGLOB> | 'profile' I<PROFILE NAME>
B<PROFILE NAME> ( I<UNQUOTED PROFILE NAME> | I<QUOTED PROFILE NAME> )
B<QUOTED PROFILE NAME> = '"' I<UNQUOTED PROFILE NAME> '"'
B<UNQUOTED PROFILE NAME> = (must start with alphanumeric character (after variable expansion), or '/' B<AARE> have special meanings; see below. May include I<VARIABLE>. Rules with embedded spaces or tabs must be quoted.)
B<ATTACHMENT SPECIFICATION> = I<FILEGLOB>
B<PROFILE FLAG CONDS> = [ 'flags=' ] '(' comma or white space separated list of I<PROFILE FLAGS> ')'
B<PROFILE FLAGS> = 'complain' | 'audit' | 'enforce' | 'mediate_deleted' | 'attach_disconnected' | 'chroot_relative'
B<RULES> = [ ( I<LINE RULES> | I<COMMA RULES> ',' | I<BLOCK RULES> )
B<LINE RULES> = ( I<COMMENT> | I<INCLUDE> ) [ '\r' ] '\n'
B<COMMA RULES> = ( I<CAPABILITY RULE> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<UNIX RULE> | I<FILE RULE> | I<LINK RULE> | I<CHANGE_PROFILE RULE> | I<RLIMIT RULE> | I<ALIAS RULE> | I<DBUS RULE> )
B<BLOCK RULES> = ( I<SUBPROFILE> | I<HAT> | I<QUALIFIER BLOCK> )
B<SUBPROFILE> = 'profile' I<PROFILE NAME> [ I<ATTACHMENT SPECIFICATION> ] [ I<PROFILE FLAG CONDS> ] '{' ( I<RULES> )* '}'
B<HAT> = ('hat' | '^') I<HATNAME> [ I<PROFILE FLAG CONDS> ] '{' ( I<RULES> )* '}'
B<HATNAME> = ( must start with alphanumeric character. see aa_change_hat(2) for a description of how this "hat" is used. IF '^' is used to start a hat then there is no space between the '^' and I<HATNAME>)
B<QUALIFIER BLOCK> = I<QUALIFIERS> I<BLOCK>
B<ACCESS TYPE> = ( 'allow' | 'deny' )
B<QUALIFIERS> = [ 'audit' ] [ I<ACCESS TYPE> ]
B<CAPABILITY RULE> = [ I<QUALIFIERS> ] 'capability' [ I<CAPABILITY LIST> ]
B<CAPABILITY LIST> = ( I<CAPABILITY> )+
B<SUBPROFILE> = [ I<COMMENT> ... ] ( I<PROGRAMHAT> | 'profile ' I<PROGRAMCHILD> ) '{' [ ( I<FILE RULE> | I<COMMENT> | I<INCLUDE> ) ... ] '}'
B<CAPABILITY> = (lowercase capability name without 'CAP_' prefix; see
capabilities(7))
B<NETWORK RULE> = [ I<QUALIFIERS> ] 'network' [ I<DOMAIN> ] [ I<TYPE> | I<PROTOCOL> ]
B<NETWORK RULE> = 'network' [ [ I<DOMAIN> [ I<TYPE> | I<PROTOCOL> ] ] | [ I<PROTOCOL> ] ] ','
B<DOMAIN> = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'bluetooth' | 'netlink' | 'unix' | 'rds' | 'llc' | 'can' | 'tipc' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' | 'mpls' | 'ib' ) ','
B<DOMAIN> = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'bluetooth' | 'netlink' ) ','
B<TYPE> = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' | 'packet' )
B<PROTOCOL> = ( 'tcp' | 'udp' | 'icmp' )
B<PROGRAM> = (non-whitespace characters except for '^', must start with '/'. Embedded spaces or tabs must be quoted.)
B<PROGRAMHAT> = '^' (non-whitespace characters; see aa_change_hat(2) for a description of how this "hat" is used.)
B<PROGRAMCHILD> = I<SUBPROFILE> name
B<MOUNT RULE> = ( I<MOUNT> | I<REMOUNT> | I<UMOUNT> )
B<MOUNT> = [ I<QUALIFIERS> ] 'mount' [ I<MOUNT CONDITIONS> ] [ I<SOURCE FILEGLOB> ] [ '-E<gt>' [ I<MOUNTPOINT FILEGLOB> ]
B<MOUNT> = [ 'audit' ] [ 'deny' ] 'mount' [ I<MOUNT CONDITIONS> ] [ I<SOURCE FILEGLOB> ] [ -E<gt> [ I<MOUNTPOINT FILEGLOB> ]
B<REMOUNT> = [ I<QUALIFIERS> ] 'remount' [ I<MOUNT CONDITIONS> ] I<MOUNTPOINT FILEGLOB>
B<REMOUNT> = [ 'audit' ] [ 'deny' ] 'remount' [ I<MOUNT CONDITIONS> ] I<MOUNTPOINT FILEGLOB>
B<UMOUNT> = [ I<QUALIFIERS> ] 'umount' [ I<MOUNT CONDITIONS> ] I<MOUNTPOINT FILEGLOB>
B<UMOUNT> = [ 'audit' ] [ 'deny' ] 'umount' [ I<MOUNT CONDITIONS> ] I<MOUNTPOINT FILEGLOB>
B<MOUNT CONDITIONS> = [ ( 'fstype' | 'vfstype' ) ( '=' | 'in' ) I<MOUNT FSTYPE EXPRESSION> ] [ 'options' ( '=' | 'in' ) I<MOUNT FLAGS EXPRESSION> ]
@@ -131,15 +97,9 @@ B<MOUNT FLAGS> = ( 'ro' | 'rw' | 'nosuid' | 'suid' | 'nodev' | 'dev' | 'noexec'
B<MOUNT EXPRESSION> = ( I<ALPHANUMERIC> | I<AARE> ) ...
B<PIVOT ROOT RULE> = [ I<QUALIFIERS> ] pivot_root [ oldroot=I<OLD PUT FILEGLOB> ] [ I<NEW ROOT FILEGLOB> ] [ '-E<gt>' I<PROFILE NAME> ]
B<PIVOT ROOT RULE> = [ 'audit' ] [ 'deny' ] pivot_root [ oldroot=I<OLD PUT FILEGLOB> ] [ I<NEW ROOT FILEGLOB> ] [ -E<gt> I<PROGRAMCHILD> ]
B<SOURCE FILEGLOB> = I<FILEGLOB>
B<MOUNTPOINT FILEGLOB> = I<FILEGLOB>
B<OLD PUT FILEGLOB> = I<FILEGLOB>
B<PTRACE_RULE> = [ I<QUALIFIERS> ] 'ptrace' [ I<PTRACE ACCESS PERMISSIONS> ] [ I<PTRACE PEER> ]
B<PTRACE_RULE> = [ 'audit' ] [ 'deny' ] 'ptrace' [ I<PTRACE ACCESS PERMISSIONS> ] [ I<PTRACE PEER> ]
B<PTRACE ACCESS PERMISSIONS> = I<PTRACE ACCESS> | I<PTRACE ACCESS LIST>
@@ -149,7 +109,7 @@ B<PTRACE ACCESS> = ( 'r' | 'w' | 'rw' | 'read' | 'readby' | 'trace' | 'tracedby'
B<PTRACE PEER> = 'peer' '=' I<AARE>
B<SIGNAL_RULE> = [ I<QUALIFIERS> ] 'signal' [ I<SIGNAL ACCESS PERMISSIONS> ] [ I<SIGNAL SET> ] [ I<SIGNAL PEER> ]
B<SIGNAL_RULE> = [ 'audit' ] [ 'deny' ] 'signal' [ I<SIGNAL ACCESS PERMISSIONS> ] [ I<SIGNAL SET> ] [ I<SIGNAL PEER> ]
B<SIGNAL ACCESS PERMISSIONS> = I<SIGNAL ACCESS> | I<SIGNAL ACCESS LIST>
@@ -161,19 +121,19 @@ B<SIGNAL SET> = 'set' '=' '(' I<SIGNAL LIST> ')'
B<SIGNAL LIST> = Comma or space separated list of I<SIGNALS>
B<SIGNALS> = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | 'sys' | 'emt' | 'exists' | 'rtmin+0' ... 'rtmin+32' )
B<SIGNALS> = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | 'sys' | 'emt' | 'exists' )
B<SIGNAL PEER> = 'peer' '=' I<AARE>
B<DBUS RULE> = ( I<DBUS MESSAGE RULE> | I<DBUS SERVICE RULE> | I<DBUS EAVESDROP RULE> | I<DBUS COMBINED RULE> )
B<DBUS MESSAGE RULE> = [ I<QUALIFIERS> ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ] [ I<DBUS PATH> ] [ I<DBUS INTERFACE> ] [ I<DBUS MEMBER> ] [ I<DBUS PEER> ]
B<DBUS MESSAGE RULE> = [ 'audit' ] [ 'deny' ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ] [ I<DBUS PATH> ] [ I<DBUS INTERFACE> ] [ I<DBUS MEMBER> ] [ I<DBUS PEER> ]
B<DBUS SERVICE RULE> = [ I<QUALIFIERS> ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ] [ I<DBUS NAME> ]
B<DBUS SERVICE RULE> = [ 'audit' ] [ 'deny' ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ] [ I<DBUS NAME> ]
B<DBUS EAVESDROP RULE> = [ I<QUALIFIERS> ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ]
B<DBUS EAVESDROP RULE> = [ 'audit' ] [ 'deny' ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ]
B<DBUS COMBINED RULE> = [ I<QUALIFIERS> ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ]
B<DBUS COMBINED RULE> = [ 'audit' ] [ 'deny' ] 'dbus' [ I<DBUS ACCESS EXPRESSION> ] [ I<DBUS BUS> ]
B<DBUS ACCESS EXPRESSION> = ( I<DBUS ACCESS> | '(' I<DBUS ACCESS LIST> ')' )
@@ -197,7 +157,9 @@ B<DBUS ACCESS> = ( 'send' | 'receive' | 'bind' | 'eavesdrop' ) (some accesses a
B<AARE> = B<?*[]{}^> (see below for meanings)
B<UNIX RULE> = [ I<QUALIFIERS> ] 'unix' [ I<UNIX ACCESS EXPR> ] [ I<UNIX RULE CONDS> ] [ I<UNIX LOCAL EXPR> ] [ I<UNIX PEER EXPR> ]
B<UNIX RILE> = [ I<UNIX QUALIFIERS> ] 'unix' [ I<UNIX ACCESS EXPR> ] [ I<UNIX RULE CONDS> ] [ I<UNIX LOCAL EXPR> ] [ I<UNIX PEER EXPR> ]
B<UNIX QUALIFIERS> = [ 'audit' ] [ 'allow' | 'deny' ]
B<UNIX ACCESS EXPR> = ( I<UNIX ACCESS> | I<UNIX ACCESS LIST> )
@@ -227,50 +189,24 @@ B<UNIX ATTR COND> 'attr' '=' ( I<AARE> | '(' '"' I<AARE> '"' | I<AARE> ')' )
B<UNIX OPT COND> 'opt' '=' ( I<AARE> | '(' '"' I<AARE> '"' | I<AARE> ')' )
B<RLIMIT RULE> = 'set' 'rlimit' [I<RLIMIT> 'E<lt>=' I<RLIMIT VALUE> ]
B<FILE RULE> = I<RULE QUALIFIER> ( '"' I<FILEGLOB> '"' | I<FILEGLOB> ) I<ACCESS> ','
B<RLIMIT> = ( 'cpu' | 'fsize' | 'data' | 'stack' | 'core' | 'rss' | 'nofile' | 'ofile' | 'as' | 'nproc' | 'memlock' | 'locks' | 'sigpending' | 'msgqueue' | 'nice' | 'rtprio' | 'rttime' )
B<RULE QUALIFIER> = [ 'audit' ] [ 'deny' ] [ 'owner' ]
B<RLIMIT VALUE> = ( I<RLIMIT SIZE> | I<RLIMIT NUMBER> | I<RLIMIT TIME> | I<RLIMIT NICE> )
B<FILEGLOB> = (must start with '/' (after variable expansion), B<AARE> have special meanings; see below. May include I<VARIABLE>. Rules with embedded spaces or tabs must be quoted. Rules must end with '/' to apply to directories.)
B<RLIMIT SIZE> = I<NUMBER> ( 'K' | 'M' | 'G' ) Only applies to RLIMIT of 'fsize', 'data', 'stack', 'core', 'rss', 'as', 'memlock', 'msgqueue'.
B<RLIMIT NUMBER> = number from 0 to max rlimit value. Only applies ot RLIMIT of 'ofile', 'nofile', 'locks', 'sigpending', 'nproc', 'rtprio'
B<RLIMIT TIME> = I<NUMBER> ( 'us' | 'microsecond' | 'microseconds' | 'ms' | 'millisecond' | 'milliseconds' | 's' | 'sec' | 'second' | 'seconds' | 'min' | 'minute' | 'minutes' | 'h' | 'hour' | 'hours' | 'd' | 'day' | 'days' | 'week' | 'weeks' ) Only applies to RLIMIT of 'cpu', 'rttime'. RLIMIT 'cpu' only allows units >= 'seconds'.
B<RLIMIT NICE> = a number between -20 and 19. Only applies to RLIMIT of 'nice'
B<FILE RULE> = [ I<QUALIFIERS> ] [ 'owner' ] ( 'file' | [ 'file' ] ( I<FILEGLOB> I<ACCESS> | I<ACCESS> I<FILEGLOB> ) [ '-E<gt>' I<EXEC TARGET> ] )
B<FILEGLOB> = ( I<QUOTED FILEGLOB> | I<UNQUOTED FILEGLOB> )
B<QUOTED FILEGLOB> = '"' I<UNQUOTED FILEGLOB> '"'
B<UNQUOTED FILEGLOB> = (must start with '/' (after variable expansion), B<AARE> have special meanings; see below. May include I<VARIABLE>. Rules with embedded spaces or tabs must be quoted. Rules must end with '/' to apply to directories.)
B<ACCESS> = ( 'r' | 'w' | 'a' | 'l' | 'k' | 'm' | I<EXEC TRANSITION> )+ (not all combinations are allowed; see below.)
B<EXEC TRANSITION> = ( 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx' | 'Cx' | 'pix' | 'Pix' | 'cix' | 'Cix' | 'pux' | 'PUx' | 'cux' | 'CUx' | 'x' ) ('x' is only allowed in rules with the deny qualifier, everything else only without the deny qualifier)
B<EXEC TARGET> = name (requires I<EXEC TRANSITION> specified)
B<LINK RULE> = I<QUALIFIERS> [ 'owner' ] 'link' [ 'subset' ] I<FILEGLOB> ( 'to' | '-E<gt>' ) I<FILEGLOB>
B<ACCESS> = ( 'r' | 'w' | 'l' | 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx -E<gt> ' I<PROGRAMCHILD> | 'Cx -E<gt> ' I<PROGRAMCHILD> | 'm' ) [ I<ACCESS> ... ] (not all combinations are allowed; see below.)
B<VARIABLE> = '@{' I<ALPHA> [ ( I<ALPHANUMERIC> | '_' ) ... ] '}'
B<VARIABLE ASSIGNMENT> = I<VARIABLE> ('=' | '+=') (space separated values)
B<ALIAS RULE> = I<ABS PATH> '-E<gt>' I<REWRITTEN ABS PATH>
B<ALIAS RULE> = I<ABS PATH> '-E<gt>' I<REWRITTEN ABS PATH> ','
B<ALPHA> = ('a', 'b', 'c', ... 'z', 'A', 'B', ... 'Z')
B<ALPHANUMERIC> = ('0', '1', '2', ... '9', 'a', 'b', 'c', ... 'z', 'A', 'B', ... 'Z')
B<CHANGE_PROFILE RULE> = 'change_profile' [ I<EXEC COND> ] [ '-E<gt>' I<PROFILE NAME> ]
B<EXEC COND> = I<FILEGLOB>
=back
All resources and programs need a full path. There may be any number of
@@ -334,42 +270,6 @@ modes:
- inherit execute
=item B<pix>
- discrete profile execute with inherit fallback
=item B<Pix>
- discrete profile execute with inherit fallback -- scrub the environment
=item B<cix>
- transition to subprofile on execute with inherit fallback
=item B<Cix>
- transition to subprofile on execute with inherit fallback -- scrub the environment
=item B<pux>
- discrete profile execute with fallback to unconfined
=item B<PUx>
- discrete profile execute with fallback to unconfined -- scrub the environment
=item B<cux>
- transition to subprofile on execute with fallback to unconfined
=item B<CUx>
- transition to subprofile on execute with fallback to unconfined -- scrub the environment
=item B<deny x>
- disallow execute (in rules with the deny qualifier)
=item B<m>
- allow PROT_EXEC with mmap(2) calls
@@ -429,7 +329,7 @@ over the callee. Use this mode only if the child absolutely must be
run unconfined and LD_PRELOAD must be used. Any profile using this mode
provides negligible security. Use at your own risk.
Incompatible with other exec transition modes and the deny qualifier.
Incompatible with 'Ux', 'px', 'Px', 'cx', 'Cx', 'ix'.
=item B<Ux - unconfined execute -- scrub the environment>
@@ -443,7 +343,7 @@ designated child processes to be run without any AppArmor protection.
Use this mode only if the child absolutely must be run unconfined. Use
at your own risk.
Incompatible with other exec transition modes and the deny qualifier.
Incompatible with 'ux', 'px', 'Px', 'cx', 'Cx', 'ix'.
=item B<px - Discrete Profile execute mode>
@@ -455,7 +355,7 @@ B<WARNING> 'px' does not scrub the environment of variables such as
LD_PRELOAD; as a result, the calling domain may have an undue amount of
influence over the callee.
Incompatible with other exec transition modes and the deny qualifier.
Incompatible with 'Ux', 'ux', 'Px', 'cx', 'Cx', 'ix'.
=item B<Px - Discrete Profile execute mode -- scrub the environment>
@@ -464,7 +364,7 @@ will invoke the Linux Kernel's B<unsafe_exec> routines to scrub
the environment, similar to setuid programs. (See ld.so(8) for some
information on setuid/setgid environment scrubbing.)
Incompatible with other exec transition modes and the deny qualifier.
Incompatible with 'Ux', 'ux', 'px', 'cx', 'Cx', 'ix'.
=item B<cx - Transition to Subprofile execute mode>
@@ -476,7 +376,7 @@ B<WARNING> 'cx' does not scrub the environment of variables such as
LD_PRELOAD; as a result, the calling domain may have an undue amount of
influence over the callee.
Incompatible with other exec transition modes and the deny qualifier.
Incompatible with 'Ux', 'ux', 'px', 'Px', 'Cx', 'ix'.
=item B<Cx - Transition to Subprofile execute mode -- scrub the environment>
@@ -485,7 +385,7 @@ will invoke the Linux Kernel's B<unsafe_exec> routines to scrub
the environment, similar to setuid programs. (See ld.so(8) for some
information on setuid/setgid environment scrubbing.)
Incompatible with other exec transition modes and the deny qualifier.
Incompatible with 'Ux', 'ux', 'px', 'Px', 'cx', 'ix'.
=item B<ix - Inherit execute mode>
@@ -499,58 +399,7 @@ profile, or losing the permissions of the current profile. There is no
version to scrub the environment because 'ix' executions don't change
privileges.
Incompatible with other exec transition modes and the deny qualifier.
=item B<Profile transition with inheritance fallback execute mode>
These modes attempt to perform a domain transition as specified by
the matching permission (shown below) and if that transition fails
to find the matching profile the domain transition proceeds using
the 'ix' transition mode.
'Pix' == 'Px' with fallback to 'ix'
'pix' == 'px' with fallback to 'ix'
'Cix' == 'Cx' with fallback to 'ix'
'cix' == 'cx' with fallback to 'ix'
Incompatible with other exec transition modes and the deny qualifier.
=item B<Profile transition with unconfined fallback execute mode>
These modes attempt to perform a domain transition as specified by
the matching permission (shown below) and if that transition fails
to find the matching profile the domain transition proceeds using
the 'ux' transition mode if 'pux', 'cux' or the 'Ux' transition mode
if 'PUx', 'CUx' is used.
'PUx' == 'Px' with fallback to 'Ux'
'pux' == 'px' with fallback to 'ux'
'CUx' == 'Cx' with fallback to 'Ux'
'cux' == 'cx' with fallback to 'ux'
Incompatible with other exec transition modes and the deny qualifier.
=item B<deny x - Deny execute>
For rules including the deny modifier, only 'x' is allowed to deny execute.
The 'ix', 'Px', 'px', 'Cx', 'cx' and the fallback modes conflict with the deny
modifier.
=item B<Directed profile transitions>
The directed ('px', 'Px', 'pix', 'Pix', 'pux', 'PUx') profile and
subprofile ('cx', 'Cx', 'cix', 'Cix', 'cux', 'CUx') transitions normally
determine the profile to transition to from the executable name. It
is however possible to specify the name of the profile that the transition
should use.
The name of the profile to transition to is specified using the '-E<gt>'
followed by the name of the profile to transition to. Eg.
/bin/** px -> profile,
Incompatible with other exec transition modes.
Incompatible with 'Ux', 'ux', 'Px', 'px', 'cx', 'Cx'. Implies 'm'.
=item B<m - Allow executable mapping>
@@ -567,71 +416,17 @@ B<LD_LIBRARY_PATH>, given to ld.so(8).
Allows the program to be able to create a link with this name. When a
link is created, the new link B<MUST> have a subset of permissions as
the original file (with the exception that the destination does not have
to have link access.) If there is an 'x' rule on the new link, it must
match the original file exactly.
the original file (with the exception that
the destination does not have to have link access.) If there is an 'x' rule
on the new link, it must match the original file exactly.
=item B<k - lock mode>
Allows the program to be able lock a file with this name. This permission
covers both advisory and mandatory locking.
=item B<leading OR trailing access permissions>
File rules can be specified with the access permission either leading
or trailing the file glob. Eg.
rw /**, # leading permissions
/** rw, # trailing permissions
When leading permissions are used further rule options and context
may be allowed, Eg.
l /foo -> /bar, # lead 'l' link permission is equivalent to link rules
=back
=head2 Link rules
Link rules allow specifying permission to form a hard link as a link
target pair. If the subset condition is specified then the permissions
to access the link file must be a subset of the profiles permissions
to access the target file. If there is an 'x' rule on the new link, it
must match the original file exactly.
Eg.
/file1 r,
/file2 rwk,
/link* rw,
link subset /link* -> /**,
The link rule allows linking of /link to both /file1 or /file2 by
name however because the /link file has 'rw' permissions it is not
allowed to link to /file1 because that would grant an access path
to /file1 with more permissions than the 'r' permissions the profile
specifies.
A link of /link to /file2 would be allowed because the 'rw' permissions
of /link are a subset of the 'rwk' permissions for /file1.
The link rule is equivalent to specifying the 'l' link permission as
a leading permission with no other file access permissions. When this
is done the link rule options can be specified.
The following link rule is equivalent to the 'l' permission file rule
link /foo -> bar,
l /foo -> /bar,
File rules that specify the 'l' permission and don't specify the extend
link permissions map to link rules as follows.
/foo l,
l /foo,
link subset /foo -> /**,
=head2 Comments
Comments start with # and may begin at any place within a line. The
@@ -975,9 +770,6 @@ Example AppArmor signal rules:
# Allow us to signal ourselves using the built-in @{profile_name} variable
signal peer=@{profile_name},
# Allow two real-time signals
signal set=(rtmin+0 rtmin+32),
=head2 DBus rules
AppArmor supports DBus mediation. The mediation is performed in conjunction
@@ -1162,69 +954,6 @@ the much wider permission rule of
network unix,
=head2 change_profile rules
AppArmor supports self directed profile transitions via the change_profile
api. Change_profile rules control which permissions for which profiles
a confined task can transition to. The profile name can contain apparmor
pattern matching to specify different profiles.
change_profile -> **,
The change_profile api allows the transition to be delayed until when
a task executes another application. If an exec rule transition is
specified for the application and the change_profile api is used to
make a transition at exec time, the transition specified by the
change_profile api takes precedence.
The Change_profile permission can restrict which profiles can be transitioned
to based off of the executable name by specifying the exec condition.
change_profile /bin/bash -> new_profile,
The restricting of the transition profile to a given executable at exec
time is only useful when then current task is allowed to make dynamic
decisions about what confinement should be, but the decision set needs
to be controlled. A list of profiles or multiple rules can be used to
specify the profiles in the set. Eg.
change_profile /bin/bash -> {new_profile1,new_profile2,new_profile3},
An exec rule can be used to specify a transition for the executable, if
the transition should be allowed even if the change_profile api has not
been used to select a transition for those available in the change_profile
rule set. Eg.
/bin/bash Px -> new_profile1,
change_profile /bin/bash -> {new_profile1,new_profile2,new_profile3},
=head2 rlimit rules
AppArmor can set and control the resource limits associated with a
profile as described in the setrlimit(2) man page.
The AppArmor rlimit controls allow setting of limits and restricting
changes of them and these actions can be audited. Enforcement of the
set limits is handled by the standard kernel enforcement mechanism
for rlimits and will not result in an audited apparmor message if
the limit is enforced.
If a profile does not have an rlimit rule associated with a given
rlimit then the rlimit is left alone and regular access, including
changing the limit, is allowed. However if the profile sets an rlimit
then the current limit is checked and if greater than the limit specified
in the rule it will be changed to the specified limit.
AppArmor rlimit rules control the hard limit of an application and
ensure that if the hard limit is lowered that the soft limit does not
exceed the hard limit value.
Eg.
set rlimit data <= 100M,
set rlimit nproc <= 10,
set rlimit nice <= 5,
=head2 Variables
AppArmor's policy language allows embedding variables into file rules
@@ -1243,10 +972,8 @@ provided AppArmor policy:
@{HOMEDIRS}
@{multiarch}
@{pid}
@{pids}
@{PROC}
@{securityfs}
@{apparmorfs}
@{sys}
@{tid}
@{XDG_DESKTOP_DIR}
@@ -1348,12 +1075,6 @@ Rule qualifiers can modify the rule and/or permissions within the rule.
=over 4
=item B<allow>
Specifies that permissions requests that match the rule are allowed. This
is the default value for rules and does not need to be specified. Conflicts
with the I<deny> qualifier.
=item B<audit>
Specifies that permissions requests that match the rule should be recorded
@@ -1362,8 +1083,7 @@ to the audit log.
=item B<deny>
Specifies that permissions requests that match the rule should be denied
without logging. Can be combined with 'audit' to enable logging. Conflicts
with the I<allow> qualifier.
without logging. Can be combined with 'audit' to enable logging.
=item B<owner>
@@ -1372,16 +1092,6 @@ referenced by the permission check.
=back
=head3 Qualifier Blocks
Rule Qualifiers can be applied to multiple rules at a time by grouping the
rules into a rule block.
audit {
/foo r,
network,
}
=head2 #include mechanism
AppArmor provides an easy abstraction mechanism to group common file

View File

@@ -183,12 +183,6 @@ defined as an absolute paths.
Set the location of the apparmor security filesystem (default is
"/sys/kernel/security/apparmor").
=item -M n, --features-file n
Use the features file located at path "n" (default is
/etc/apparmor.d/cache/.features). If the --cache-loc option is present, the
".features" file in the specified cache directory is used.
=item -m n, --match-string n
Only use match features "n".
@@ -280,7 +274,7 @@ Use --help=dump to see a full list of which dump flags are supported
=item -O n, --optimize=n
Set the optimization flags used by policy compilation. A single optimization
Set the optimization flags used by policy compilation. A sinlge optimization
flag can be toggled per -O option, but the optimize flag can be passed
multiple times. Turning off some phases of the optimization can make
it so that policy can't complete compilation due to size constraints

View File

@@ -98,7 +98,7 @@ dbus_rule::dbus_rule(int mode_p, struct cond_entry *conds,
if (mode_p) {
mode = mode_p;
if (mode & ~AA_VALID_DBUS_PERMS)
yyerror("mode contains unknown dbus access\n");
yyerror("mode contains unknown dbus accesss\n");
else if (message_rule && (mode & AA_DBUS_BIND))
yyerror("dbus \"bind\" access cannot be used with message rule conditionals\n");
else if (service_rule && (mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE)))
@@ -228,7 +228,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
busbuf.append(buffer.str());
if (bus) {
ptype = convert_aaregex_to_pcre(bus, 0, glob_default, busbuf, &pos);
ptype = convert_aaregex_to_pcre(bus, 0, busbuf, &pos);
if (ptype == ePatternInvalid)
goto fail;
} else {
@@ -238,7 +238,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
vec[0] = busbuf.c_str();
if (name) {
ptype = convert_aaregex_to_pcre(name, 0, glob_default, namebuf, &pos);
ptype = convert_aaregex_to_pcre(name, 0, namebuf, &pos);
if (ptype == ePatternInvalid)
goto fail;
vec[1] = namebuf.c_str();
@@ -248,7 +248,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
}
if (peer_label) {
ptype = convert_aaregex_to_pcre(peer_label, 0, glob_default,
ptype = convert_aaregex_to_pcre(peer_label, 0,
peer_labelbuf, &pos);
if (ptype == ePatternInvalid)
goto fail;
@@ -259,7 +259,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
}
if (path) {
ptype = convert_aaregex_to_pcre(path, 0, glob_default, pathbuf, &pos);
ptype = convert_aaregex_to_pcre(path, 0, pathbuf, &pos);
if (ptype == ePatternInvalid)
goto fail;
vec[3] = pathbuf.c_str();
@@ -269,7 +269,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
}
if (interface) {
ptype = convert_aaregex_to_pcre(interface, 0, glob_default, ifacebuf, &pos);
ptype = convert_aaregex_to_pcre(interface, 0, ifacebuf, &pos);
if (ptype == ePatternInvalid)
goto fail;
vec[4] = ifacebuf.c_str();
@@ -279,7 +279,7 @@ int dbus_rule::gen_policy_re(Profile &prof)
}
if (member) {
ptype = convert_aaregex_to_pcre(member, 0, glob_default, memberbuf, &pos);
ptype = convert_aaregex_to_pcre(member, 0, memberbuf, &pos);
if (ptype == ePatternInvalid)
goto fail;
vec[5] = memberbuf.c_str();

View File

@@ -29,9 +29,9 @@
#define AA_MAY_WRITE (1 << 1)
#define AA_MAY_READ (1 << 2)
#define AA_MAY_APPEND (1 << 3)
#define AA_OLD_MAY_LINK (1 << 4)
#define AA_OLD_MAY_LOCK (1 << 5)
#define AA_OLD_EXEC_MMAP (1 << 6)
#define AA_MAY_LINK (1 << 4)
#define AA_MAY_LOCK (1 << 5)
#define AA_EXEC_MMAP (1 << 6)
#define AA_EXEC_PUX (1 << 7)
#define AA_EXEC_UNSAFE (1 << 8)
#define AA_EXEC_INHERIT (1 << 9)
@@ -42,8 +42,8 @@
#define AA_BASE_PERMS (AA_MAY_EXEC | AA_MAY_WRITE | \
AA_MAY_READ | AA_MAY_APPEND | \
AA_OLD_MAY_LINK | AA_OLD_MAY_LOCK | \
AA_EXEC_PUX | AA_OLD_EXEC_MMAP | \
AA_MAY_LINK | AA_MAY_LOCK | \
AA_EXEC_PUX | AA_EXEC_MMAP | \
AA_EXEC_UNSAFE | AA_EXEC_INHERIT | \
AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \
AA_EXEC_MOD_2 | AA_EXEC_MOD_3)
@@ -95,8 +95,8 @@
#define ALL_USER_EXEC (AA_USER_EXEC | AA_USER_EXEC_TYPE)
#define ALL_OTHER_EXEC (AA_OTHER_EXEC | AA_OTHER_EXEC_TYPE)
#define AA_LINK_BITS ((AA_OLD_MAY_LINK << AA_USER_SHIFT) | \
(AA_OLD_MAY_LINK << AA_OTHER_SHIFT))
#define AA_LINK_BITS ((AA_MAY_LINK << AA_USER_SHIFT) | \
(AA_MAY_LINK << AA_OTHER_SHIFT))
#define SHIFT_MODE(MODE, SHIFT) ((((MODE) & AA_BASE_PERMS) << (SHIFT))\
| ((MODE) & ~AA_FILE_PERMS))
@@ -104,7 +104,7 @@
| ((MODE) & ~AA_FILE_PERMS))
#define AA_LINK_SUBSET_TEST (AA_OLD_MAY_LINK << 1)
#define AA_LINK_SUBSET_TEST (AA_MAY_LINK << 1)
#define LINK_SUBSET_BITS ((AA_LINK_SUBSET_TEST << AA_USER_SHIFT) | \
(AA_LINK_SUBSET_TEST << AA_OTHER_SHIFT))
#define LINK_TO_LINK_SUBSET(X) (((X) << 1) & AA_LINK_SUBSET_TEST)
@@ -137,9 +137,9 @@ enum pattern_t {
#define HAS_MAY_WRITE(mode) ((mode) & AA_MAY_WRITE)
#define HAS_MAY_APPEND(mode) ((mode) & AA_MAY_APPEND)
#define HAS_MAY_EXEC(mode) ((mode) & AA_MAY_EXEC)
#define HAS_MAY_LINK(mode) ((mode) & AA_OLD_MAY_LINK)
#define HAS_MAY_LOCK(mode) ((mode) & AA_OLD_MAY_LOCK)
#define HAS_EXEC_MMAP(mode) ((mode) & AA_OLD_EXEC_MMAP)
#define HAS_MAY_LINK(mode) ((mode) & AA_MAY_LINK)
#define HAS_MAY_LOCK(mode) ((mode) & AA_MAY_LOCK)
#define HAS_EXEC_MMAP(mode) ((mode) & AA_EXEC_MMAP)
#define HAS_EXEC_UNSAFE(mode) ((mode) & AA_EXEC_UNSAFE)
#define HAS_CHANGE_PROFILE(mode) ((mode) & AA_CHANGE_PROFILE)
@@ -161,6 +161,3 @@ static inline int is_merged_x_consistent(int a, int b)
}
#endif /* ! _IMMUNIX_H */
/* LocalWords: MMAP
*/

View File

@@ -16,27 +16,134 @@
* Ltd.
*/
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <cstdint>
#include <sys/apparmor_private.h>
#include "lib.h"
#include "parser.h"
int dirat_for_each(int dirfd, const char *name, void *data,
int (* cb)(int, const char *, struct stat *, void *))
/**
* dirat_for_each: iterate over a directory calling cb for each entry
* @dir: already opened directory (MAY BE NULL)
* @name: name of the directory (MAY BE NULL)
* @data: data pointer to pass to the callback fn (MAY BE NULL)
* @cb: the callback to pass entry too (NOT NULL)
*
* Iterate over the entries in a directory calling cb for each entry.
* The directory to iterate is determined by a combination of @dir and
* @name.
*
* IF @name is a relative path it is determine relative to at @dir if it
* is specified, else it the lookup is done relative to the current
* working directory.
*
* If @name is not specified then @dir is used as the directory to iterate
* over.
*
* It is an error if both @name and @dir are null
*
* The cb function is called with the DIR in use and the name of the
* file in that directory. If the file is to be opened it should
* use the openat, fstatat, and related fns.
*
* Returns: 0 on success, else -1 and errno is set to the error code
*/
int dirat_for_each(DIR *dir, const char *name, void *data,
int (* cb)(DIR *, const char *, struct stat *, void *))
{
int retval = _aa_dirat_for_each(dirfd, name, data, cb);
struct dirent *dirent = NULL;
DIR *d = NULL;
int error;
if (retval)
PDEBUG("dirat_for_each failed: %m\n");
if (!cb || (!dir && !name)) {
errno = EINVAL;
return -1;
}
return retval;
if (dir && (!name || *name != '/')) {
dirent = (struct dirent *)
malloc(offsetof(struct dirent, d_name) +
fpathconf(dirfd(dir), _PC_NAME_MAX) + 1);
} else {
dirent = (struct dirent *)
malloc(offsetof(struct dirent, d_name) +
pathconf(name, _PC_NAME_MAX) + 1);
}
if (!dirent) {
PDEBUG("could not alloc dirent");
return -1;
}
if (name) {
if (dir && *name != '/') {
int fd = openat(dirfd(dir), name, O_RDONLY);
if (fd == -1)
goto fail;
d = fdopendir(fd);
} else {
d = opendir(name);
}
PDEBUG("Open dir '%s': %s\n", name, d ? "succeeded" : "failed");
if (!(d))
goto fail;
} else { /* dir && !name */
PDEBUG("Recieved cache directory\n");
d = dir;
}
for (;;) {
struct dirent *ent;
struct stat my_stat;
error = readdir_r(d, dirent, &ent);
if (error) {
PDEBUG("readdir_r failed");
errno = error; /* readdir_r directly returns an errno */
goto fail;
} else if (!ent) {
break;
}
if (strcmp(ent->d_name, ".") == 0 ||
strcmp(ent->d_name, "..") == 0)
continue;
if (fstatat(dirfd(d), ent->d_name, &my_stat, 0)) {
PDEBUG("stat failed for '%s'", name);
goto fail;
}
if (cb(d, ent->d_name, &my_stat, data)) {
PDEBUG("dir_for_each callback failed\n");
goto fail;
}
}
if (d != dir)
closedir(d);
free(dirent);
return 0;
fail:
error = errno;
if (d && d != dir)
closedir(d);
free(dirent);
errno = error;
return -1;
}
/**
@@ -45,7 +152,7 @@ int dirat_for_each(int dirfd, const char *name, void *data,
*
* Returns: true if an octal digit, else false
*/
int isodigit(char c)
bool isodigit(char c)
{
return (c >= '0' && c <= '7') ? true : false;
}

View File

@@ -1,18 +1,12 @@
#ifndef __AA_LIB_H_
#define __AA_LIB_H_
#include <sys/apparmor_private.h>
#include <dirent.h>
#define autofree __attribute((cleanup(_aa_autofree)))
#define autoclose __attribute((cleanup(_aa_autoclose)))
#define autofclose __attribute((cleanup(_aa_autofclose)))
int dirat_for_each(DIR *dir, const char *name, void *data,
int (* cb)(DIR *, const char *, struct stat *, void *));
#define asprintf _aa_asprintf
int dirat_for_each(int dirfd, const char *name, void *data,
int (* cb)(int, const char *, struct stat *, void *));
int isodigit(char c);
bool isodigit(char c);
long strntol(const char *str, const char **endptr, int base, long maxval,
size_t n);
int strn_escseq(const char **pos, const char *chrs, size_t n);

View File

@@ -1,17 +1,10 @@
# Profiling:
#EXTRA_CFLAGS = -pg
ifdef USE_SYSTEM
# Using the system libapparmor
INCLUDE_APPARMOR =
else
INCLUDE_APPARMOR = -I../../libraries/libapparmor/include
endif
TARGET=libapparmor_re.a
CFLAGS ?= -g -Wall -O2 ${EXTRA_CFLAGS} -std=gnu++0x
CXXFLAGS := ${CFLAGS} ${INCLUDE_APPARMOR}
CXXFLAGS := ${CFLAGS}
ARFLAGS=-rcs

View File

@@ -35,13 +35,13 @@
#include "../immunix.h"
aare_rules::~aare_rules(void)
{
if (root)
root->release();
unique_perms.clear();
expr_map.clear();
aare_reset_matchflags();
}
bool aare_rules::add_rule(const char *rule, int deny, uint32_t perms,
@@ -50,15 +50,40 @@ bool aare_rules::add_rule(const char *rule, int deny, uint32_t perms,
return add_rule_vec(deny, perms, audit, 1, &rule, flags);
}
#define FLAGS_WIDTH 2
#define MATCH_FLAGS_SIZE (sizeof(uint32_t) * 8 - 1)
MatchFlag *match_flags[FLAGS_WIDTH][MATCH_FLAGS_SIZE];
DenyMatchFlag *deny_flags[FLAGS_WIDTH][MATCH_FLAGS_SIZE];
#define EXEC_MATCH_FLAGS_SIZE (AA_EXEC_COUNT *2 * 2 * 2) /* double for each of ix pux, unsafe x bits * u::o */
MatchFlag *exec_match_flags[FLAGS_WIDTH][EXEC_MATCH_FLAGS_SIZE]; /* mods + unsafe + ix + pux * u::o */
ExactMatchFlag *exact_match_flags[FLAGS_WIDTH][EXEC_MATCH_FLAGS_SIZE]; /* mods + unsafe + ix + pux *u::o */
void aare_reset_matchflags(void)
{
uint32_t i, j;
#define RESET_FLAGS(group, size) { \
for (i = 0; i < FLAGS_WIDTH; i++) { \
for (j = 0; j < size; j++) { \
if ((group)[i][j]) delete (group)[i][j]; \
(group)[i][j] = NULL; \
} \
} \
}
RESET_FLAGS(match_flags, MATCH_FLAGS_SIZE);
RESET_FLAGS(deny_flags, MATCH_FLAGS_SIZE);
RESET_FLAGS(exec_match_flags, EXEC_MATCH_FLAGS_SIZE);
RESET_FLAGS(exact_match_flags, EXEC_MATCH_FLAGS_SIZE);
#undef RESET_FLAGS
}
void aare_rules::add_to_rules(Node *tree, Node *perms)
{
if (reverse)
flip_tree(tree);
Node *base = expr_map[perms];
if (base)
expr_map[perms] = new AltNode(base, tree);
if (root)
root = new AltNode(root, new CatNode(tree, perms));
else
expr_map[perms] = tree;
root = new CatNode(tree, perms);
}
static Node *cat_with_null_seperator(Node *l, Node *r)
@@ -66,6 +91,84 @@ static Node *cat_with_null_seperator(Node *l, Node *r)
return new CatNode(new CatNode(l, new CharNode(0)), r);
}
static Node *convert_file_perms(int deny, uint32_t perms, uint32_t audit,
bool exact_match)
{
Node *accept;
assert(perms != 0);
/* 0x7f == 4 bits x mods + 1 bit unsafe mask + 1 bit ix, + 1 pux after shift */
#define EXTRACT_X_INDEX(perm, shift) (((perm) >> (shift + 7)) & 0x7f)
/* the permissions set is assumed to be non-empty if any audit
* bits are specified */
accept = NULL;
for (unsigned int n = 0; perms && n < (sizeof(perms) * 8); n++) {
uint32_t mask = 1 << n;
if (!(perms & mask))
continue;
int ai = audit & mask ? 1 : 0;
perms &= ~mask;
Node *flag;
if (mask & ALL_AA_EXEC_TYPE)
/* these cases are covered by EXEC_BITS */
continue;
if (deny) {
if (deny_flags[ai][n]) {
flag = deny_flags[ai][n];
} else {
//fprintf(stderr, "Adding deny ai %d mask 0x%x audit 0x%x\n", ai, mask, audit & mask);
deny_flags[ai][n] = new DenyMatchFlag(mask, audit & mask);
flag = deny_flags[ai][n];
}
} else if (mask & AA_EXEC_BITS) {
uint32_t eperm = 0;
uint32_t index = 0;
if (mask & AA_USER_EXEC) {
eperm = mask | (perms & AA_USER_EXEC_TYPE);
index = EXTRACT_X_INDEX(eperm, AA_USER_SHIFT);
} else {
eperm = mask | (perms & AA_OTHER_EXEC_TYPE);
index = EXTRACT_X_INDEX(eperm, AA_OTHER_SHIFT) + (AA_EXEC_COUNT << 2);
}
//fprintf(stderr, "index %d eperm 0x%x\n", index, eperm);
if (exact_match) {
if (exact_match_flags[ai][index]) {
flag = exact_match_flags[ai][index];
} else {
exact_match_flags[ai][index] = new ExactMatchFlag(eperm, audit & mask);
flag = exact_match_flags[ai][index];
}
} else {
if (exec_match_flags[ai][index]) {
flag = exec_match_flags[ai][index];
} else {
exec_match_flags[ai][index] = new MatchFlag(eperm, audit & mask);
flag = exec_match_flags[ai][index];
}
}
} else {
if (match_flags[ai][n]) {
flag = match_flags[ai][n];
} else {
match_flags[ai][n] = new MatchFlag(mask, audit & mask);
flag = match_flags[ai][n];
}
}
if (accept)
accept = new AltNode(accept, flag);
else
accept = flag;
} /* for ... */
return accept;
}
bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit,
int count, const char **rulev, dfaflags_t flags)
{
@@ -99,7 +202,7 @@ bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit,
if (reverse)
flip_tree(tree);
accept = unique_perms.insert(deny, perms, audit, exact_match);
accept = convert_file_perms(deny, perms, audit, exact_match);
if (flags & DFA_DUMP_RULE_EXPR) {
cerr << "rule: ";
@@ -132,30 +235,6 @@ void *aare_rules::create_dfa(size_t *size, dfaflags_t flags)
{
char *buffer = NULL;
/* finish constructing the expr tree from the different permission
* set nodes */
PermExprMap::iterator i = expr_map.begin();
if (i != expr_map.end()) {
if (flags & DFA_CONTROL_TREE_SIMPLE) {
Node *tmp = simplify_tree(i->second, flags);
root = new CatNode(tmp, i->first);
} else
root = new CatNode(i->second, i->first);
for (i++; i != expr_map.end(); i++) {
Node *tmp;
if (flags & DFA_CONTROL_TREE_SIMPLE) {
tmp = simplify_tree(i->second, flags);
} else
tmp = i->second;
root = new AltNode(root, new CatNode(tmp, i->first));
}
}
/* dumping of the none simplified tree without -O no-expr-simplify
* is broken because we need to build the tree above first, and
* simplification is woven into the build. Reevaluate how to fix
* this debug dump.
*/
label_nodes(root);
if (flags & DFA_DUMP_TREE) {
cerr << "\nDFA: Expression Tree\n";
@@ -164,13 +243,7 @@ void *aare_rules::create_dfa(size_t *size, dfaflags_t flags)
}
if (flags & DFA_CONTROL_TREE_SIMPLE) {
/* This is old total tree, simplification point
* For now just do simplification up front. It gets most
* of the benefit running on the smaller chains, and is
* overall faster because there are less nodes. Reevaluate
* once tree simplification is rewritten
*/
//root = simplify_tree(root, flags);
root = simplify_tree(root, flags);
if (flags & DFA_DUMP_SIMPLE_TREE) {
cerr << "\nDFA: Simplified Expression Tree\n";

View File

@@ -26,78 +26,14 @@
#include "apparmor_re.h"
#include "expr-tree.h"
class UniquePerm {
public:
bool deny;
bool exact_match;
uint32_t perms;
uint32_t audit;
bool operator<(UniquePerm const &rhs)const
{
if (deny == rhs.deny) {
if (exact_match == rhs.exact_match) {
if (perms == rhs.perms)
return audit < rhs.audit;
return perms < rhs.perms;
}
return exact_match;
}
return deny;
}
};
class UniquePermsCache {
public:
typedef map<UniquePerm, Node*> UniquePermMap;
typedef UniquePermMap::iterator iterator;
UniquePermMap nodes;
UniquePermsCache(void) { };
~UniquePermsCache() { clear(); }
void clear()
{
for (iterator i = nodes.begin(); i != nodes.end(); i++) {
delete i->second;
}
nodes.clear();
}
Node *insert(bool deny, uint32_t perms, uint32_t audit,
bool exact_match)
{
UniquePerm tmp = { deny, exact_match, perms, audit };
iterator res = nodes.find(tmp);
if (res == nodes.end()) {
Node *node;
if (deny)
node = new DenyMatchFlag(perms, audit);
else if (exact_match)
node = new ExactMatchFlag(perms, audit);
else
node = new MatchFlag(perms, audit);
pair<iterator, bool> val = nodes.insert(make_pair(tmp, node));
if (val.second == false)
return val.first->second;
return node;
}
return res->second;
}
};
typedef std::map<Node *, Node *> PermExprMap;
class aare_rules {
Node *root;
void add_to_rules(Node *tree, Node *perms);
UniquePermsCache unique_perms;
PermExprMap expr_map;
public:
public:
int reverse;
int rule_count;
aare_rules(void): root(NULL), unique_perms(), expr_map(), reverse(0), rule_count(0) { };
aare_rules(int reverse): root(NULL), unique_perms(), expr_map(), reverse(reverse), rule_count(0) { };
aare_rules(): root(NULL), reverse(0), rule_count(0) { };
aare_rules(int reverse): root(NULL), reverse(reverse), rule_count(0) { };
~aare_rules();
bool add_rule(const char *rule, int deny, uint32_t perms,
@@ -107,4 +43,6 @@ class aare_rules {
void *create_dfa(size_t *size, dfaflags_t flags);
};
void aare_reset_matchflags(void);
#endif /* __LIBAA_RE_RULES_H */

View File

@@ -554,7 +554,7 @@ static int build_mnt_opts(std::string& buffer, struct value_list *opts)
}
list_for_each(opts, ent) {
ptype = convert_aaregex_to_pcre(ent->value, 0, glob_default, buffer, &pos);
ptype = convert_aaregex_to_pcre(ent->value, 0, buffer, &pos);
if (ptype == ePatternInvalid)
return FALSE;

View File

@@ -103,7 +103,7 @@
#define MS_CMDS (MS_MOVE | MS_REMOUNT | MS_BIND | MS_RBIND | \
MS_UNBINDABLE | MS_RUNBINDABLE | MS_PRIVATE | MS_RPRIVATE | \
MS_SLAVE | MS_RSLAVE | MS_SHARED | MS_RSHARED)
#define MS_REMOUNT_FLAGS (MS_ALL_FLAGS & ~(MS_CMDS & ~MS_REMOUNT & ~MS_BIND & ~MS_RBIND))
#define MS_REMOUNT_FLAGS (MS_ALL_FLAGS & ~(MS_CMDS & ~MS_REMOUNT))
#define MNT_SRC_OPT 1
#define MNT_DST_OPT 2

View File

@@ -25,7 +25,6 @@
#include <sstream>
#include <map>
#include "lib.h"
#include "parser.h"
#include "profile.h"
#include "parser_yacc.h"
@@ -155,8 +154,7 @@ static struct network_tuple network_mappings[] = {
static size_t kernel_af_max(void) {
char buffer[32];
int major;
autoclose int fd = -1;
int res;
int fd, res;
if (!net_af_max_override) {
return 0;
@@ -170,6 +168,7 @@ static size_t kernel_af_max(void) {
/* fall back to default provided during build */
return 0;
res = read(fd, &buffer, sizeof(buffer) - 1);
close(fd);
if (res <= 0)
return 0;
buffer[res] = '\0';

View File

@@ -17,11 +17,8 @@
## Be verbose
#verbose
## Set additional include path
#Include /etc/apparmor.d/
# or
#Include /usr/share/apparmor
## Set include path
#Include /etc/apparmor.d/abstractions
## Set location of apparmor filesystem
#subdomainfs /sys/kernel/security/apparmor

View File

@@ -30,8 +30,6 @@
#include <libintl.h>
#define _(s) gettext(s)
#include <sys/apparmor.h>
#include "immunix.h"
#include "libapparmor_re/apparmor_re.h"
#include "libapparmor_re/aare_rules.h"
@@ -44,8 +42,6 @@ using namespace std;
class Profile;
class rule_t;
#define MODULE_NAME "apparmor"
/* Global variable to pass token to lexer. Will be replaced by parameter
* when lexer and parser are made reentrant
*/
@@ -100,10 +96,7 @@ struct cond_entry_list {
struct cod_entry {
char *ns;
char *name;
union {
char *link_name;
char *onexec;
};
char *link_name;
char *nt_name;
Profile *prof; /* Special profile defined
* just for this executable */
@@ -313,6 +306,7 @@ extern int option;
extern int current_lineno;
extern dfaflags_t dfaflags;
extern const char *progname;
extern char *subdomainbase;
extern char *profilename;
extern char *profile_ns;
extern char *current_filename;
@@ -322,20 +316,9 @@ extern void pwarn(const char *fmt, ...) __attribute__((__format__(__printf__, 1,
/* from parser_main (cannot be used in tst builds) */
extern int force_complain;
extern struct timespec mru_tstamp;
extern void update_mru_tstamp(FILE *file);
extern void display_version(void);
extern int show_cache;
extern int skip_cache;
extern int skip_read_cache;
extern int write_cache;
extern int cond_clear_cache;
extern int force_clear_cache;
extern int create_cache_dir;
extern int preprocess_only;
extern int skip_mode_force;
extern int abort_on_error;
extern int skip_bad_cache_rebuild;
extern int mru_skip_cache;
extern int debug_cache;
/* provided by parser_lex.l (cannot be used in tst builds) */
extern FILE *yyin;
@@ -351,9 +334,7 @@ extern const char *basedir;
#define default_match_pattern "[^\\000]*"
#define anyone_match_pattern "[^\\000]+"
#define glob_default 0
#define glob_null 1
extern pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
extern pattern_t convert_aaregex_to_pcre(const char *aare, int anchor,
std::string& pcre, int *first_re_pos);
extern int build_list_val_expr(std::string& buffer, struct value_list *list);
extern int convert_entry(std::string& buffer, char *entry);
@@ -361,6 +342,8 @@ extern int clear_and_convert_entry(std::string& buffer, char *entry);
extern int process_regex(Profile *prof);
extern int post_process_entry(struct cod_entry *entry);
extern void reset_regex(void);
extern int process_policydb(Profile *prof);
extern int process_policy_ents(Profile *prof);
@@ -393,9 +376,6 @@ extern int get_rlimit(const char *name);
extern char *process_var(const char *var);
extern int parse_mode(const char *mode);
extern int parse_X_mode(const char *X, int valid, const char *str_mode, int *mode, int fail);
void parse_label(char **ns, char **name, const char *label);
void parse_named_transition_target(struct named_transition *nt,
const char *target);
extern struct cod_entry *new_entry(char *ns, char *id, int mode, char *link_id);
/* returns -1 if value != true or false, otherwise 0 == false, 1 == true */
@@ -405,9 +385,6 @@ extern void free_cod_entries(struct cod_entry *list);
extern void __debug_capabilities(uint64_t capset, const char *name);
void debug_cod_entries(struct cod_entry *list);
#define SECONDS_P_MS (1000LL * 1000LL)
long long convert_time_units(long long value, long long base, const char *units);
/* parser_symtab.c */
struct set_value {
@@ -434,8 +411,7 @@ extern void free_aliases(void);
extern int profile_merge_rules(Profile *prof);
/* parser_interface.c */
extern int load_profile(int option, aa_kernel_interface *kernel_interface,
Profile *prof, int cache_fd);
extern int load_profile(int option, Profile *prof);
extern void sd_serialize_profile(std::ostringstream &buf, Profile *prof,
int flatten);
extern int sd_load_buffer(int option, char *buffer, int size);
@@ -456,12 +432,9 @@ extern int process_profile_policydb(Profile *prof);
extern int post_merge_rules(void);
extern int merge_hat_rules(Profile *prof);
extern Profile *merge_policy(Profile *a, Profile *b);
extern int load_policy(int option, aa_kernel_interface *kernel_interface,
int cache_fd);
extern int load_policy(int option);
extern int load_hats(std::ostringstream &buf, Profile *prof);
extern int load_flattened_hats(Profile *prof, int option,
aa_kernel_interface *kernel_interface,
int cache_fd);
extern int load_flattened_hats(Profile *prof, int option);
extern void dump_policy_hats(Profile *prof);
extern void dump_policy_names(void);
void dump_policy(void);

View File

@@ -57,7 +57,7 @@
* numbers where supported.
*/
uint32_t policy_version = 2;
uint32_t parser_abi_version = 2;
uint32_t parser_abi_version = 1;
uint32_t kernel_abi_version = 5;
int force_complain = 0;
@@ -82,6 +82,7 @@ int option = OPTION_ADD;
dfaflags_t dfaflags = (dfaflags_t)(DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_DIFF_ENCODE);
dfaflags_t warnflags = 0;
char *subdomainbase = NULL;
const char *progname = __FILE__;
char *profile_ns = NULL;
char *profilename = NULL;

View File

@@ -45,8 +45,6 @@
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include "lib.h"
#include "parser.h"
#include "parser_include.h"
@@ -178,7 +176,7 @@ int add_search_dir(const char *dir)
SUBDOMAIN_PATH=/etc/subdomain.d/include */
void parse_default_paths(void)
{
autofclose FILE *f;
FILE *f;
char buf[1024];
char *t, *s;
int saved_npath = npath;
@@ -204,6 +202,7 @@ void parse_default_paths(void)
} while (s != NULL);
}
}
fclose(f);
/* if subdomain.conf doesn't set a base search dir set it to this */
out:

View File

@@ -27,9 +27,7 @@
#include <string>
#include <sstream>
#include <sys/apparmor.h>
#include "lib.h"
#include "parser.h"
#include "profile.h"
#include "libapparmor_re/apparmor_re.h"
@@ -42,8 +40,7 @@
#define SD_STR_LEN (sizeof(u16))
int __sd_serialize_profile(int option, aa_kernel_interface *kernel_interface,
Profile *prof, int cache_fd);
int __sd_serialize_profile(int option, Profile *prof);
static void print_error(int error)
{
@@ -84,14 +81,13 @@ static void print_error(int error)
}
}
int load_profile(int option, aa_kernel_interface *kernel_interface,
Profile *prof, int cache_fd)
int load_profile(int option, Profile *prof)
{
int retval = 0;
int error = 0;
PDEBUG("Serializing policy for %s.\n", prof->name);
retval = __sd_serialize_profile(option, kernel_interface, prof, cache_fd);
retval = __sd_serialize_profile(option, prof);
if (retval < 0) {
error = retval; /* yeah, we'll just report the last error */
@@ -378,11 +374,13 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
sd_write_struct(buf, "profile");
if (flattened) {
assert(profile->parent);
autofree char *name = (char *) malloc(3 + strlen(profile->name) + strlen(profile->parent->name));
char *name = (char *) malloc(3 + strlen(profile->name) +
strlen(profile->parent->name));
if (!name)
return;
sprintf(name, "%s//%s", profile->parent->name, profile->name);
sd_write_string(buf, name, NULL);
free(name);
} else {
sd_write_string(buf, profile->name, NULL);
}
@@ -470,42 +468,45 @@ void sd_serialize_top_profile(std::ostringstream &buf, Profile *profile)
sd_write_name(buf, "version");
sd_write_uint32(buf, version);
if (profile->ns) {
if (profile_ns) {
sd_write_string(buf, profile_ns, "namespace");
} else if (profile->ns) {
sd_write_string(buf, profile->ns, "namespace");
}
sd_serialize_profile(buf, profile, profile->parent ? 1 : 0);
}
int __sd_serialize_profile(int option, aa_kernel_interface *kernel_interface,
Profile *prof, int cache_fd)
int cache_fd = -1;
int __sd_serialize_profile(int option, Profile *prof)
{
autoclose int fd = -1;
int error, size, wsize;
int fd = -1;
int error = -ENOMEM, size, wsize;
std::ostringstream work_area;
char *filename = NULL;
switch (option) {
case OPTION_ADD:
if (asprintf(&filename, "%s/.load", subdomainbase) == -1)
goto exit;
if (kernel_load) fd = open(filename, O_WRONLY);
break;
case OPTION_REPLACE:
if (asprintf(&filename, "%s/.replace", subdomainbase) == -1)
goto exit;
if (kernel_load) fd = open(filename, O_WRONLY);
break;
case OPTION_REMOVE:
if (asprintf(&filename, "%s/.remove", subdomainbase) == -1)
goto exit;
if (kernel_load) fd = open(filename, O_WRONLY);
break;
case OPTION_STDOUT:
filename = strdup("stdout");
fd = dup(1);
if (fd < 0) {
error = -errno;
PERROR(_("Unable to open stdout - %s\n"),
strerror(errno));
goto exit;
}
break;
case OPTION_OFILE:
fd = dup(fileno(ofile));
if (fd < 0) {
error = -errno;
PERROR(_("Unable to open output file - %s\n"),
strerror(errno));
goto exit;
}
break;
default:
error = -EINVAL;
@@ -513,37 +514,78 @@ int __sd_serialize_profile(int option, aa_kernel_interface *kernel_interface,
break;
}
if (fd < 0 && (kernel_load || option == OPTION_OFILE || option == OPTION_STDOUT)) {
PERROR(_("Unable to open %s - %s\n"), filename,
strerror(errno));
error = -errno;
goto exit;
}
error = 0;
free(filename);
if (option == OPTION_REMOVE) {
char *name, *ns = NULL;
int len = 0;
if (profile_ns) {
len += strlen(profile_ns) + 2;
ns = profile_ns;
} else if (prof->ns) {
len += strlen(prof->ns) + 2;
ns = prof->ns;
}
if (prof->parent) {
name = (char *) malloc(strlen(prof->name) + 3 +
strlen(prof->parent->name) + len);
if (!name) {
PERROR(_("Memory Allocation Error: Unable to remove ^%s\n"), prof->name);
error = -errno;
goto exit;
}
if (ns)
sprintf(name, ":%s:%s//%s", ns,
prof->parent->name, prof->name);
else
sprintf(name, "%s//%s", prof->parent->name,
prof->name);
} else if (ns) {
name = (char *) malloc(len + strlen(prof->name) + 1);
if (!name) {
PERROR(_("Memory Allocation Error: Unable to remove %s:%s."), ns, prof->name);
error = -errno;
goto exit;
}
sprintf(name, ":%s:%s", ns, prof->name);
} else {
name = prof->name;
}
size = strlen(name) + 1;
if (kernel_load) {
if (aa_kernel_interface_remove_policy(kernel_interface,
prof->fqname().c_str()) == -1)
wsize = write(fd, name, size);
if (wsize < 0)
error = -errno;
}
if (prof->parent || ns)
free(name);
} else {
std::string tmp;
sd_serialize_top_profile(work_area, prof);
tmp = work_area.str();
size = (long) work_area.tellp();
if (kernel_load) {
if (option == OPTION_ADD &&
aa_kernel_interface_load_policy(kernel_interface,
tmp.c_str(), size) == -1) {
error = -errno;
} else if (option == OPTION_REPLACE &&
aa_kernel_interface_replace_policy(kernel_interface,
tmp.c_str(), size) == -1) {
if (kernel_load || option == OPTION_STDOUT || option == OPTION_OFILE) {
std::string tmp = work_area.str();
wsize = write(fd, tmp.c_str(), size);
if (wsize < 0) {
error = -errno;
} else if (wsize < size) {
PERROR(_("%s: Unable to write entire profile entry\n"),
progname);
error = -EIO;
}
} else if ((option == OPTION_STDOUT || option == OPTION_OFILE) &&
aa_kernel_interface_write_policy(fd, tmp.c_str(), size) == -1) {
error = -errno;
}
if (cache_fd != -1) {
std::string tmp = work_area.str();
wsize = write(cache_fd, tmp.c_str(), size);
if (wsize < 0) {
error = -errno;
@@ -555,8 +597,11 @@ int __sd_serialize_profile(int option, aa_kernel_interface *kernel_interface,
}
}
if (fd != -1)
close(fd);
if (!prof->hat_table.empty() && option != OPTION_REMOVE) {
if (load_flattened_hats(prof, option, kernel_interface, cache_fd) == 0)
if (load_flattened_hats(prof, option) == 0)
return 0;
}
@@ -565,3 +610,91 @@ exit:
return error;
}
/* bleah the kernel should just loop and do multiple load, but to support
* older systems we need to do this
*/
#define PROFILE_HEADER_SIZE
static char header_version[] = "\x04\x08\x00version";
static char *next_profile_buffer(char *buffer, int size)
{
char *b = buffer;
for (; size - sizeof(header_version); b++, size--) {
if (memcmp(b, header_version, sizeof(header_version)) == 0) {
return b;
}
}
return NULL;
}
static int write_buffer(int fd, char *buffer, int size, bool set)
{
const char *err_str = set ? "profile set" : "profile";
int wsize = write(fd, buffer, size);
if (wsize < 0) {
PERROR(_("%s: Unable to write %s\n"), progname, err_str);
return -errno;
} else if (wsize < size) {
PERROR(_("%s: Unable to write %s\n"), progname, err_str);
return -EPROTO;
}
return 0;
}
int sd_load_buffer(int option, char *buffer, int size)
{
int fd = -1;
int error, bsize;
char *filename = NULL;
/* TODO: push backup into caller */
if (!kernel_load)
return 0;
switch (option) {
case OPTION_ADD:
if (asprintf(&filename, "%s/.load", subdomainbase) == -1)
return -ENOMEM;
break;
case OPTION_REPLACE:
if (asprintf(&filename, "%s/.replace", subdomainbase) == -1)
return -ENOMEM;
break;
default:
return -EINVAL;
}
fd = open(filename, O_WRONLY);
if (fd < 0) {
PERROR(_("Unable to open %s - %s\n"), filename,
strerror(errno));
error = -errno;
goto out;
}
if (kernel_supports_setload) {
error = write_buffer(fd, buffer, size, true);
} else {
char *b, *next;
error = 0; /* in case there are no profiles */
for (b = buffer; b; b = next, size -= bsize) {
next = next_profile_buffer(b + sizeof(header_version),
size);
if (next)
bsize = next - b;
else
bsize = size;
error = write_buffer(fd, b, bsize, false);
if (error)
break;
}
}
close(fd);
out:
free(filename);
return error;
}

View File

@@ -32,6 +32,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <unordered_map>
#include <string>
@@ -41,7 +42,6 @@
#include "parser_include.h"
#include "parser_yacc.h"
#include "lib.h"
#include "policy_cache.h"
#ifdef PDEBUG
#undef PDEBUG
@@ -119,28 +119,32 @@ struct cb_struct {
const char *filename;
};
static int include_dir_cb(int dirfd unused, const char *name, struct stat *st,
static int include_dir_cb(DIR *dir unused, const char *name, struct stat *st,
void *data)
{
struct cb_struct *d = (struct cb_struct *) data;
autofree char *path = NULL;
char *path;
if (asprintf(&path, "%s/%s", d->fullpath, name) < 0)
yyerror("Out of memory");
if (is_blacklisted(name, path))
if (is_blacklisted(name, path)) {
free(path);
return 0;
}
if (S_ISREG(st->st_mode)) {
if (!(yyin = fopen(path,"r")))
yyerror(_("Could not open '%s' in '%s'"), path, d->filename);
PDEBUG("Opened include \"%s\" in \"%s\"\n", path, d->filename);
update_mru_tstamp(yyin, path);
update_mru_tstamp(yyin);
push_include_stack(path);
yypush_buffer_state(yy_create_buffer(yyin, YY_BUF_SIZE));
}
free(path);
return 0;
}
@@ -148,7 +152,7 @@ void include_filename(char *filename, int search)
{
FILE *include_file = NULL;
struct stat my_stat;
autofree char *fullpath = NULL;
char *fullpath = NULL;
if (search) {
if (preprocess_only)
@@ -170,7 +174,7 @@ void include_filename(char *filename, int search)
if (S_ISREG(my_stat.st_mode)) {
yyin = include_file;
update_mru_tstamp(include_file, fullpath);
update_mru_tstamp(include_file);
PDEBUG("Opened include \"%s\"\n", fullpath);
push_include_stack(fullpath);
yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE ));
@@ -178,11 +182,14 @@ void include_filename(char *filename, int search)
struct cb_struct data = { fullpath, filename };
fclose(include_file);
include_file = NULL;
if (dirat_for_each(AT_FDCWD, fullpath, &data, include_dir_cb)) {
if (dirat_for_each(NULL, fullpath, &data, include_dir_cb)) {
yyerror(_("Could not process include directory"
" '%s' in '%s'"), fullpath, filename);;
}
}
if (fullpath)
free(fullpath);
}
%}
@@ -225,8 +232,8 @@ SET_VAR_PREFIX @
SET_VARIABLE {SET_VAR_PREFIX}(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
BOOL_VARIABLE $(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
LABEL (\/|{SET_VARIABLE}{POST_VAR_ID}|{COLON}){ID}*
QUOTED_LABEL \"(\/|{SET_VAR_PREFIX}|{COLON})([^\0"]|\\\")*\"
PATHNAME (\/|{SET_VARIABLE}{POST_VAR_ID}){ID}*
QPATHNAME \"(\/|{SET_VAR_PREFIX})([^\0"]|\\\")*\"
OPEN_PAREN \(
CLOSE_PAREN \)
@@ -273,8 +280,9 @@ LT_EQUAL <=
<INCLUDE>{
(\<([^\> \t\n]+)\>|\"([^\" \t\n]+)\") { /* <filename> */
autofree char *filename = strndup(yytext, yyleng - 1);
char *filename = strndup(yytext, yyleng - 1);
include_filename(filename + 1, *filename == '<');
free(filename);
POP_NODUMP();
}
@@ -442,12 +450,12 @@ LT_EQUAL <=
({IDS}|{QUOTED_ID}) {
yylval.id = processid(yytext, yyleng);
RETURN_TOKEN(TOK_ID);
POP_AND_RETURN(TOK_ID);
}
}
<RLIMIT_MODE>{
-?{NUMBER} {
-?{NUMBER}[[:alpha:]]* {
yylval.var_val = strdup(yytext);
RETURN_TOKEN(TOK_VALUE);
}
@@ -510,7 +518,7 @@ LT_EQUAL <=
}
<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
({IDS_NOEQ}|{LABEL}|{QUOTED_ID}) {
({IDS_NOEQ}|{PATHNAME}|{QUOTED_ID}) {
yylval.id = processid(yytext, yyleng);
RETURN_TOKEN(TOK_ID);
}
@@ -519,14 +527,7 @@ LT_EQUAL <=
#include/.*\r?\n {
/* Don't use PUSH() macro here as we don't want #include echoed out.
* It needs to be handled specially
*/
yy_push_state(INCLUDE);
}
include/{WS} {
/* Don't use PUSH() macro here as we don't want #include echoed out.
* It needs to be handled specially
*/
*/
yy_push_state(INCLUDE);
}
@@ -557,7 +558,7 @@ include/{WS} {
{CLOSE_BRACE} { RETURN_TOKEN(TOK_CLOSE); }
({LABEL}|{QUOTED_LABEL}) {
({PATHNAME}|{QPATHNAME}) {
yylval.id = processid(yytext, yyleng);
RETURN_TOKEN(TOK_ID);
}
@@ -618,7 +619,7 @@ include/{WS} {
PUSH_AND_RETURN(state, token);
}
<INITIAL,NETWORK_MODE,RLIMIT_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
<INITIAL,NETWORK_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
{END_OF_RULE} {
if (YY_START != INITIAL)
POP_NODUMP();

View File

@@ -28,6 +28,7 @@
#include <getopt.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
/* enable the following line to get voluminous debug info */
/* #define DEBUG */
@@ -39,17 +40,18 @@
#include <sys/apparmor.h>
#include "lib.h"
#include "features.h"
#include "parser.h"
#include "parser_version.h"
#include "parser_include.h"
#include "common_optarg.h"
#include "policy_cache.h"
#include "libapparmor_re/apparmor_re.h"
#define MODULE_NAME "apparmor"
#define OLD_MODULE_NAME "subdomain"
#define PROC_MODULES "/proc/modules"
#define DEFAULT_APPARMORFS "/sys/kernel/security/" MODULE_NAME
#define MATCH_FILE "/sys/kernel/security/" MODULE_NAME "/matching"
#define FEATURES_FILE "/sys/kernel/security/" MODULE_NAME "/features"
#define MOUNTED_FS "/proc/mounts"
#define AADFA "pattern=aadfa"
@@ -69,19 +71,20 @@ int skip_read_cache = 0;
int write_cache = 0;
int cond_clear_cache = 1; /* only applies if write is set */
int force_clear_cache = 0; /* force clearing regargless of state */
int create_cache_dir = 0; /* DEPRECATED in favor of write_cache */
int create_cache_dir = 0; /* create the cache dir if missing? */
int preprocess_only = 0;
int skip_mode_force = 0;
int abort_on_error = 0; /* stop processing profiles if error */
int skip_bad_cache_rebuild = 0;
int mru_skip_cache = 1;
int debug_cache = 0;
struct timespec cache_tstamp, mru_policy_tstamp;
struct timespec mru_tstamp;
static char *apparmorfs = NULL;
static char *cacheloc = NULL;
#define FEATURES_STRING_SIZE 8192
char *features_string = NULL;
char *cacheloc = NULL;
static aa_features *features = NULL;
/* per-profile settings */
static int load_features(const char *name);
/* Make sure to update BOTH the short and long_options */
static const char *short_options = "adf:h::rRVvI:b:BCD:NSm:M:qQn:XKTWkL:O:po:";
@@ -125,7 +128,6 @@ struct option long_options[] = {
{"abort-on-error", 0, 0, 132}, /* no short option */
{"skip-bad-cache-rebuild", 0, 0, 133}, /* no short option */
{"warn", 1, 0, 134}, /* no short option */
{"debug-cache", 0, 0, 135}, /* no short option */
{NULL, 0, 0, 0},
};
@@ -164,7 +166,7 @@ static void display_usage(const char *command)
"-W, --write-cache Save cached profile (force with -T)\n"
" --skip-bad-cache Don't clear cache if out of sync\n"
" --purge-cache Clear cache regardless of its state\n"
" --debug-cache Debug cache file checks\n"
" --create-cache-dir Create the cache dir if missing\n"
"-L, --cache-loc n Set the location of the profile cache\n"
"-q, --quiet Don't emit warnings\n"
"-v, --verbose Show profile names as they load\n"
@@ -361,7 +363,7 @@ static int process_arg(int c, char *optarg)
}
break;
case 'f':
apparmorfs = strndup(optarg, PATH_MAX);
subdomainbase = strndup(optarg, PATH_MAX);
break;
case 'D':
skip_read_cache = 1;
@@ -389,17 +391,11 @@ static int process_arg(int c, char *optarg)
}
break;
case 'm':
if (aa_features_new_from_string(&features,
optarg, strlen(optarg))) {
fprintf(stderr,
"Failed to parse features string: %m\n");
exit(1);
}
features_string = strdup(optarg);
break;
case 'M':
if (aa_features_new(&features, AT_FDCWD, optarg)) {
fprintf(stderr,
"Failed to load features from '%s': %m\n",
if (load_features(optarg) == -1) {
fprintf(stderr, "Failed to load features from '%s'\n",
optarg);
exit(1);
}
@@ -467,9 +463,6 @@ static int process_arg(int c, char *optarg)
exit(1);
}
break;
case 135:
debug_cache = 1;
break;
default:
display_usage(progname);
exit(1);
@@ -504,7 +497,7 @@ static int process_args(int argc, char *argv[])
static int process_config_file(const char *name)
{
char *optarg;
autofclose FILE *f = NULL;
FILE *f;
int c, o;
f = fopen(name, "r");
@@ -513,9 +506,27 @@ static int process_config_file(const char *name)
while ((c = getopt_long_file(f, long_options, &optarg, &o)) != -1)
process_arg(c, optarg);
fclose(f);
return 1;
}
int find_subdomainfs_mountpoint(void)
{
if (aa_find_mountpoint(&subdomainbase) == -1) {
struct stat buf;
if (stat(DEFAULT_APPARMORFS, &buf) == -1) {
PERROR(_("Warning: unable to find a suitable fs in %s, is it "
"mounted?\nUse --subdomainfs to override.\n"),
MOUNTED_FS);
} else {
subdomainbase = strdup(DEFAULT_APPARMORFS);
}
}
return (subdomainbase == NULL);
}
int have_enough_privilege(void)
{
uid_t uid, euid;
@@ -539,118 +550,275 @@ int have_enough_privilege(void)
return 0;
}
char *snprintf_buffer(char *buf, char *pos, ssize_t size, const char *fmt, ...)
{
va_list args;
int i, remaining = size - (pos - buf);
va_start(args, fmt);
i = vsnprintf(pos, remaining, fmt, args);
va_end(args);
if (i >= size) {
PERROR(_("Feature buffer full."));
exit(1);
}
return pos + i;
}
struct features_struct {
char **buffer;
int size;
char *pos;
};
static int features_dir_cb(DIR *dir, const char *name, struct stat *st,
void *data)
{
struct features_struct *fst = (struct features_struct *) data;
/* skip dot files and files with no name */
if (*name == '.' || !strlen(name))
return 0;
fst->pos = snprintf_buffer(*fst->buffer, fst->pos, fst->size, "%s {", name);
if (S_ISREG(st->st_mode)) {
int len, file;
int remaining = fst->size - (fst->pos - *fst->buffer);
file = openat(dirfd(dir), name, O_RDONLY);
if (file == -1) {
PDEBUG("Could not open '%s'", name);
return -1;
}
PDEBUG("Opened features \"%s\"\n", name);
if (st->st_size > remaining) {
PDEBUG("Feature buffer full.");
return -1;
}
do {
len = read(file, fst->pos, remaining);
if (len > 0) {
remaining -= len;
fst->pos += len;
*fst->pos = 0;
}
} while (len > 0);
if (len < 0) {
PDEBUG("Error reading feature file '%s'\n", name);
return -1;
}
close(file);
} else if (S_ISDIR(st->st_mode)) {
if (dirat_for_each(dir, name, fst, features_dir_cb))
return -1;
}
fst->pos = snprintf_buffer(*fst->buffer, fst->pos, fst->size, "}\n");
return 0;
}
static char *handle_features_dir(const char *filename, char **buffer, int size,
char *pos)
{
struct features_struct fst = { buffer, size, pos };
if (dirat_for_each(NULL, filename, &fst, features_dir_cb)) {
PDEBUG("Failed evaluating %s\n", filename);
exit(1);
}
return fst.pos;
}
static char *load_features_file(const char *name) {
char *buffer;
FILE *f = NULL;
size_t size;
f = fopen(name, "r");
if (!f)
return NULL;
buffer = (char *) malloc(FEATURES_STRING_SIZE);
if (!buffer)
goto fail;
size = fread(buffer, 1, FEATURES_STRING_SIZE - 1, f);
if (!size || ferror(f))
goto fail;
buffer[size] = 0;
fclose(f);
return buffer;
fail:
int save = errno;
free(buffer);
if (f)
fclose(f);
errno = save;
return NULL;
}
static int load_features(const char *name)
{
struct stat stat_file;
if (stat(name, &stat_file) == -1)
return -1;
if (S_ISDIR(stat_file.st_mode)) {
/* if we have a features directory default to */
features_string = (char *) malloc(FEATURES_STRING_SIZE);
handle_features_dir(name, &features_string, FEATURES_STRING_SIZE, features_string);
} else {
features_string = load_features_file(name);
if (!features_string)
return -1;
}
return 0;
}
static void set_features_by_match_file(void)
{
autofclose FILE *ms = fopen(MATCH_FILE, "r");
FILE *ms = fopen(MATCH_FILE, "r");
if (ms) {
autofree char *match_string = (char *) malloc(1000);
char *match_string = (char *) malloc(1000);
if (!match_string)
goto no_match;
if (!fgets(match_string, 1000, ms))
if (!fgets(match_string, 1000, ms)) {
free(match_string);
goto no_match;
}
if (strstr(match_string, " perms=c"))
perms_create = 1;
free(match_string);
kernel_supports_network = 1;
return;
goto out;
}
no_match:
perms_create = 1;
out:
if (ms)
fclose(ms);
}
static void set_supported_features(void)
{
static void set_supported_features(void) {
/* has process_args() already assigned a match string? */
if (!features && aa_features_new_from_kernel(&features) == -1) {
set_features_by_match_file();
return;
if (!features_string) {
if (load_features(FEATURES_FILE) == -1) {
set_features_by_match_file();
return;
}
}
perms_create = 1;
kernel_supports_policydb = aa_features_supports(features, "file");
kernel_supports_network = aa_features_supports(features, "network");
kernel_supports_unix = aa_features_supports(features,
"network/af_unix");
kernel_supports_mount = aa_features_supports(features, "mount");
kernel_supports_dbus = aa_features_supports(features, "dbus");
kernel_supports_signal = aa_features_supports(features, "signal");
kernel_supports_ptrace = aa_features_supports(features, "ptrace");
kernel_supports_setload = aa_features_supports(features,
"policy/set_load");
kernel_supports_diff_encode = aa_features_supports(features,
"policy/diff_encode");
if (aa_features_supports(features, "policy/versions/v7"))
kernel_abi_version = 7;
else if (aa_features_supports(features, "policy/versions/v6"))
/* TODO: make this real parsing and config setting */
if (strstr(features_string, "file {")) /* pre policydb is file= */
kernel_supports_policydb = 1;
if (strstr(features_string, "v6"))
kernel_abi_version = 6;
if (!kernel_supports_diff_encode)
if (strstr(features_string, "v7"))
kernel_abi_version = 7;
if (strstr(features_string, "set_load"))
kernel_supports_setload = 1;
if (strstr(features_string, "network"))
kernel_supports_network = 1;
if (strstr(features_string, "af_unix"))
kernel_supports_unix = 1;
if (strstr(features_string, "mount"))
kernel_supports_mount = 1;
if (strstr(features_string, "dbus"))
kernel_supports_dbus = 1;
if (strstr(features_string, "signal"))
kernel_supports_signal = 1;
if (strstr(features_string, "ptrace {"))
kernel_supports_ptrace = 1;
if (strstr(features_string, "diff_encode"))
kernel_supports_diff_encode = 1;
else if (dfaflags & DFA_CONTROL_DIFF_ENCODE)
/* clear diff_encode because it is not supported */
dfaflags &= ~DFA_CONTROL_DIFF_ENCODE;
}
int process_binary(int option, aa_kernel_interface *kernel_interface,
const char *profilename)
int process_binary(int option, const char *profilename)
{
const char *printed_name;
int retval;
char *buffer = NULL;
int retval = 0, size = 0, asize = 0, rsize;
int chunksize = 1 << 14;
int fd;
printed_name = profilename ? profilename : "stdin";
if (kernel_load) {
if (option == OPTION_ADD) {
retval = profilename ?
aa_kernel_interface_load_policy_from_file(kernel_interface, AT_FDCWD, profilename) :
aa_kernel_interface_load_policy_from_fd(kernel_interface, 0);
if (retval == -1) {
retval = errno;
PERROR(_("Error: Could not load profile %s: %s\n"),
printed_name, strerror(retval));
return retval;
}
} else if (option == OPTION_REPLACE) {
retval = profilename ?
aa_kernel_interface_replace_policy_from_file(kernel_interface, AT_FDCWD, profilename) :
aa_kernel_interface_replace_policy_from_fd(kernel_interface, 0);
if (retval == -1) {
retval = errno;
PERROR(_("Error: Could not replace profile %s: %s\n"),
printed_name, strerror(retval));
return retval;
}
} else {
PERROR(_("Error: Invalid load option specified: %d\n"),
option);
return EINVAL;
if (profilename) {
fd = open(profilename, O_RDONLY);
if (fd == -1) {
retval = errno;
PERROR(_("Error: Could not read binary profile or cache file %s: %s.\n"),
profilename, strerror(errno));
return retval;
}
} else {
fd = dup(0);
}
do {
if (asize - size == 0) {
buffer = (char *) realloc(buffer, chunksize);
asize = chunksize;
chunksize <<= 1;
if (!buffer) {
PERROR(_("Memory allocation error."));
return ENOMEM;
}
}
rsize = read(fd, buffer + size, asize - size);
if (rsize)
size += rsize;
} while (rsize > 0);
close(fd);
if (rsize == 0)
retval = sd_load_buffer(option, buffer, size);
else
retval = rsize;
free(buffer);
if (conf_verbose) {
switch (option) {
case OPTION_ADD:
printf(_("Cached load succeeded for \"%s\".\n"),
printed_name);
profilename ? profilename : "stdin");
break;
case OPTION_REPLACE:
printf(_("Cached reload succeeded for \"%s\".\n"),
printed_name);
profilename ? profilename : "stdin");
break;
default:
break;
}
}
return 0;
return retval;
}
void reset_parser(const char *filename)
{
memset(&mru_policy_tstamp, 0, sizeof(mru_policy_tstamp));
memset(&cache_tstamp, 0, sizeof(cache_tstamp));
mru_skip_cache = 1;
memset(&mru_tstamp, 0, sizeof(mru_tstamp));
free_aliases();
free_symtabs();
free_policies();
reset_regex();
reset_include_stack(filename);
}
@@ -659,7 +827,7 @@ int test_for_dir_mode(const char *basename, const char *linkdir)
int rc = 0;
if (!skip_mode_force) {
autofree char *target = NULL;
char *target = NULL;
if (asprintf(&target, "%s/%s/%s", basedir, linkdir, basename) < 0) {
perror("asprintf");
exit(1);
@@ -667,18 +835,64 @@ int test_for_dir_mode(const char *basename, const char *linkdir)
if (access(target, R_OK) == 0)
rc = 1;
free(target);
}
return rc;
}
int process_profile(int option, aa_kernel_interface *kernel_interface,
const char *profilename, const char *cachedir)
#define le16_to_cpu(x) ((uint16_t)(le16toh (*(uint16_t *) x)))
const char header_string[] = "\004\010\000version\000\002";
#define HEADER_STRING_SIZE 12
static bool valid_cached_file_version(const char *cachename)
{
char buffer[16];
FILE *f;
if (!(f = fopen(cachename, "r"))) {
PERROR(_("Error: Could not read cache file '%s', skipping...\n"), cachename);
return false;
}
size_t res = fread(buffer, 1, 16, f);
fclose(f);
if (res < 16)
return false;
/* 12 byte header that is always the same and then 4 byte version # */
if (memcmp(buffer, header_string, HEADER_STRING_SIZE) != 0)
return false;
uint32_t version = cpu_to_le32(ENCODE_VERSION(force_complain,
policy_version,
parser_abi_version,
kernel_abi_version));
if (memcmp(buffer + 12, &version, 4) != 0)
return false;
return true;
}
/* returns true if time is more recent than mru_tstamp */
#define mru_t_cmp(a) \
(((a).tv_sec == (mru_tstamp).tv_sec) ? \
(a).tv_nsec > (mru_tstamp).tv_nsec : (a).tv_sec > (mru_tstamp).tv_sec)
void update_mru_tstamp(FILE *file)
{
struct stat stat_file;
if (fstat(fileno(file), &stat_file))
return;
if (mru_t_cmp(stat_file.st_mtim))
mru_tstamp = stat_file.st_mtim;
}
int process_profile(int option, const char *profilename)
{
struct stat stat_bin;
int retval = 0;
autofree const char *cachename = NULL;
autofree const char *cachetmpname = NULL;
autoclose int cachetmp = -1;
char * cachename = NULL;
char * cachetemp = NULL;
const char *basename = NULL;
/* per-profile states */
@@ -690,12 +904,11 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
profilename, strerror(errno));
return errno;
}
} else {
}
else {
pwarn("%s: cannot use or update cache, disable, or force-complain via stdin\n", progname);
}
reset_parser(profilename);
if (profilename && option != OPTION_REMOVE) {
/* make decisions about disabled or complain-mode profiles */
basename = strrchr(profilename, '/');
@@ -715,17 +928,17 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
force_complain = 1;
}
/* setup cachename and tstamp */
if (!force_complain && !skip_cache) {
cachename = cache_filename(cachedir, basename);
valid_read_cache(cachename);
}
/* TODO: add primary cache check.
* If .file for cached binary exists get the list of profile
* names and check their time stamps.
*/
/* TODO: primary cache miss/hit messages */
}
reset_parser(profilename);
if (yyin) {
yyrestart(yyin);
update_mru_tstamp(yyin, profilename ? profilename : "stdin");
update_mru_tstamp(yyin);
}
retval = yyparse();
@@ -737,21 +950,44 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
* TODO: Add support for caching profiles in an alternate namespace
* TODO: Add support for embedded namespace defines if they aren't
* removed from the language.
* TODO: test profile->ns NOT profile_ns (must be after parse)
*/
if (profile_ns)
skip_cache = 1;
if (cachename) {
/* Load a binary cache if it exists and is newest */
if (cache_hit(cachename)) {
retval = process_binary(option, kernel_interface,
cachename);
if (!retval || skip_bad_cache_rebuild)
return retval;
/* Do secondary test to see if cached binary profile is good,
* instead of checking against a presupplied list of files
* use the timestamps from the files that were parsed.
* Parsing the profile is slower that doing primary cache check
* its still faster than doing full compilation
*/
if ((profilename && option != OPTION_REMOVE) && !force_complain &&
!skip_cache) {
if (asprintf(&cachename, "%s/%s", cacheloc, basename)<0) {
PERROR(_("Memory allocation error."));
return ENOMEM;
}
/* Load a binary cache if it exists and is newest */
if (!skip_read_cache &&
stat(cachename, &stat_bin) == 0 &&
stat_bin.st_size > 0 && (mru_t_cmp(stat_bin.st_mtim)) &&
valid_cached_file_version(cachename)) {
if (show_cache)
PERROR("Cache hit: %s\n", cachename);
retval = process_binary(option, cachename);
if (!retval || skip_bad_cache_rebuild)
goto out;
}
if (write_cache) {
/* Otherwise, set up to save a cached copy */
if (asprintf(&cachetemp, "%s-XXXXXX", cachename)<0) {
perror("asprintf");
return ENOMEM;
}
if ( (cache_fd = mkstemp(cachetemp)) < 0) {
perror("mkstemp");
return ENOMEM;
}
}
cachetmp = setup_cache_tmp(&cachetmpname, cachename);
}
if (show_cache)
@@ -787,80 +1023,186 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
goto out;
}
/* cache file generated by load_policy */
retval = load_policy(option, kernel_interface, cachetmp);
if (retval == 0 && write_cache) {
if (cachetmp == -1) {
unlink(cachetmpname);
PERROR("Warning failed to create cache: %s\n",
basename);
} else {
install_cache(cachetmpname, cachename);
}
}
out:
retval = load_policy(option);
out:
if (cachetemp) {
/* Only install the generate cache file if it parsed correctly
and did not have write/close errors */
int useable_cache = (cache_fd != -1 && retval == 0);
if (cache_fd != -1) {
if (close(cache_fd))
useable_cache = 0;
cache_fd = -1;
}
if (useable_cache) {
if (rename(cachetemp, cachename) < 0) {
pwarn("Warning failed to write cache: %s\n", cachename);
unlink(cachetemp);
}
else if (show_cache)
PERROR("Wrote cache: %s\n", cachename);
}
else {
unlink(cachetemp);
PERROR("Warning failed to create cache: %s\n", basename);
}
free(cachetemp);
}
if (cachename)
free(cachename);
return retval;
}
struct dir_cb_data {
aa_kernel_interface *kernel_interface;
const char *dirname; /* name of the parent dir */
const char *cachedir; /* path to the cache sub directory */
};
/* data - pointer to a dir_cb_data */
static int profile_dir_cb(int dirfd unused, const char *name, struct stat *st,
/* data - name of parent dir */
static int profile_dir_cb(DIR *dir unused, const char *name, struct stat *st,
void *data)
{
int rc = 0;
if (!S_ISDIR(st->st_mode) && !is_blacklisted(name, NULL)) {
struct dir_cb_data *cb_data = (struct dir_cb_data *)data;
autofree char *path = NULL;
if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0)
const char *dirname = (const char *)data;
char *path;
if (asprintf(&path, "%s/%s", dirname, name) < 0)
PERROR(_("Out of memory"));
rc = process_profile(option, cb_data->kernel_interface, path,
cb_data->cachedir);
rc = process_profile(option, path);
free(path);
}
return rc;
}
/* data - pointer to a dir_cb_data */
static int binary_dir_cb(int dirfd unused, const char *name, struct stat *st,
/* data - name of parent dir */
static int binary_dir_cb(DIR *dir unused, const char *name, struct stat *st,
void *data)
{
int rc = 0;
if (!S_ISDIR(st->st_mode) && !is_blacklisted(name, NULL)) {
struct dir_cb_data *cb_data = (struct dir_cb_data *)data;
autofree char *path = NULL;
if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0)
const char *dirname = (const char *)data;
char *path;
if (asprintf(&path, "%s/%s", dirname, name) < 0)
PERROR(_("Out of memory"));
rc = process_binary(option, cb_data->kernel_interface, path);
rc = process_binary(option, path);
free(path);
}
return rc;
}
static int clear_cache_cb(DIR *dir, const char *path, struct stat *st,
void *data unused)
{
/* remove regular files */
if (S_ISREG(st->st_mode))
return unlinkat(dirfd(dir), path, 0);
/* do nothing with other file types */
return 0;
}
static int clear_cache_files(const char *path)
{
return dirat_for_each(NULL, path, NULL, clear_cache_cb);
}
static int create_cache(const char *cachedir, const char *path,
const char *features)
{
struct stat stat_file;
FILE * f = NULL;
if (clear_cache_files(cacheloc) != 0)
goto error;
create_file:
f = fopen(path, "w");
if (f) {
if (fwrite(features, strlen(features), 1, f) != 1 )
goto error;
fclose(f);
return 0;
}
error:
/* does the dir exist? */
if (stat(cachedir, &stat_file) == -1 && create_cache_dir) {
if (mkdir(cachedir, 0700) == 0)
goto create_file;
if (show_cache)
PERROR(_("Can't create cache directory: %s\n"), cachedir);
} else if (!S_ISDIR(stat_file.st_mode)) {
if (show_cache)
PERROR(_("File in cache directory location: %s\n"), cachedir);
} else {
if (show_cache)
PERROR(_("Can't update cache directory: %s\n"), cachedir);
}
if (show_cache)
PERROR("Cache write disabled: cannot create %s\n", path);
write_cache = 0;
return -1;
}
static void setup_flags(void)
{
char *cache_features_path = NULL;
char *cache_flags = NULL;
/* Get the match string to determine type of regex support needed */
set_supported_features();
/* Gracefully handle AppArmor kernel without compatibility patch */
if (!features) {
PERROR("Cache read/write disabled: interface file missing. "
"(Kernel needs AppArmor 2.4 compatibility patch.)\n");
if (!features_string) {
PERROR("Cache read/write disabled: %s interface file missing. "
"(Kernel needs AppArmor 2.4 compatibility patch.)\n",
FEATURES_FILE);
write_cache = 0;
skip_read_cache = 1;
return;
}
/*
* Deal with cache directory versioning:
* - If cache/.features is missing, create it if --write-cache.
* - If cache/.features exists, and does not match features_string,
* force cache reading/writing off.
*/
if (asprintf(&cache_features_path, "%s/.features", cacheloc) == -1) {
PERROR(_("Memory allocation error."));
exit(1);
}
cache_flags = load_features_file(cache_features_path);
if (cache_flags) {
if (strcmp(features_string, cache_flags) != 0) {
if (write_cache && cond_clear_cache) {
if (create_cache(cacheloc, cache_features_path,
features_string))
skip_read_cache = 1;
} else {
if (show_cache)
PERROR("Cache read/write disabled: %s does not match %s\n", FEATURES_FILE, cache_features_path);
write_cache = 0;
skip_read_cache = 1;
}
}
free(cache_flags);
cache_flags = NULL;
} else if (write_cache) {
create_cache(cacheloc, cache_features_path, features_string);
}
free(cache_features_path);
}
int main(int argc, char *argv[])
{
aa_kernel_interface *kernel_interface = NULL;
aa_policy_cache *policy_cache = NULL;
int retval, last_error;
int i;
int optind;
@@ -883,62 +1225,27 @@ int main(int argc, char *argv[])
return retval;
}
/* create the cacheloc once and use it everywhere */
if (!cacheloc) {
if (asprintf(&cacheloc, "%s/cache", basedir) == -1) {
PERROR(_("Memory allocation error."));
exit(1);
}
}
if (force_clear_cache)
exit(clear_cache_files(cacheloc));
/* Check to make sure there is an interface to load policy */
if (!(UNPRIVILEGED_OPS) && (subdomainbase == NULL) &&
(retval = find_subdomainfs_mountpoint())) {
return retval;
}
if (!binary_input) parse_default_paths();
setup_flags();
if (!(UNPRIVILEGED_OPS) &&
aa_kernel_interface_new(&kernel_interface, features, apparmorfs) == -1) {
PERROR(_("Warning: unable to find a suitable fs in %s, is it "
"mounted?\nUse --subdomainfs to override.\n"),
MOUNTED_FS);
return 1;
}
if ((!skip_cache && (write_cache || !skip_read_cache)) ||
force_clear_cache) {
uint16_t max_caches = write_cache && cond_clear_cache ? 1 : 0;
if (!cacheloc && asprintf(&cacheloc, "%s/cache", basedir) == -1) {
PERROR(_("Memory allocation error."));
return 1;
}
if (force_clear_cache) {
if (aa_policy_cache_remove(AT_FDCWD, cacheloc)) {
PERROR(_("Failed to clear cache files (%s): %s\n"),
cacheloc, strerror(errno));
return 1;
}
return 0;
}
if (create_cache_dir)
pwarn(_("The --create-cache-dir option is deprecated. Please use --write-cache.\n"));
retval = aa_policy_cache_new(&policy_cache, features,
AT_FDCWD, cacheloc, max_caches);
if (retval) {
if (errno != ENOENT && errno != EEXIST) {
PERROR(_("Failed setting up policy cache (%s): %s\n"),
cacheloc, strerror(errno));
return 1;
}
if (show_cache) {
if (max_caches > 0)
PERROR("Cache write disabled: Cannot create cache '%s': %m\n",
cacheloc);
else
PERROR("Cache read/write disabled: Policy cache is invalid\n");
}
write_cache = 0;
skip_read_cache = 1;
}
}
retval = last_error = 0;
for (i = optind; i <= argc; i++) {
struct stat stat_file;
@@ -960,26 +1267,17 @@ int main(int argc, char *argv[])
}
if (profilename && S_ISDIR(stat_file.st_mode)) {
int (*cb)(int dirfd, const char *name, struct stat *st,
int (*cb)(DIR *dir, const char *name, struct stat *st,
void *data);
struct dir_cb_data cb_data;
memset(&cb_data, 0, sizeof(struct dir_cb_data));
cb_data.dirname = profilename;
cb_data.cachedir = cacheloc;
cb_data.kernel_interface = kernel_interface;
cb = binary_input ? binary_dir_cb : profile_dir_cb;
if ((retval = dirat_for_each(AT_FDCWD, profilename,
&cb_data, cb))) {
if ((retval = dirat_for_each(NULL, profilename, profilename, cb))) {
PDEBUG("Failed loading profiles from %s\n",
profilename);
}
} else if (binary_input) {
retval = process_binary(option, kernel_interface,
profilename);
retval = process_binary(option, profilename);
} else {
retval = process_profile(option, kernel_interface,
profilename, cacheloc);
retval = process_profile(option, profilename);
}
if (profilename) free(profilename);
@@ -994,7 +1292,6 @@ int main(int argc, char *argv[])
if (ofile)
fclose(ofile);
aa_policy_cache_unref(policy_cache);
return last_error;
}

View File

@@ -32,7 +32,6 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/apparmor.h>
#include <sys/apparmor_private.h>
#include "lib.h"
#include "parser.h"
@@ -51,14 +50,53 @@
#endif
#define NPDEBUG(fmt, args...) /* Do nothing */
struct ignored_suffix_t {
const char * text;
int len;
int silent;
};
static struct ignored_suffix_t ignored_suffixes[] = {
/* Debian packging files, which are in flux during install
should be silently ignored. */
{ ".dpkg-new", 9, 1 },
{ ".dpkg-old", 9, 1 },
{ ".dpkg-dist", 10, 1 },
{ ".dpkg-bak", 9, 1 },
/* RPM packaging files have traditionally not been silently
ignored */
{ ".rpmnew", 7, 0 },
{ ".rpmsave", 8, 0 },
/* patch file backups/conflicts */
{ ".orig", 5, 0 },
{ ".rej", 4, 0 },
/* Backup files should be mentioned */
{ "~", 1, 0 },
{ NULL, 0, 0 }
};
int is_blacklisted(const char *name, const char *path)
{
int retval = _aa_is_blacklisted(name);
int name_len;
struct ignored_suffix_t *suffix;
if (retval == -1)
PERROR("Ignoring: '%s'\n", path ? path : name);
/* skip dot files and files with no name */
if (*name == '.' || !strlen(name))
return 1;
return !retval ? 0 : 1;
name_len = strlen(name);
/* skip blacklisted suffixes */
for (suffix = ignored_suffixes; suffix->text; suffix++) {
char *found;
if ( (found = strstr((char *) name, suffix->text)) &&
found - name + suffix->len == name_len ) {
if (!suffix->silent)
PERROR("Ignoring: '%s'\n", path ? path : name);
return 1;
}
}
return 0;
}
struct keyword_table {
@@ -205,10 +243,7 @@ char *processunquoted(const char *string, int len)
* pass it through to be handled by the backend
* pcre conversion
*/
if (c == 0) {
strncpy(s, string, pos - string);
s += pos - string;
} else if (strchr("*?[]{}^,\\", c) != NULL) {
if (strchr("*?[]{}^,\\", c) != NULL) {
*s++ = '\\';
*s++ = c;
} else
@@ -332,7 +367,7 @@ reeval:
case COD_READ_CHAR:
if (read_implies_exec) {
PDEBUG("Parsing mode: found %s READ imply X\n", mode_desc);
mode |= AA_MAY_READ | AA_OLD_EXEC_MMAP;
mode |= AA_MAY_READ | AA_EXEC_MMAP;
} else {
PDEBUG("Parsing mode: found %s READ\n", mode_desc);
mode |= AA_MAY_READ;
@@ -355,12 +390,12 @@ reeval:
case COD_LINK_CHAR:
PDEBUG("Parsing mode: found %s LINK\n", mode_desc);
mode |= AA_OLD_MAY_LINK;
mode |= AA_MAY_LINK;
break;
case COD_LOCK_CHAR:
PDEBUG("Parsing mode: found %s LOCK\n", mode_desc);
mode |= AA_OLD_MAY_LOCK;
mode |= AA_MAY_LOCK;
break;
case COD_INHERIT_CHAR:
@@ -439,7 +474,7 @@ reeval:
case COD_MMAP_CHAR:
PDEBUG("Parsing mode: found %s MMAP\n", mode_desc);
mode |= AA_OLD_EXEC_MMAP;
mode |= AA_EXEC_MMAP;
break;
case COD_EXEC_CHAR:
@@ -569,65 +604,6 @@ int parse_X_mode(const char *X, int valid, const char *str_mode, int *mode, int
return 1;
}
void parse_label(char **ns, char **name, const char *label)
{
const char *name_start = NULL;
char *_ns = NULL;
char *_name = NULL;
if (label[0] != ':') {
/* There is no namespace specified in the label */
name_start = label;
} else {
/* A leading ':' indicates that a namespace is specified */
const char *ns_start = label + 1;
const char *ns_end = strstr(ns_start, ":");
if (!ns_end)
yyerror(_("Namespace not terminated: %s\n"), label);
else if (ns_end - ns_start == 0)
yyerror(_("Empty namespace: %s\n"), label);
/**
* Handle either of the two namespace formats:
* 1) :ns:name
* 2) :ns://name
*/
name_start = ns_end + 1;
if (!strncmp(name_start, "//", 2))
name_start += 2;
_ns = strndup(ns_start, ns_end - ns_start);
if (!_ns)
yyerror(_("Memory allocation error."));
}
if (!strlen(name_start))
yyerror(_("Empty named transition profile name: %s\n"), label);
_name = strdup(name_start);
if (!_name) {
free(_ns);
yyerror(_("Memory allocation error."));
}
*ns = _ns;
*name = _name;
}
void parse_named_transition_target(struct named_transition *nt,
const char *target)
{
memset(nt, 0, sizeof(*nt));
if (!target) {
/* Return with nt->present set to 0 (thanks to the memset) */
return;
}
parse_label(&nt->ns, &nt->name, target);
nt->present = 1;
}
struct cod_entry *new_entry(char *ns, char *id, int mode, char *link_id)
{
struct cod_entry *entry = NULL;
@@ -783,7 +759,7 @@ static const char *capnames[] = {
"audit_write",
"audit_control",
"setfcap",
"mac_override",
"mac_override"
"syslog",
};
@@ -926,54 +902,6 @@ void print_cond_entry(struct cond_entry *ent)
}
}
struct time_units {
const char *str;
long long value;
};
static struct time_units time_units[] = {
{ "us", 1LL },
{ "microsecond", 1LL },
{ "microseconds", 1LL },
{ "ms", 1000LL },
{ "millisecond", 1000LL },
{ "milliseconds", 1000LL },
{ "s", 1000LL * 1000LL },
{ "sec", SECONDS_P_MS },
{ "second", SECONDS_P_MS },
{ "seconds", SECONDS_P_MS },
{ "min" , 60LL * SECONDS_P_MS },
{ "minute", 60LL * SECONDS_P_MS },
{ "minutes", 60LL * SECONDS_P_MS },
{ "h", 60LL * 60LL * SECONDS_P_MS },
{ "hour", 60LL * 60LL * SECONDS_P_MS },
{ "hours", 60LL * 60LL * SECONDS_P_MS },
{ "d", 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "day", 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "days", 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "week", 7LL * 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "weeks", 7LL * 24LL * 60LL * 60LL * SECONDS_P_MS },
{ NULL, 0 }
};
long long convert_time_units(long long value, long long base, const char *units)
{
struct time_units *ent;
if (!units)
/* default to base if no units */
return value;
for (ent = time_units; ent->str; ent++) {
if (strcmp(ent->str, units) == 0) {
if (value * ent->value < base)
return -1LL;
return value * ent->value / base;
}
}
return -2LL;
}
#ifdef UNIT_TEST
#include "unit_test.h"
@@ -1192,50 +1120,6 @@ int test_processquoted(void)
return rc;
}
#define TIME_TEST(V, B, U, R) \
MY_TEST(convert_time_units((V), (B), U) == (R), \
"convert " #V " with base of " #B ", " #U " units")
int test_convert_time_units()
{
int rc = 0;
TIME_TEST(1LL, 1LL, NULL, 1LL);
TIME_TEST(12345LL, 1LL, NULL, 12345LL);
TIME_TEST(10LL, 10LL, NULL, 10LL);
TIME_TEST(123450LL, 10LL, NULL, 123450LL);
TIME_TEST(12345LL, 1LL, "us", 12345LL);
TIME_TEST(12345LL, 1LL, "microsecond", 12345LL);
TIME_TEST(12345LL, 1LL, "microseconds", 12345LL);
TIME_TEST(12345LL, 1LL, "ms", 12345LL*1000LL);
TIME_TEST(12345LL, 1LL, "millisecond", 12345LL*1000LL);
TIME_TEST(12345LL, 1LL, "milliseconds", 12345LL*1000LL);
TIME_TEST(12345LL, 1LL, "s", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "sec", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "second", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "seconds", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "min", 12345LL*1000LL*1000LL*60LL);
TIME_TEST(12345LL, 1LL, "minute", 12345LL*1000LL*1000LL*60LL);
TIME_TEST(12345LL, 1LL, "minutes", 12345LL*1000LL*1000LL*60LL);
TIME_TEST(12345LL, 1LL, "h", 12345LL*1000LL*1000LL*60LL*60LL);
TIME_TEST(12345LL, 1LL, "hour", 12345LL*1000LL*1000LL*60LL*60LL);
TIME_TEST(12345LL, 1LL, "hours", 12345LL*1000LL*1000LL*60LL*60LL);
TIME_TEST(12345LL, 1LL, "d", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
TIME_TEST(12345LL, 1LL, "day", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
TIME_TEST(12345LL, 1LL, "days", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
TIME_TEST(12345LL, 1LL, "week", 12345LL*1000LL*1000LL*60LL*60LL*24LL*7LL);
TIME_TEST(12345LL, 1LL, "weeks", 12345LL*1000LL*1000LL*60LL*60LL*24LL*7LL);
return rc;
}
int main(void)
{
int rc = 0;
@@ -1253,10 +1137,6 @@ int main(void)
if (retval != 0)
rc = retval;
retval = test_convert_time_units();
if (retval != 0)
rc = retval;
return rc;
}
#endif /* UNIT_TEST */

View File

@@ -27,7 +27,6 @@
#include <search.h>
#include <string.h>
#include <errno.h>
#include <sys/apparmor.h>
#include "parser.h"
#include "profile.h"
@@ -221,13 +220,12 @@ static int profile_add_hat_rules(Profile *prof)
return 0;
}
int load_policy_list(ProfileList &list, int option,
aa_kernel_interface *kernel_interface, int cache_fd)
int load_policy_list(ProfileList &list, int option)
{
int res = 0;
for (ProfileList::iterator i = list.begin(); i != list.end(); i++) {
res = load_profile(option, kernel_interface, *i, cache_fd);
res = load_profile(option, *i);
if (res != 0)
break;
}
@@ -235,16 +233,14 @@ int load_policy_list(ProfileList &list, int option,
return res;
}
int load_flattened_hats(Profile *prof, int option,
aa_kernel_interface *kernel_interface, int cache_fd)
int load_flattened_hats(Profile *prof, int option)
{
return load_policy_list(prof->hat_table, option, kernel_interface,
cache_fd);
return load_policy_list(prof->hat_table, option);
}
int load_policy(int option, aa_kernel_interface *kernel_interface, int cache_fd)
int load_policy(int option)
{
return load_policy_list(policy_list, option, kernel_interface, cache_fd);
return load_policy_list(policy_list, option);
}
int load_hats(std::ostringstream &buf, Profile *prof)

View File

@@ -29,7 +29,6 @@
/* #define DEBUG */
#include "lib.h"
#include "parser.h"
#include "profile.h"
#include "libapparmor_re/apparmor_re.h"
@@ -84,27 +83,9 @@ static void filter_slashes(char *path)
*dptr = 0;
}
static error_type append_glob(std::string &pcre, int glob,
const char *default_glob, const char *null_glob)
{
switch (glob) {
case glob_default:
pcre.append(default_glob);
break;
case glob_null:
pcre.append(null_glob);
break;
default:
PERROR(_("%s: Invalid glob type %d\n"), progname, glob);
return e_parse_error;
break;
}
return e_no_error;
}
/* converts the apparmor regex in aare and appends pcre regex output
* to pcre string */
pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
pattern_t convert_aaregex_to_pcre(const char *aare, int anchor,
std::string& pcre, int *first_re_pos)
{
#define update_re_pos(X) if (!(*first_re_pos)) { *first_re_pos = (X); }
@@ -189,8 +170,9 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
const char *s = sptr;
while (*s == '*')
s++;
if (*s == '/' || !*s)
error = append_glob(pcre, glob, "[^/\\x00]", "[^/]");
if (*s == '/' || !*s) {
pcre.append("[^/\\x00]");
}
}
if (*(sptr + 1) == '*') {
/* is this the first regex form we
@@ -206,12 +188,13 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
} else {
ptype = ePatternRegex;
}
error = append_glob(pcre, glob, "[^\\x00]*", ".*");
pcre.append("[^\\x00]*");
sptr++;
} else {
update_re_pos(sptr - aare);
ptype = ePatternRegex;
error = append_glob(pcre, glob, "[^/\\x00]*", "[^/]*");
pcre.append("[^/\\x00]*");
} /* *(sptr+1) == '*' */
} /* bEscape */
@@ -359,26 +342,12 @@ pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, int glob,
default:
if (bEscape) {
const char *pos = sptr;
int c;
if ((c = str_escseq(&pos, "")) != -1) {
/* valid escape we don't want to
* interpret here */
pcre.append("\\");
pcre.append(sptr, pos - sptr);
sptr += (pos - sptr) - 1;
} else {
/* quoting mark used for something that
* does not need to be quoted; give a
* warning */
pwarn("Character %c was quoted "
"unnecessarily, dropped preceding"
" quote ('\\') character\n",
*sptr);
pcre.append(1, *sptr);
}
} else
pcre.append(1, *sptr);
/* quoting mark used for something that
* does not need to be quoted; give a warning */
pwarn("Character %c was quoted unnecessarily, "
"dropped preceding quote ('\\') character\n", *sptr);
}
pcre.append(1, *sptr);
break;
} /* switch (*sptr) */
@@ -443,7 +412,7 @@ static int process_profile_name_xmatch(Profile *prof)
name = prof->attachment;
else
name = local_name(prof->name);
ptype = convert_aaregex_to_pcre(name, 0, glob_default, tbuf,
ptype = convert_aaregex_to_pcre(name, 0, tbuf,
&prof->xmatch_len);
if (ptype == ePatternBasic)
prof->xmatch_len = strlen(name);
@@ -471,8 +440,8 @@ static int process_profile_name_xmatch(Profile *prof)
int len;
tbuf.clear();
ptype = convert_aaregex_to_pcre(alt->name, 0,
glob_default,
tbuf, &len);
tbuf,
&len);
if (ptype == ePatternBasic)
len = strlen(alt->name);
if (len < prof->xmatch_len)
@@ -492,8 +461,6 @@ static int process_profile_name_xmatch(Profile *prof)
return TRUE;
}
static int warn_change_profile = 1;
static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
{
std::string tbuf;
@@ -506,7 +473,7 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
if (entry->mode & ~AA_CHANGE_PROFILE)
filter_slashes(entry->name);
ptype = convert_aaregex_to_pcre(entry->name, 0, glob_default, tbuf, &pos);
ptype = convert_aaregex_to_pcre(entry->name, 0, tbuf, &pos);
if (ptype == ePatternInvalid)
return FALSE;
@@ -516,9 +483,9 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
* dfa states like it does for pcre
*/
if ((entry->mode >> AA_OTHER_SHIFT) & AA_EXEC_INHERIT)
entry->mode |= AA_OLD_EXEC_MMAP << AA_OTHER_SHIFT;
entry->mode |= AA_EXEC_MMAP << AA_OTHER_SHIFT;
if ((entry->mode >> AA_USER_SHIFT) & AA_EXEC_INHERIT)
entry->mode |= AA_OLD_EXEC_MMAP << AA_USER_SHIFT;
entry->mode |= AA_EXEC_MMAP << AA_USER_SHIFT;
/* the link bit on the first pair entry should not get masked
* out by a deny rule, as both pieces of the link pair must
@@ -532,9 +499,8 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
if (entry->deny) {
if ((entry->mode & ~(AA_LINK_BITS | AA_CHANGE_PROFILE)) &&
!dfarules->add_rule(tbuf.c_str(), entry->deny,
entry->mode & ~(AA_LINK_BITS | AA_CHANGE_PROFILE),
entry->audit & ~(AA_LINK_BITS | AA_CHANGE_PROFILE),
dfaflags))
entry->mode & ~AA_LINK_BITS,
entry->audit & ~AA_LINK_BITS, dfaflags))
return FALSE;
} else if (entry->mode & ~AA_CHANGE_PROFILE) {
if (!dfarules->add_rule(tbuf.c_str(), entry->deny, entry->mode,
@@ -550,7 +516,7 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
int pos;
vec[0] = tbuf.c_str();
if (entry->link_name) {
ptype = convert_aaregex_to_pcre(entry->link_name, 0, glob_default, lbuf, &pos);
ptype = convert_aaregex_to_pcre(entry->link_name, 0, lbuf, &pos);
if (ptype == ePatternInvalid)
return FALSE;
if (entry->subset)
@@ -565,40 +531,26 @@ static int process_dfa_entry(aare_rules *dfarules, struct cod_entry *entry)
}
if (entry->mode & AA_CHANGE_PROFILE) {
const char *vec[3];
std::string lbuf, xbuf;
std::string lbuf;
int index = 1;
if ((warnflags & WARN_RULE_DOWNGRADED) && entry->audit && warn_change_profile) {
/* don't have profile name here, so until this code
* gets refactored just throw out a generic warning
*/
fprintf(stderr, "Warning kernel does not support audit modifier for change_profile rule.\n");
warn_change_profile = 0;
}
if (entry->onexec) {
ptype = convert_aaregex_to_pcre(entry->onexec, 0, glob_default, xbuf, &pos);
if (ptype == ePatternInvalid)
return FALSE;
vec[0] = xbuf.c_str();
} else
/* allow change_profile for all execs */
vec[0] = "/[^/\\x00][^\\x00]*";
/* allow change_profile for all execs */
vec[0] = "/[^\\x00]*";
if (entry->ns) {
int pos;
ptype = convert_aaregex_to_pcre(entry->ns, 0, glob_default, lbuf, &pos);
ptype = convert_aaregex_to_pcre(entry->ns, 0, lbuf, &pos);
vec[index++] = lbuf.c_str();
}
vec[index++] = tbuf.c_str();
/* regular change_profile rule */
if (!dfarules->add_rule_vec(entry->deny, AA_CHANGE_PROFILE | AA_ONEXEC, 0, index - 1, &vec[1], dfaflags))
if (!dfarules->add_rule_vec(0, AA_CHANGE_PROFILE | AA_ONEXEC, 0, index - 1, &vec[1], dfaflags))
return FALSE;
/* onexec rules - both rules are needed for onexec */
if (!dfarules->add_rule_vec(entry->deny, AA_ONEXEC, 0, 1, vec, dfaflags))
if (!dfarules->add_rule_vec(0, AA_ONEXEC, 0, 1, vec, dfaflags))
return FALSE;
if (!dfarules->add_rule_vec(entry->deny, AA_ONEXEC, 0, index, vec, dfaflags))
if (!dfarules->add_rule_vec(0, AA_ONEXEC, 0, index, vec, dfaflags))
return FALSE;
}
return TRUE;
@@ -669,13 +621,13 @@ int build_list_val_expr(std::string& buffer, struct value_list *list)
buffer.append("(");
ptype = convert_aaregex_to_pcre(list->value, 0, glob_default, buffer, &pos);
ptype = convert_aaregex_to_pcre(list->value, 0, buffer, &pos);
if (ptype == ePatternInvalid)
goto fail;
list_for_each(list->next, ent) {
buffer.append("|");
ptype = convert_aaregex_to_pcre(ent->value, 0, glob_default, buffer, &pos);
ptype = convert_aaregex_to_pcre(ent->value, 0, buffer, &pos);
if (ptype == ePatternInvalid)
goto fail;
}
@@ -692,7 +644,7 @@ int convert_entry(std::string& buffer, char *entry)
int pos;
if (entry) {
ptype = convert_aaregex_to_pcre(entry, 0, glob_default, buffer, &pos);
ptype = convert_aaregex_to_pcre(entry, 0, buffer, &pos);
if (ptype == ePatternInvalid)
return FALSE;
} else {
@@ -779,6 +731,8 @@ int process_profile_policydb(Profile *prof)
prof->policy.rules = NULL;
}
aare_reset_matchflags();
error = 0;
out:
@@ -788,6 +742,11 @@ out:
return error;
}
void reset_regex(void)
{
aare_reset_matchflags();
}
#ifdef UNIT_TEST
#include "unit_test.h"
@@ -836,7 +795,7 @@ static int test_filter_slashes(void)
return rc;
}
#define MY_REGEX_EXT_TEST(glob, input, expected_str, expected_type) \
#define MY_REGEX_TEST(input, expected_str, expected_type) \
do { \
std::string tbuf; \
std::string tbuf2 = "testprefix"; \
@@ -845,7 +804,7 @@ static int test_filter_slashes(void)
pattern_t ptype; \
int pos; \
\
ptype = convert_aaregex_to_pcre((input), 0, glob, tbuf, &pos); \
ptype = convert_aaregex_to_pcre((input), 0, tbuf, &pos); \
asprintf(&output_string, "simple regex conversion for '%s'\texpected = '%s'\tresult = '%s'", \
(input), (expected_str), tbuf.c_str()); \
MY_TEST(strcmp(tbuf.c_str(), (expected_str)) == 0, output_string); \
@@ -854,25 +813,21 @@ static int test_filter_slashes(void)
/* ensure convert_aaregex_to_pcre appends only to passed ref string */ \
expected_str2 = tbuf2; \
expected_str2.append((expected_str)); \
ptype = convert_aaregex_to_pcre((input), 0, glob, tbuf2, &pos); \
asprintf(&output_string, "simple regex conversion %sfor '%s'\texpected = '%s'\tresult = '%s'", \
glob == glob_null ? "with null allowed in glob " : "",\
ptype = convert_aaregex_to_pcre((input), 0, tbuf2, &pos); \
asprintf(&output_string, "simple regex conversion for '%s'\texpected = '%s'\tresult = '%s'", \
(input), expected_str2.c_str(), tbuf2.c_str()); \
MY_TEST((tbuf2 == expected_str2), output_string); \
free(output_string); \
} \
while (0)
#define MY_REGEX_TEST(input, expected_str, expected_type) MY_REGEX_EXT_TEST(glob_default, input, expected_str, expected_type)
#define MY_REGEX_FAIL_TEST(input) \
do { \
std::string tbuf; \
pattern_t ptype; \
int pos; \
\
ptype = convert_aaregex_to_pcre((input), 0, glob_default, tbuf, &pos); \
ptype = convert_aaregex_to_pcre((input), 0, tbuf, &pos); \
MY_TEST(ptype == ePatternInvalid, "simple regex conversion invalid type check for '" input "'"); \
} \
while (0)
@@ -977,9 +932,6 @@ static int test_aaregex_to_pcre(void)
MY_REGEX_TEST("\\\\|", "\\\\\\|", ePatternBasic);
MY_REGEX_TEST("\\\\(", "\\\\\\(", ePatternBasic);
MY_REGEX_TEST("\\\\)", "\\\\\\)", ePatternBasic);
MY_REGEX_TEST("\\000", "\\000", ePatternBasic);
MY_REGEX_TEST("\\x00", "\\x00", ePatternBasic);
MY_REGEX_TEST("\\d000", "\\d000", ePatternBasic);
/* more complicated character class tests */
/* -- embedded alternations */
@@ -993,27 +945,6 @@ static int test_aaregex_to_pcre(void)
MY_REGEX_TEST("{alpha,b[\\{a,b\\}]t,gamma}", "(alpha|b[\\{a,b\\}]t|gamma)", ePatternRegex);
MY_REGEX_TEST("{alpha,b[\\{a\\,b\\}]t,gamma}", "(alpha|b[\\{a\\,b\\}]t|gamma)", ePatternRegex);
/* test different globbing behavior conversion */
MY_REGEX_EXT_TEST(glob_default, "/foo/**", "/foo/[^/\\x00][^\\x00]*", ePatternTailGlob);
MY_REGEX_EXT_TEST(glob_null, "/foo/**", "/foo/[^/].*", ePatternTailGlob);
MY_REGEX_EXT_TEST(glob_default, "/foo/f**", "/foo/f[^\\x00]*", ePatternTailGlob);
MY_REGEX_EXT_TEST(glob_null, "/foo/f**", "/foo/f.*", ePatternTailGlob);
MY_REGEX_EXT_TEST(glob_default, "/foo/*", "/foo/[^/\\x00][^/\\x00]*", ePatternRegex);
MY_REGEX_EXT_TEST(glob_null, "/foo/*", "/foo/[^/][^/]*", ePatternRegex);
MY_REGEX_EXT_TEST(glob_default, "/foo/f*", "/foo/f[^/\\x00]*", ePatternRegex);
MY_REGEX_EXT_TEST(glob_null, "/foo/f*", "/foo/f[^/]*", ePatternRegex);
MY_REGEX_EXT_TEST(glob_default, "/foo/**.ext", "/foo/[^\\x00]*\\.ext", ePatternRegex);
MY_REGEX_EXT_TEST(glob_null, "/foo/**.ext", "/foo/.*\\.ext", ePatternRegex);
MY_REGEX_EXT_TEST(glob_default, "/foo/f**.ext", "/foo/f[^\\x00]*\\.ext", ePatternRegex);
MY_REGEX_EXT_TEST(glob_null, "/foo/f**.ext", "/foo/f.*\\.ext", ePatternRegex);
MY_REGEX_EXT_TEST(glob_default, "/foo/*.ext", "/foo/[^/\\x00]*\\.ext", ePatternRegex);
MY_REGEX_EXT_TEST(glob_null, "/foo/*.ext", "/foo/[^/]*\\.ext", ePatternRegex);
MY_REGEX_EXT_TEST(glob_default, "/foo/f*.ext", "/foo/f[^/\\x00]*\\.ext", ePatternRegex);
MY_REGEX_EXT_TEST(glob_null, "/foo/f*.ext", "/foo/f[^/]*\\.ext", ePatternRegex);
return rc;
}

View File

@@ -254,11 +254,6 @@ static int process_variables_in_entries(struct cod_entry *entry_list)
error = expand_entry_variables(&entry->name);
if (error)
return error;
if (entry->link_name) {
error = expand_entry_variables(&entry->link_name);
if (error)
return error;
}
}
return 0;
@@ -275,51 +270,14 @@ static int process_variables_in_rules(Profile &prof)
return 0;
}
static int process_variables_in_name(Profile &prof)
{
/* this needs to be done before alias expansion, ie. altnames are
* setup
*/
int error = expand_entry_variables(&prof.name);
if (!error && prof.attachment)
error = expand_entry_variables(&prof.attachment);
return error;
}
static std::string escape_re(std::string str)
{
for (size_t i = 0; i < str.length(); i++) {
if (str[i] == '\\') {
/* skip \ and follow char. Skipping \ and first
* char is enough for multichar escape sequence
*/
i++;
continue;
}
if (strchr("{}[]*?", str[i]) != NULL) {
str.insert(i++, "\\");
}
}
return str;
}
int process_profile_variables(Profile *prof)
{
int error = 0, rc;
std::string *buf = prof->get_name(true);
/* needs to be before PROFILE_NAME_VARIABLE so that variable will
* have the correct name
*/
error = process_variables_in_name(*prof);
if (!error) {
/* escape profile name elements that could be interpreted
* as regular expressions.
*/
error = new_set_var(PROFILE_NAME_VARIABLE, escape_re(prof->get_name(false)).c_str());
}
error = new_set_var(PROFILE_NAME_VARIABLE, buf->c_str());
delete buf;
if (!error)
error = process_variables_in_entries(prof->entries);

View File

@@ -78,6 +78,7 @@ mnt_rule *do_mnt_rule(struct cond_entry *src_conds, char *src,
int mode);
mnt_rule *do_pivot_rule(struct cond_entry *old, char *root,
char *transition);
void add_local_entry(Profile *prof);
%}
@@ -243,7 +244,6 @@ void add_local_entry(Profile *prof);
%type <flags> flagval
%type <cap> caps
%type <cap> capability
%type <id> change_profile_head
%type <user_entry> change_profile
%type <set_var> TOK_SET_VAR
%type <bool_var> TOK_BOOL_VAR
@@ -251,13 +251,13 @@ void add_local_entry(Profile *prof);
%type <val_list> valuelist
%type <boolean> expr
%type <id> id_or_var
%type <id> opt_id_or_var
%type <boolean> opt_subset_flag
%type <boolean> opt_audit_flag
%type <boolean> opt_owner_flag
%type <boolean> opt_profile_flag
%type <boolean> opt_flags
%type <boolean> opt_perm_mode
%type <id> opt_ns
%type <id> opt_id
%type <prefix> opt_prefix
%type <fmode> dbus_perm
@@ -297,13 +297,13 @@ opt_profile_flag: { /* nothing */ $$ = 0; }
| TOK_PROFILE { $$ = 1; }
| hat_start { $$ = 2; }
opt_ns: { /* nothing */ $$ = NULL; }
| TOK_COLON TOK_ID TOK_COLON { $$ = $2; }
opt_id: { /* nothing */ $$ = NULL; }
| TOK_ID { $$ = $1; }
opt_id_or_var: { /* nothing */ $$ = NULL; }
| id_or_var { $$ = $1; }
profile_base: TOK_ID opt_id_or_var flags TOK_OPEN rules TOK_CLOSE
profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE
{
Profile *prof = $5;
@@ -311,29 +311,13 @@ profile_base: TOK_ID opt_id_or_var flags TOK_OPEN rules TOK_CLOSE
yyerror(_("Memory allocation error."));
}
parse_label(&prof->ns, &prof->name, $1);
free($1);
/* Honor the --namespace-string command line option */
if (profile_ns) {
/**
* Print warning if the profile specified a namespace
* different than the one specified with the
* --namespace-string command line option
*/
if (prof->ns && strcmp(prof->ns, profile_ns))
pwarn("%s: -n %s overriding policy specified namespace :%s:\n",
progname, profile_ns, prof->ns);
free(prof->ns);
prof->ns = strdup(profile_ns);
if (!prof->ns)
yyerror(_("Memory allocation error."));
}
prof->name = $1;
prof->attachment = $2;
if ($2 && !($2[0] == '/' || strncmp($2, "@{", 2) == 0))
yyerror(_("Profile attachment must begin with a '/' or variable."));
if ($2 && $2[0] != '/')
/* we don't support variables as part of the profile
* name or attachment atm
*/
yyerror(_("Profile attachment must begin with a '/'."));
prof->flags = $3;
if (force_complain && kernel_abi_version == 5)
/* newer abis encode force complain as part of the
@@ -352,18 +336,18 @@ profile_base: TOK_ID opt_id_or_var flags TOK_OPEN rules TOK_CLOSE
};
profile: opt_profile_flag profile_base
profile: opt_profile_flag opt_ns profile_base
{
Profile *prof = $2;
if ($2->ns)
PDEBUG("Matched: :%s://%s { ... }\n", $2->ns, $2->name);
Profile *prof = $3;
if ($2)
PDEBUG("Matched: %s://%s { ... }\n", $2, $3->name);
else
PDEBUG("Matched: %s { ... }\n", $2->name);
PDEBUG("Matched: %s { ... }\n", $3->name);
if ($2->name[0] != '/' && !($1 || $2->ns))
if ($3->name[0] != '/' && !($1 || $2))
yyerror(_("Profile names must begin with a '/', namespace or keyword 'profile' or 'hat'."));
prof->ns = $2;
if ($1 == 2)
prof->flags.hat = 1;
$$ = prof;
@@ -794,23 +778,13 @@ rules: rules opt_prefix unix_rule
$$ = $1;
}
rules: rules opt_prefix change_profile
rules: rules change_profile
{
PDEBUG("matched: rules change_profile\n");
PDEBUG("rules change_profile: (%s)\n", $3->name);
if (!$3)
PDEBUG("rules change_profile: (%s)\n", $2->name);
if (!$2)
yyerror(_("Assert: `change_profile' returned NULL."));
if ($2.owner)
yyerror(_("owner prefix not allowed on unix rules"));
if ($2.deny && $2.audit) {
$3->deny = 1;
} else if ($2.deny) {
$3->deny = 1;
$3->audit = $3->mode;
} else if ($2.audit) {
$3->audit = $3->mode;
}
add_entry_to_policy($1, $3);
add_entry_to_policy($1, $2);
$$ = $1;
};
@@ -858,7 +832,7 @@ rules: rules cond_rule
$$ = merge_policy($1, $2);
}
rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
{
rlim_t value = RLIM_INFINITY;
long long tmp;
@@ -871,6 +845,11 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
if (strcmp($6, "infinity") == 0) {
value = RLIM_INFINITY;
} else {
const char *seconds = "seconds";
const char *milliseconds = "ms";
const char *minutes = "minutes";
const char *hours = "hours";
const char *days = "days";
const char *kb = "KB";
const char *mb = "MB";
const char *gb = "GB";
@@ -880,25 +859,34 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
case RLIMIT_CPU:
if (!end || $6 == end || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
tmp = convert_time_units(tmp, SECONDS_P_MS, $7);
if (tmp == -1LL)
yyerror("RLIMIT '%s %s' < minimum value of 1s\n", $4, $6);
else if (tmp < 0LL)
if (*end == '\0' ||
strstr(seconds, end) == seconds) {
value = tmp;
} else if (strstr(minutes, end) == minutes) {
value = tmp * 60;
} else if (strstr(hours, end) == hours) {
value = tmp * 60 * 60;
} else if (strstr(days, end) == days) {
value = tmp * 60 * 60 * 24;
} else {
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
if (!$7)
pwarn(_("RLIMIT 'cpu' no units specified using default units of seconds\n"));
value = tmp;
}
break;
case RLIMIT_RTTIME:
/* RTTIME is measured in microseconds */
if (!end || $6 == end || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
tmp = convert_time_units(tmp, 1LL, $7);
if (tmp < 0LL)
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
if (!$7)
pwarn(_("RLIMIT 'rttime' no units specified using default units of microseconds\n"));
value = tmp;
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
if (*end == '\0') {
value = tmp;
} else if (strstr(milliseconds, end) == milliseconds) {
value = tmp * 1000;
} else if (strstr(seconds, end) == seconds) {
value = tmp * 1000 * 1000;
} else if (strstr(minutes, end) == minutes) {
value = tmp * 1000 * 1000 * 60;
} else {
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
}
break;
case RLIMIT_NOFILE:
case RLIMIT_NPROC:
@@ -906,15 +894,15 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
case RLIMIT_SIGPENDING:
#ifdef RLIMIT_RTPRIO
case RLIMIT_RTPRIO:
if (!end || $6 == end || $7 || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
if (!end || $6 == end || *end != '\0' || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
value = tmp;
break;
#endif
#ifdef RLIMIT_NICE
case RLIMIT_NICE:
if (!end || $6 == end || $7)
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
if (!end || $6 == end || *end != '\0')
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
if (tmp < -20 || tmp > 19)
yyerror("RLIMIT '%s' out of range (-20 .. 19) %d\n", $4, tmp);
value = tmp + 20;
@@ -929,17 +917,15 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE opt_id TOK_END_OF_RULE
case RLIMIT_MEMLOCK:
case RLIMIT_MSGQUEUE:
if ($6 == end || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7 ? $7 : "");
if (!$7) {
; /* use default of bytes */
} else if (strstr(kb, $7) == kb) {
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
if (strstr(kb, end) == kb) {
tmp *= 1024;
} else if (strstr(mb, $7) == mb) {
} else if (strstr(mb, end) == mb) {
tmp *= 1024*1024;
} else if (strstr(gb, $7) == gb) {
} else if (strstr(gb, end) == gb) {
tmp *= 1024*1024*1024;
} else {
yyerror("RLIMIT '%s' invalid value %s %s\n", $4, $6, $7);
} else if (*end != '\0') {
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
}
value = tmp;
break;
@@ -1040,12 +1026,21 @@ id_or_var: TOK_SET_VAR { $$ = $1; };
opt_named_transition:
{ /* nothing */
parse_named_transition_target(&$$, NULL);
$$.present = 0;
$$.ns = NULL;
$$.name = NULL;
}
| TOK_ARROW id_or_var
{
parse_named_transition_target(&$$, $2);
free($2);
$$.present = 1;
$$.ns = NULL;
$$.name = $2;
}
| TOK_ARROW TOK_COLON id_or_var TOK_COLON id_or_var
{
$$.present = 1;
$$.ns = $3;
$$.name = $5;
};
rule: file_rule { $$ = $1; }
@@ -1118,7 +1113,7 @@ file_rule_tail: opt_unsafe id_or_var file_mode id_or_var
yyerror(_("missing an end of line character? (entry: %s)"), $2);
};
link_rule: TOK_LINK opt_subset_flag id_or_var TOK_ARROW id_or_var TOK_END_OF_RULE
link_rule: TOK_LINK opt_subset_flag TOK_ID TOK_ARROW TOK_ID TOK_END_OF_RULE
{
struct cod_entry *entry;
PDEBUG("Matched: link tok_id (%s) -> (%s)\n", $3, $5);
@@ -1479,36 +1474,29 @@ file_mode: TOK_MODE
free($1);
}
change_profile_head: TOK_CHANGE_PROFILE opt_id
{
if ($2 && !($2[0] == '/' || strncmp($2, "@{", 2) == 0))
yyerror(_("Exec condition must begin with '/'."));
$$ = $2;
}
change_profile: change_profile_head opt_named_transition TOK_END_OF_RULE
change_profile: TOK_CHANGE_PROFILE TOK_ARROW TOK_ID TOK_END_OF_RULE
{
struct cod_entry *entry;
if ($2.present) {
PDEBUG("Matched change_profile: tok_id (:%s://%s)\n",
$2.ns ? $2.ns : "", $2.name);
entry = new_entry($2.ns, $2.name, AA_CHANGE_PROFILE, $1);
} else {
char *rule = strdup("**");
if (!rule)
yyerror(_("Memory allocation error."));
PDEBUG("Matched change_profile,\n");
entry = new_entry(NULL, rule, AA_CHANGE_PROFILE, $1);
}
PDEBUG("Matched change_profile: tok_id (%s)\n", $3);
entry = new_entry(NULL, $3, AA_CHANGE_PROFILE, NULL);
if (!entry)
yyerror(_("Memory allocation error."));
PDEBUG("change_profile.entry: (%s)\n", entry->name);
$$ = entry;
};
change_profile: TOK_CHANGE_PROFILE TOK_ARROW TOK_COLON TOK_ID TOK_COLON TOK_ID TOK_END_OF_RULE
{
struct cod_entry *entry;
PDEBUG("Matched change_profile: tok_id (%s:%s)\n", $4, $6);
entry = new_entry($4, $6, AA_CHANGE_PROFILE, NULL);
if (!entry)
yyerror(_("Memory allocation error."));
PDEBUG("change_profile.entry: (%s)\n", entry->name);
$$ = entry;
};
capability: TOK_CAPABILITY caps TOK_END_OF_RULE
{
if ($2 == 0) {

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