...
 
Commits (169)
......@@ -63,7 +63,8 @@ parser/techdoc.aux
parser/techdoc.log
parser/techdoc.pdf
parser/techdoc.toc
profiles/apparmor.d/local/*.*
profiles/apparmor.d/local/*
!profiles/apparmor.d/local/README
libraries/libapparmor/Makefile
libraries/libapparmor/Makefile.in
libraries/libapparmor/aclocal.m4
......@@ -94,6 +95,8 @@ libraries/libapparmor/src/.deps
libraries/libapparmor/src/.libs
libraries/libapparmor/src/Makefile
libraries/libapparmor/src/Makefile.in
libraries/libapparmor/src/PMurHash.lo
libraries/libapparmor/src/PMurHash.o
libraries/libapparmor/src/af_protos.h
libraries/libapparmor/src/change_hat.lo
libraries/libapparmor/src/features.lo
......@@ -160,8 +163,14 @@ libraries/libapparmor/swig/python/test/test-suite.log
libraries/libapparmor/swig/python/test/test_python.py
libraries/libapparmor/swig/python/test/test_python.py.log
libraries/libapparmor/swig/python/test/test_python.py.trs
libraries/libapparmor/swig/ruby/LibAppArmor.so
libraries/libapparmor/swig/ruby/LibAppArmor_wrap.c
libraries/libapparmor/swig/ruby/LibAppArmor_wrap.o
libraries/libapparmor/swig/ruby/Makefile
libraries/libapparmor/swig/ruby/Makefile.in
libraries/libapparmor/swig/ruby/Makefile.new
libraries/libapparmor/swig/ruby/Makefile.ruby
libraries/libapparmor/swig/ruby/mkmf.log
libraries/libapparmor/testsuite/.deps
libraries/libapparmor/testsuite/.libs
libraries/libapparmor/testsuite/Makefile
......@@ -187,6 +196,7 @@ utils/*.tmp
utils/po/*.mo
utils/apparmor/*.pyc
utils/apparmor/rule/*.pyc
utils/test/common_test.pyc
utils/test/.coverage
utils/test/htmlcov/
utils/vim/apparmor.vim
......
......@@ -19,7 +19,7 @@ DIRS=libraries/libapparmor \
# with conversion to git, we don't export from the remote
REPO_URL?=git@gitlab.com:apparmor/apparmor.git
REPO_BRANCH?=master
REPO_BRANCH?=apparmor-2.12
COVERITY_DIR=cov-int
RELEASE_DIR=apparmor-${VERSION}
......@@ -55,7 +55,11 @@ snapshot: clean
coverity: snapshot
cd $(SNAPSHOT_NAME)/libraries/libapparmor && ./configure --with-python
$(foreach dir, $(filter-out utils profiles tests, $(DIRS)), \
cov-build --dir $(COVERITY_DIR) -- $(MAKE) -C $(SNAPSHOT_NAME)/$(dir);)
cov-build --dir $(COVERITY_DIR) -- $(MAKE) -C $(SNAPSHOT_NAME)/$(dir); \
mv $(COVERITY_DIR)/build-log.txt $(COVERITY_DIR)/build-log-$(subst /,.,$(dir)).txt ;)
$(foreach dir, libraries/libapparmor utils, \
cov-build --dir $(COVERITY_DIR) --no-command --fs-capture-search $(SNAPSHOT_NAME)/$(dir); \
mv $(COVERITY_DIR)/build-log.txt $(COVERITY_DIR)/build-log-python-$(subst /,.,$(dir)).txt ;)
tar -cvzf $(SNAPSHOT_NAME)-$(COVERITY_DIR).tar.gz $(COVERITY_DIR)
.PHONY: export_dir
......
# AppArmor
[![Build status](https://gitlab.com/apparmor/apparmor/badges/master/build.svg)](https://gitlab.com/apparmor/apparmor/commits/master)
[![Overall test coverage](https://gitlab.com/apparmor/apparmor/badges/master/coverage.svg)](https://gitlab.com/apparmor/apparmor/pipelines)
[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/1699/badge)](https://bestpractices.coreinfrastructure.org/projects/1699)
------------
Introduction
------------
......@@ -17,9 +23,27 @@ library, available under the LGPL license, which allows change_hat(2)
and change_profile(2) to be used by non-GPL binaries).
For more information, you can read the techdoc.pdf (available after
building the parser) and by visiting the http://apparmor.net/ web
building the parser) and by visiting the https://apparmor.net/ web
site.
----------------
Getting in Touch
----------------
Please send all complaints, feature requests, rants about the software,
and questions to the
[AppArmor mailing list](https://lists.ubuntu.com/mailman/listinfo/apparmor).
Bug reports can be filed against the AppArmor project on
[launchpad](https://bugs.launchpad.net/apparmor) or reported to the mailing
list directly for those who wish not to register for an account on
launchpad. See the
[wiki page](https://gitlab.com/apparmor/apparmor/wikis/home#reporting-bugs)
for more information.
Security issues can be filed as security bugs on launchpad
or directed to `security@apparmor.net`. Additional details can be found
in the [wiki](https://gitlab.com/apparmor/apparmor/wikis/home#reporting-security-vulnerabilities).
-------------
Source Layout
......@@ -27,6 +51,7 @@ Source Layout
AppArmor consists of several different parts:
```
binutils/ source for basic utilities written in compiled languages
changehat/ source for using changehat with Apache, PAM and Tomcat
common/ common makefile rules
......@@ -37,6 +62,7 @@ parser/ source for parser/loader and corresponding documentation
profiles/ configuration files, reference profiles and abstractions
tests/ regression and stress testsuites
utils/ high-level utilities for working with AppArmor
```
--------------------------------------
Important note on AppArmor kernel code
......@@ -61,63 +87,82 @@ the following order. Some systems may need to export various python-related
environment variables to complete the build. For example, before building
anything on these systems, use something along the lines of:
```
$ export PYTHONPATH=$(realpath libraries/libapparmor/swig/python)
$ export PYTHON=/usr/bin/python3
$ export PYTHON_VERSION=3
$ export PYTHON_VERSIONS=python3
```
libapparmor:
```
$ cd ./libraries/libapparmor
$ sh ./autogen.sh
$ sh ./configure --prefix=/usr --with-perl --with-python # see below
$ make
$ make check
$ make install
```
[an additional optional argument to libapparmor's configure is --with-ruby, to
generate Ruby bindings to libapparmor.]
Binary Utilities:
```
$ cd binutils
$ make
$ make check
$ make install
```
parser:
```
$ cd parser
$ make # depends on libapparmor having been built first
$ make check
$ make install
```
Utilities:
```
$ cd utils
$ make
$ make check
$ make install
```
Apache mod_apparmor:
```
$ cd changehat/mod_apparmor
$ make # depends on libapparmor having been built first
$ make install
```
PAM AppArmor:
```
$ cd changehat/pam_apparmor
$ make # depends on libapparmor having been built first
$ make install
```
Profiles:
```
$ cd profiles
$ make
$ make check # depends on the parser having been built first
$ make install
```
[Note that for the parser, binutils, and utils, if you only wish to build/use
some of the locale languages, you can override the default by passing
......@@ -138,38 +183,50 @@ For details on structure and adding tests, see
tests/regression/apparmor/README.
To run:
```
$ cd tests/regression/apparmor (requires root)
$ make
$ sudo make tests
$ sudo bash open.sh -r # runs and saves the last testcase from open.sh
```
Parser tests
------------
For details on structure and adding tests, see parser/tst/README.
To run:
```
$ cd parser/tst
$ make
$ make tests
```
Libapparmor
-----------
For details on structure and adding tests, see libraries/libapparmor/README.
```
$ cd libraries/libapparmor
$ make check
```
Utils
-----
Tests for the Python utilities exist in the test/ subdirectory.
```
$ cd utils
$ make check
```
The aa-decode utility to be tested can be overridden by
setting up environment variable APPARMOR_DECODE; e.g.:
```
$ APPARMOR_DECODE=/usr/bin/aa-decode make check
```
Profile checks
--------------
......@@ -177,29 +234,44 @@ A basic consistency check to ensure that the parser and aa-logprof parse
successfully the current set of shipped profiles. The system or other
parser and logprof can be passed in by overriding the PARSER and LOGPROF
variables.
```
$ cd profiles
$ make && make check
```
Stress Tests
------------
To run AppArmor stress tests:
```
$ make all
```
Use these:
```
$ ./change_hat
$ ./child
$ ./kill.sh
$ ./open
$ ./s.sh
```
Or run all at once:
```
$ ./stress.sh
```
Please note that the above will stress the system so much it may end up
invoking the OOM killer.
To run parser stress tests (requires /usr/bin/ruby):
```
$ ./stress.sh
```
(see stress.sh -h for options)
......@@ -214,7 +286,10 @@ https://scan.coverity.com/download?tab=cxx to obtain a pre-built copy of
cov-build.
To generate a compressed tarball of an intermediate Coverity directory:
```
$ make coverity
```
The compressed tarball is written to
apparmor-<SNAPSHOT_VERSION>-cov-int.tar.gz, where <SNAPSHOT_VERSION>
......
......@@ -89,6 +89,6 @@ L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), aa_is_enabled(2), and L<http://wiki.apparmor.net>.
apparmor(7), apparmor.d(5), aa_is_enabled(2), and L<https://wiki.apparmor.net>.
=cut
......@@ -88,6 +88,6 @@ L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
aa-stack(8), aa-namespace(8), apparmor(7), apparmor.d(5), aa_change_profile(3),
aa_change_onexec(3) and L<http://wiki.apparmor.net>.
aa_change_onexec(3) and L<https://wiki.apparmor.net>.
=cut
......@@ -140,6 +140,6 @@ them at L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), subdomain.conf(5), apparmor_parser(8), aa_change_hat(2) and
L<http://wiki.apparmor.net>.
L<https://wiki.apparmor.net>.
=cut
......@@ -138,7 +138,7 @@ my $ratelimit_saved = sysctl_read($ratelimit_sysctl);
END { sysctl_write($ratelimit_sysctl, $ratelimit_saved); }
sysctl_write($ratelimit_sysctl, 0);
UI_Info(gettext("\nBefore you begin, you may wish to check if a\nprofile already exists for the application you\nwish to confine. See the following wiki page for\nmore information:\nhttp://wiki.apparmor.net/index.php/Profiles"));
UI_Info(gettext("\nBefore you begin, you may wish to check if a\nprofile already exists for the application you\nwish to confine. See the following wiki page for\nmore information:\nhttps://gitlab.com/apparmor/apparmor/wikis/Profiles"));
UI_Important(gettext("Please start the application to be profiled in \nanother window and exercise its functionality now.\n\nOnce completed, select the \"Scan\" button below in \norder to scan the system logs for AppArmor events. \n\nFor each AppArmor event, you will be given the \nopportunity to choose whether the access should be \nallowed or denied."));
......@@ -195,7 +195,7 @@ for my $p (sort keys %helpers) {
}
UI_Info(gettext("Reloaded AppArmor profiles in enforce mode."));
UI_Info(gettext("\nPlease consider contributing your new profile! See\nthe following wiki page for more information:\nhttp://wiki.apparmor.net/index.php/Profiles\n"));
UI_Info(gettext("\nPlease consider contributing your new profile! See\nthe following wiki page for more information:\nhttps://gitlab.com/apparmor/apparmor/wikis/Profiles\n"));
UI_Info(sprintf(gettext('Finished generating profile for %s.'), $fqdbin));
exit 0;
......
......@@ -257,6 +257,6 @@ should be used.
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2),
aa_getcon(2) and
L<http://wiki.apparmor.net>.
L<https://wiki.apparmor.net>.
=cut
......@@ -204,6 +204,6 @@ separate processes should be used.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_hat(2) and
L<http://wiki.apparmor.net>.
L<https://wiki.apparmor.net>.
=cut
......@@ -146,6 +146,6 @@ L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
openat(2) and L<http://wiki.apparmor.net>.
openat(2) and L<https://wiki.apparmor.net>.
=cut
......@@ -115,6 +115,6 @@ L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), and
L<http://wiki.apparmor.net>.
L<https://wiki.apparmor.net>.
=cut
......@@ -132,6 +132,6 @@ 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>.
aa_splitcon(3) and L<https://wiki.apparmor.net>.
=cut
......@@ -160,6 +160,6 @@ L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
aa_features(3), openat(2) and L<http://wiki.apparmor.net>.
aa_features(3), openat(2) and L<https://wiki.apparmor.net>.
=cut
......@@ -123,6 +123,6 @@ 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>.
L<https://wiki.apparmor.net>.
=cut
......@@ -128,6 +128,6 @@ 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>.
and L<https://wiki.apparmor.net>.
=cut
......@@ -67,6 +67,6 @@ L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
aa_getcon(2) and L<http://wiki.apparmor.net>.
aa_getcon(2) and L<https://wiki.apparmor.net>.
=cut
......@@ -216,6 +216,6 @@ separate processes should be used.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2),
aa_getcon(2) and L<http://wiki.apparmor.net>.
aa_getcon(2) and L<https://wiki.apparmor.net>.
=cut
......@@ -68,7 +68,7 @@ extern int aa_is_enabled(void);
extern int aa_find_mountpoint(char **mnt);
/* Prototypes for self directed domain transitions
* see <http://apparmor.net>
* see <https://apparmor.net>
* Please see the change_hat(2) manpage for information.
*/
......
......@@ -12,6 +12,7 @@ LibAppArmor.pm: libapparmor_wrap.c
Makefile.perl: Makefile.PL LibAppArmor.pm
$(PERL) $< PREFIX=$(prefix) MAKEFILE=$@
sed -ie 's/LD_RUN_PATH="\x24(LD_RUN_PATH)"//g' Makefile.perl
sed -ie 's/^LD_RUN_PATH.*//g' Makefile.perl
LibAppArmor.so: libapparmor_wrap.c Makefile.perl
......
......@@ -5,7 +5,7 @@ setup(name = 'LibAppArmor',
version = '@VERSION@',
author = 'AppArmor Dev Team',
author_email = 'apparmor@lists.ubuntu.com',
url = 'http://wiki.apparmor.net',
url = 'https://wiki.apparmor.net',
description = 'AppArmor python bindings',
download_url = 'https://launchpad.net/apparmor/+download',
package_dir = {'LibAppArmor': '@srcdir@'},
......
......@@ -27,9 +27,9 @@ INSTALL_CONFDIR=${DESTDIR}${CONFDIR}
LOCALEDIR=/usr/share/locale
MANPAGES=apparmor.d.5 apparmor.7 apparmor_parser.8 subdomain.conf.5
YACC := /usr/bin/bison
YACC := bison
YFLAGS := -d
LEX := /usr/bin/flex
LEX := flex
LEXFLAGS = -B -v
WARNINGS = -Wall
EXTRA_WARNINGS = -Wsign-compare -Wmissing-field-initializers -Wformat-security -Wunused-parameter
......
......@@ -2,19 +2,6 @@ The apparmor_parser allows you to add, replace, and remove AppArmor
policy through the use of command line options. The default is to add.
`apparmor_parser --help` shows what the command line options are.
You can also find more information at http://wiki.apparmor.net
Please send all complaints, feature requests, rants about the software,
and questions to the apparmor@lists.ubuntu.com mailing list. Bug
reports can be filed against the AppArmor project on launchpad.net at
https://launchpad.net/apparmor or reported to the mailing list directly
for those who wish not to register for an account on launchpad.
Security issues can be filed as security bugs on launchpad
or directed to security@ubuntu.com. We will attempt to
conform to the RFP vulnerability disclosure protocol:
http://www.wiretrip.net/rfp/policy.html
Thanks.
You can also find more information at https://wiki.apparmor.net
-- The AppArmor development team
......@@ -55,7 +55,7 @@ B<VARIABLE> = '@{' I<ALPHA> [ ( I<ALPHANUMERIC> | '_' ) ... ] '}'
B<ALIAS RULE> = 'alias' I<ABS PATH> '-E<gt>' I<REWRITTEN ABS PATH> ','
B<INCLUDE> = '#include' ( I<ABS PATH> | I<MAGIC PATH> )
B<INCLUDE> = ( '#include' | 'include' ) [ 'if exists' ] ( I<ABS PATH> | I<MAGIC PATH> )
B<ABS PATH> = '"' path '"' (the path is passed to open(2))
......@@ -111,7 +111,7 @@ capabilities(7))
B<NETWORK RULE> = [ I<QUALIFIERS> ] 'network' [ I<DOMAIN> ] [ I<TYPE> | 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' | 'kcm' | 'smc' ) ','
B<DOMAIN> = ( 'unix' | 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'netlink' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'rds' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'llc' | 'ib' | 'mpls' | 'can' | 'tipc' | 'bluetooth' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' | 'kcm' | 'qipcrtr' | 'smc' | 'xdp' ) ','
B<TYPE> = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' | 'packet' )
......@@ -271,7 +271,7 @@ B<EXEC TRANSITION> = ( 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx' | 'Cx' | 'pix' |
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<LINK RULE> = I<QUALIFIERS> [ 'owner' ] 'link' [ 'subset' ] I<FILEGLOB> '-E<gt>' I<FILEGLOB>
B<ALPHA> = ('a', 'b', 'c', ... 'z', 'A', 'B', ... 'Z')
......@@ -1414,13 +1414,17 @@ rules into a rule block.
=head2 #include mechanism
AppArmor provides an easy abstraction mechanism to group common file
AppArmor provides an easy abstraction mechanism to group common
access requirements; this abstraction is an extremely flexible way to
grant site-specific rights and makes writing new AppArmor profiles very
simple by assembling the needed building blocks for any given program.
The use of '#include' is modelled directly after cpp(1); its use will
replace the '#include' statement with the specified file's contents.
The leading '#' is optional, and the '#include' keyword can be followed
by an option conditional 'if exists' that specifies profile compilation
should continue if the specified file or directory is not found.
B<#include "/absolute/path"> specifies that F</absolute/path> should be
used. B<#include "relative/path"> specifies that F<relative/path> should
be used, where the path is relative to the current working directory.
......@@ -1607,6 +1611,6 @@ negative values match when specifying one or the other. Eg, 'rw' matches when
apparmor(7), apparmor_parser(8), aa-complain(1),
aa-enforce(1), aa_change_hat(2), mod_apparmor(5), and
L<http://wiki.apparmor.net>.
L<https://wiki.apparmor.net>.
=cut
......@@ -70,9 +70,12 @@ with B<.> (except for the root B</>) so profiles are easier to manage
(e.g. the F</usr/sbin/nscd> profile would be named F<usr.sbin.nscd>).
Profiles are applied to a process at exec(3) time (as seen through the
execve(2) system call); an already running process cannot be confined.
However, once a profile is loaded for a program, that program will be
confined on the next exec(3).
execve(2) system call): once a profile is loaded for a program, that
program will be confined on the next exec(3). If a process is already
running under a profile, when one replaces that profile in the kernel,
the updated profile is applied immediately to that process.
On the other hand, a process that is already running unconfined cannot
be confined.
AppArmor supports the Linux kernel's securityfs filesystem, and makes
available the list of the profiles currently loaded; to mount the
......@@ -140,6 +143,56 @@ messages with the KERN facility. Thus, REJECTING and PERMITTING messages
may go to either F</var/log/audit/audit.log> or F</var/log/messages>,
depending upon local configuration.
=head1 DEBUGGING
AppArmor provides a few facilities to log more information,
which can help debugging profiles.
=head2 Enable debug mode
When debug mode is enabled, AppArmor will log a few extra messages to
dmesg (not via the audit subsystem). For example, the logs will tell
whether environment scrubbing has been applied.
To enable debug mode, run:
echo 1 > /sys/module/apparmor/parameters/debug
=head2 Turn off deny audit quieting
By default, operations that trigger C<deny> rules are not logged.
This is called I<deny audit quieting>.
To turn off deny audit quieting, run:
echo -n noquiet >/sys/module/apparmor/parameters/audit
=head2 Force audit mode
AppArmor can log a message for every operation that triggers a rule
configured in the policy. This is called I<force audit mode>.
B<Warning!> Force audit mode can be extremely noisy even for a single profile,
let alone when enabled globally.
To set a specific profile in force audit mode, add the C<audit> flag:
profile foo flags=(audit) { ... }
To enable force audit mode globally, run:
echo -n all > /sys/module/apparmor/parameters/audit
If auditd is not running, to avoid losing too many of the extra log
messages, you will likely have to turn off rate limiting by doing:
echo 0 > /proc/sys/kernel/printk_ratelimit
But even then the kernel ring buffer may overflow and you might
lose messages.
Else, if auditd is running, see auditd(8) and auditd.conf(5).
=head1 FILES
=over 4
......@@ -162,6 +215,6 @@ apparmor_parser(8), aa_change_hat(2), apparmor.d(5),
subdomain.conf(5), aa-autodep(1), clean(1),
auditd(8),
aa-unconfined(8), aa-enforce(1), aa-complain(1), and
L<http://wiki.apparmor.net>.
L<https://wiki.apparmor.net>.
=cut
......@@ -46,7 +46,7 @@ program. The B<profiles> may be specified by file name or a directory
name containing a set of profiles. If a directory is specified then the
B<apparmor_parser> will try to do a profile load for each file in the
directory that is not a dot file, or explicitly black listed (*.dpkg-new,
*.dpkg-old, *.dpkg-dist, *-dpkg-bak, *.repnew, *.rpmsave, *orig, *.rej,
*.dpkg-old, *.dpkg-dist, *-dpkg-bak, *.rpmnew, *.rpmsave, *orig, *.rej,
*~). The B<apparmor_parser> will fall back to taking input from standard
input if a profile or directory is not supplied.
......@@ -376,6 +376,6 @@ L<https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), subdomain.conf(5), aa_change_hat(2), and
L<http://wiki.apparmor.net>.
L<https://wiki.apparmor.net>.
=cut
......@@ -126,9 +126,10 @@ bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit,
/* create a dfa from the ruleset
* returns: buffer contain dfa tables, @size set to the size of the tables
* else NULL on failure
* else NULL on failure, @min_match_len set to the shortest string
* that can match the dfa for determining xmatch priority.
*/
void *aare_rules::create_dfa(size_t *size, dfaflags_t flags)
void *aare_rules::create_dfa(size_t *size, int *min_match_len, dfaflags_t flags)
{
char *buffer = NULL;
......@@ -150,6 +151,7 @@ void *aare_rules::create_dfa(size_t *size, dfaflags_t flags)
root = new AltNode(root, new CatNode(tmp, i->first));
}
}
*min_match_len = root->min_match_len();
/* dumping of the none simplified tree without -O no-expr-simplify
* is broken because we need to build the tree above first, and
......
......@@ -104,7 +104,7 @@ class aare_rules {
uint32_t audit, dfaflags_t flags);
bool add_rule_vec(int deny, uint32_t perms, uint32_t audit, int count,
const char **rulev, dfaflags_t flags);
void *create_dfa(size_t *size, dfaflags_t flags);
void *create_dfa(size_t *size, int *min_match_len, dfaflags_t flags);
};
#endif /* __LIBAA_RE_RULES_H */
......@@ -123,6 +123,19 @@ public:
virtual void compute_firstpos() = 0;
virtual void compute_lastpos() = 0;
virtual void compute_followpos() { }
/*
* min_match_len determines the smallest string that can match the
* syntax tree. This is used to determine the priority of a regex.
*/
virtual int min_match_len() { return 0; }
/*
* contains_null returns if the expression tree contains a null character.
* Null characters indicate that the rest of the DFA matches the xattrs and
* not the path. This is used to compute min_match_len.
*/
virtual bool contains_null() { return false; }
virtual int eq(Node *other) = 0;
virtual ostream &dump(ostream &os) = 0;
void dump_syntax_tree(ostream &os);
......@@ -257,6 +270,17 @@ public:
return os << c;
}
int min_match_len()
{
if (c == 0) {
// Null character indicates end of string.
return 0;
}
return 1;
}
bool contains_null() { return c == 0; }
uchar c;
};
......@@ -298,6 +322,24 @@ public:
return os << ']';
}
int min_match_len()
{
if (contains_null()) {
return 0;
}
return 1;
}
bool contains_null()
{
for (Chars::iterator i = chars.begin(); i != chars.end(); i++) {
if (*i == 0) {
return true;
}
}
return false;
}
Chars chars;
};
......@@ -346,6 +388,24 @@ public:
return os << ']';
}
int min_match_len()
{
if (contains_null()) {
return 0;
}
return 1;
}
bool contains_null()
{
for (Chars::iterator i = chars.begin(); i != chars.end(); i++) {
if (*i == 0) {
return false;
}
}
return true;
}
Chars chars;
};
......@@ -369,6 +429,8 @@ public:
return 0;
}
ostream &dump(ostream &os) { return os << "."; }
bool contains_null() { return true; }
};
/* Match a node zero or more times. (This is a unary operator.) */
......@@ -396,6 +458,8 @@ public:
child[0]->dump(os);
return os << ")*";
}
bool contains_null() { return child[0]->contains_null(); }
};
/* Match a node one or more times. (This is a unary operator.) */
......@@ -423,6 +487,8 @@ public:
child[0]->dump(os);
return os << ")+";
}
int min_match_len() { return child[0]->min_match_len(); }
bool contains_null() { return child[0]->contains_null(); }
};
/* Match a pair of consecutive nodes. */
......@@ -470,6 +536,22 @@ public:
return os;
}
void normalize(int dir);
int min_match_len()
{
int len = child[0]->min_match_len();
if (child[0]->contains_null()) {
// Null characters are used to indicate when the DFA transitions
// from matching the path to matching the xattrs. If the left child
// contains a null character, the right side doesn't contribute to
// the path match.
return len;
}
return len + child[1]->min_match_len();
}
bool contains_null()
{
return child[0]->contains_null() || child[1]->contains_null();
}
};
/* Match one of two alternative nodes. */
......@@ -507,6 +589,20 @@ public:
return os;
}
void normalize(int dir);
int min_match_len()
{
int m1, m2;
m1 = child[0]->min_match_len();
m2 = child[1]->min_match_len();
if (m1 < m2) {
return m1;
}
return m2;
}
bool contains_null()
{
return child[0]->contains_null() || child[1]->contains_null();
}
};
class SharedNode: public ImportantNode {
......
......@@ -171,13 +171,23 @@ extern int preprocess_only;
#ifdef DEBUG
#define PDEBUG(fmt, args...) fprintf(stderr, "parser: " fmt, ## args)
#define PDEBUG(fmt, args...) \
do { \
int pdebug_error = errno; \
fprintf(stderr, "parser: " fmt, ## args); \
errno = pdebug_error; \
} while (0)
#else
#define PDEBUG(fmt, args...) /* Do nothing */
#endif
#define NPDEBUG(fmt, args...) /* Do nothing */
#define PERROR(fmt, args...) fprintf(stderr, fmt, ## args)
#define PERROR(fmt, args...) \
do { \
int perror_error = errno; \
fprintf(stderr, fmt, ## args); \
errno = perror_error; \
} while (0)
#ifndef TRUE
#define TRUE (1)
......
......@@ -144,7 +144,7 @@ static int include_dir_cb(int dirfd unused, const char *name, struct stat *st,
return 0;
}
void include_filename(char *filename, int search)
void include_filename(char *filename, int search, bool if_exists)
{
FILE *include_file = NULL;
struct stat my_stat;
......@@ -161,11 +161,14 @@ void include_filename(char *filename, int search)
include_file = fopen(fullpath, "r");
}
if (!include_file)
if (!include_file) {
if (if_exists)
return;
yyerror(_("Could not open '%s'"),
fullpath ? fullpath: filename);
}
if (fstat(fileno(include_file), &my_stat))
if (fstat(fileno(include_file), &my_stat))
yyerror(_("fstat failed for '%s'"), fullpath);
if (S_ISREG(my_stat.st_mode)) {
......@@ -200,7 +203,7 @@ MODES {MODE_CHARS}+
WS [[:blank:]]
NUMBER [[:digit:]]+
ID_CHARS [^ \t\n"!,]
ID_CHARS [^ \t\r\n"!,]
ID {ID_CHARS}|(,{ID_CHARS}|\\[ ]|\\\t|\\\"|\\!|\\,)
IDS {ID}+
POST_VAR_ID_CHARS [^ \t\n"!,]{-}[=\+]
......@@ -257,6 +260,8 @@ LT_EQUAL <=
%x UNIX_MODE
%x CHANGE_PROFILE_MODE
%x INCLUDE
%x INCLUDE_EXISTS
%x ABI_MODE
%%
......@@ -269,23 +274,61 @@ LT_EQUAL <=
}
%}
<INITIAL,SUB_ID_WS,INCLUDE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
<INITIAL,SUB_ID_WS,INCLUDE,INCLUDE_EXISTS,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
{WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ }
}
<INCLUDE_EXISTS>{
(\<([^"\>\t\r\n]+)\>|{QUOTED_ID}) { /* <filename> | "filename" */
autofree char *filename = strndup(yytext, yyleng - 1);
include_filename(filename + 1, *filename == '<', true);
POP_NODUMP();
}
(\<{QUOTED_ID}\>) { /* <"filename"> */
autofree char *filename = strndup(yytext, yyleng - 2);
include_filename(filename + 2, true, true);
POP_NODUMP();
}
({IDS}|{QUOTED_ID}) { /* filename */
include_filename(yytext, 0, true);
POP_NODUMP();
}
}
<INCLUDE>{
(\<([^\> \t\n]+)\>|\"([^\" \t\n]+)\") { /* <filename> */
(\<([^"\>\t\r\n]+)\>|{QUOTED_ID}) { /* <filename> | "filename" */
autofree char *filename = strndup(yytext, yyleng - 1);
include_filename(filename + 1, *filename == '<');
include_filename(filename + 1, *filename == '<', false);
POP_NODUMP();
}
[^\<\>\" \t\n]+ { /* filename */
include_filename(yytext, 0);
(\<{QUOTED_ID}\>) { /* <"filename"> */
autofree char *filename = strndup(yytext, yyleng - 2);
include_filename(filename + 2, true, false);
POP_NODUMP();
}
({IDS}|{QUOTED_ID}) { /* filename */
include_filename(yytext, 0, false);
POP_NODUMP();
}
}
<ABI_MODE>{
(\<(([^"\>\t\r\n]+)|{QUOTED_ID})\>|{QUOTED_ID}|{IDS}) { /* <filename> | <"filename"> | "filename" | filename */
int lt = *yytext == '<' ? 1 : 0;
char *filename = processid(yytext + lt, yyleng - lt*2);
bool exists = YYSTATE == INCLUDE_EXISTS;
if (!filename)
yyerror(_("Failed to process filename\n"));
yylval.id = filename;
POP_AND_RETURN(TOK_ID);
}
}
<<EOF>> {
fclose(yyin);
pop_include_stack();
......@@ -527,6 +570,20 @@ LT_EQUAL <=
}
}
#include{WS}+if{WS}+exists/{WS}.*\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_EXISTS);
}
include{WS}+if{WS}+exists/{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_EXISTS);
}
#include/.*\r?\n {
/* Don't use PUSH() macro here as we don't want #include echoed out.
* It needs to be handled specially
......@@ -623,6 +680,9 @@ include/{WS} {
case TOK_UNIX:
state = UNIX_MODE;
break;
case TOK_ABI:
state = ABI_MODE;
break;
default: /* nothing */
break;
}
......@@ -675,4 +735,6 @@ unordered_map<int, string> state_names = {
STATE_TABLE_ENT(UNIX_MODE),
STATE_TABLE_ENT(CHANGE_PROFILE_MODE),
STATE_TABLE_ENT(INCLUDE),
STATE_TABLE_ENT(INCLUDE_EXISTS),
STATE_TABLE_ENT(ABI_MODE),
};
......@@ -759,7 +759,8 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
return errno;
}
} else {
pwarn("%s: cannot use or update cache, disable, or force-complain via stdin\n", progname);
if (write_cache)
pwarn("%s: cannot use or update cache, disable, or force-complain via stdin\n", progname);
}
reset_parser(profilename);
......@@ -898,8 +899,11 @@ do { \
work_sync_one(RESULT); \
} while (0)
/* returns -1 if work_spawn fails, not a return value of any unit of work */
#define work_spawn(WORK, RESULT) \
do { \
({ \
int localrc = 0; \
do { \
/* what to do to avoid fork() overhead when single threaded \
if (jobs == 1) { \
// no parallel work so avoid fork() overhead \
......@@ -936,11 +940,17 @@ do { \
fprintf(stderr, " JOBS SPAWN: created %ld ...\n", njobs); \
} else { \
/* error */ \
if (debug_jobs) \
fprintf(stderr, " JOBS SPAWN: failed error: %d) ...\n", errno); \
if (debug_jobs) { \
int error = errno; \
fprintf(stderr, " JOBS SPAWN: failed error: %d) ...\n", errno); \
errno = error; \
} \
RESULT(errno); \
localrc = -1; \
} \
} while (0)
} while (0); \
localrc; \
})
/* sadly C forces us to do this with exit, long_jump or returning error
......@@ -1017,11 +1027,15 @@ static int profile_dir_cb(int dirfd unused, const char *name, struct stat *st,
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)
if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0) {
PERROR(_("Out of memory"));
work_spawn(process_profile(option, cb_data->kernel_interface,
path, cb_data->cachedir),
handle_work_result);
handle_work_result(errno);
return -1;
}
rc = work_spawn(process_profile(option,
cb_data->kernel_interface,
path, cb_data->cachedir),
handle_work_result);
}
return rc;
}
......@@ -1035,11 +1049,15 @@ static int binary_dir_cb(int dirfd unused, const char *name, struct stat *st,
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)
if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0) {
PERROR(_("Out of memory"));
work_spawn(process_binary(option, cb_data->kernel_interface,
path),
handle_work_result);
handle_work_result(errno);
return -1;
}
rc = work_spawn(process_binary(option,
cb_data->kernel_interface,
path),
handle_work_result);
}
return rc;
}
......@@ -1124,7 +1142,7 @@ int main(int argc, char *argv[])
retval = aa_policy_cache_new(&policy_cache, features,
AT_FDCWD, cacheloc, max_caches);
if (retval) {
if (errno != ENOENT && errno != EEXIST) {
if (errno != ENOENT && errno != EEXIST && errno != EROFS) {
PERROR(_("Failed setting up policy cache (%s): %s\n"),
cacheloc, strerror(errno));
return 1;
......@@ -1156,11 +1174,14 @@ int main(int argc, char *argv[])
}
/* skip stdin if we've seen other command line arguments */
if (i == argc && optind != argc)
continue;
goto cleanup;
if (profilename && stat(profilename, &stat_file) == -1) {
last_error = errno;
PERROR("File %s not found, skipping...\n", profilename);
continue;
if (abort_on_error)
break;
goto cleanup;
}
if (profilename && S_ISDIR(stat_file.st_mode)) {
......@@ -1175,20 +1196,27 @@ int main(int argc, char *argv[])
cb = binary_input ? binary_dir_cb : profile_dir_cb;
if ((retval = dirat_for_each(AT_FDCWD, profilename,
&cb_data, cb))) {
last_error = errno;
PDEBUG("Failed loading profiles from %s\n",
profilename);
if (abort_on_error)
break;
}
} else if (binary_input) {
/* ignore return as error is handled in work_spawn */
work_spawn(process_binary(option, kernel_interface,
profilename),
handle_work_result);
} else {
/* ignore return as error is handled in work_spawn */
work_spawn(process_profile(option, kernel_interface,
profilename, cacheloc),
handle_work_result);
}
if (profilename) free(profilename);
cleanup:
if (profilename)
free(profilename);
profilename = NULL;
}
work_sync(handle_work_result);
......
......@@ -111,6 +111,7 @@ static struct keyword_table keyword_table[] = {
{"trace", TOK_TRACE},
{"tracedby", TOK_TRACEDBY},
{"readby", TOK_READBY},
{"abi", TOK_ABI},
/* terminate */
{NULL, 0}
......
......@@ -473,17 +473,13 @@ static int process_profile_name_xmatch(Profile *prof)
ptype = convert_aaregex_to_pcre(alt->name, 0,
glob_default,
tbuf, &len);
if (ptype == ePatternBasic)
len = strlen(alt->name);
if (len < prof->xmatch_len)
prof->xmatch_len = len;
if (!rules->add_rule(tbuf.c_str(), 0, AA_MAY_EXEC, 0, dfaflags)) {
delete rules;
return FALSE;
}
}
}
prof->xmatch = rules->create_dfa(&prof->xmatch_size, dfaflags);
prof->xmatch = rules->create_dfa(&prof->xmatch_size, &prof->xmatch_len, dfaflags);
delete rules;
if (!prof->xmatch)
return FALSE;
......@@ -679,8 +675,9 @@ int process_profile_regex(Profile *prof)
goto out;
if (prof->dfa.rules->rule_count > 0) {
int xmatch_len = 0;
prof->dfa.dfa = prof->dfa.rules->create_dfa(&prof->dfa.size,
dfaflags);
&xmatch_len, dfaflags);
delete prof->dfa.rules;
prof->dfa.rules = NULL;
if (!prof->dfa.dfa)
......@@ -815,7 +812,9 @@ int process_profile_policydb(Profile *prof)
goto out;
if (prof->policy.rules->rule_count > 0) {
prof->policy.dfa = prof->policy.rules->create_dfa(&prof->policy.size, dfaflags);
int xmatch_len = 0;
prof->policy.dfa = prof->policy.rules->create_dfa(&prof->policy.size,
&xmatch_len, dfaflags);
delete prof->policy.rules;
prof->policy.rules = NULL;
......
......@@ -152,6 +152,7 @@ void add_local_entry(Profile *prof);
%token TOK_TRACE
%token TOK_TRACEDBY
%token TOK_READBY
%token TOK_ABI
/* rlimits */
%token TOK_RLIMIT
......@@ -400,6 +401,7 @@ hat: hat_start profile_base
preamble: { /* nothing */ }
| preamble alias { /* nothing */ };
| preamble varassign { /* nothing */ };
| preamble abi_rule { /* nothing */ };
alias: TOK_ALIAS TOK_ID TOK_ARROW TOK_ID TOK_END_OF_RULE
{
......@@ -615,6 +617,8 @@ rules: { /* nothing */
$$ = prof;
};
rules: rules abi_rule { /* nothing */ }
rules: rules opt_prefix rule
{
PDEBUG("matched: rules rule\n");
......@@ -1065,6 +1069,12 @@ opt_named_transition: { /* nothing */ $$ = NULL; }
rule: file_rule { $$ = $1; }
| link_rule { $$ = $1; }
abi_rule: TOK_ABI TOK_ID TOK_END_OF_RULE
{
pwarn(_("%s: Profile abi not supported, falling back to system abi.\n"), progname);
free($2);
};
opt_exec_mode: { /* nothing */ $$ = EXEC_MODE_EMPTY; }
| TOK_UNSAFE { $$ = EXEC_MODE_UNSAFE; };
| TOK_SAFE { $$ = EXEC_MODE_SAFE; };
......
......@@ -147,13 +147,13 @@ int setup_cache_tmp(const char **cachetmpname, const char *cachename)
*cachetmpname = NULL;
if (write_cache) {
/* Otherwise, set up to save a cached copy */
if (asprintf(&tmpname, "%s-XXXXXX", cachename)<0) {
if (asprintf(&tmpname, "%s-XXXXXX", cachename) < 0) {
perror("asprintf");
exit(1);
return -1;
}
if ((cache_fd = mkstemp(tmpname)) < 0) {
perror("mkstemp");
exit(1);
return -1;
}
*cachetmpname = tmpname;
}
......
......@@ -113,6 +113,8 @@ skip_profile() {
local profile=$1
if [ "${profile%.rpmnew}" != "${profile}" -o \
"${profile%.rpmsave}" != "${profile}" -o \
"${profile%.orig}" != "${profile}" -o \
"${profile%.rej}" != "${profile}" -o \
-e "${PROFILE_DIR}/disable/`basename ${profile}`" -o \
"${profile%\~}" != "${