2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-09-01 14:55:10 +00:00

Compare commits

...

187 Commits

Author SHA1 Message Date
John Johansen
c52cf4d537 AppArmor 2.7 2011-12-15 04:20:50 -08:00
Steve Beattie
1e8dc4f375 This fixes the libapparmor log parsing library to properly parse log
entries where the comm entry has been hex-encoded. This occurs when the
binary being confined contains a space or other problematic character in
its filename. A test case is included.
2011-11-30 11:07:48 -08:00
Steve Beattie
7988124de5 Add a testcase for libapparmor for encoded profiles to ensure that
https://bugs.launchpad.net/apparmor/+bug/897957/ is in the utils and not
the parsing library itself.
2011-11-30 09:53:04 -08:00
Steve Beattie
f0aa65c832 abstractions/python: for some reason, the python runtimes need access
to pyconfig.h

Bug: https://launchpad.net/bugs/840734
2011-11-30 08:56:45 -08:00
Christian Boltz
1c96c09ec9 /usr/sbin/traceroute6 is a symlink to /usr/sbin/traceroute.
This patch allows "network inet6 raw" in addition to the ipv4 rule.

References: https://bugzilla.novell.com/show_bug.cgi?id=733312

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-11-30 13:15:21 +01:00
Christian Boltz
9685aae967 rpmlint complains about an outdated FSF address in parser/COPYING.GPL.
This commit upgrades the COPYING.GPL file to the latest version from
http://www.gnu.org/licenses/gpl-2.0.txt - which means some small text 
and whitespace changes and of course the correct address.

Acked-by: John Johansen <john.johansen@canonical.com>
2011-11-27 13:52:06 +01:00
John Johansen
c7a6608777 bzr tag apparmor_2.7.0-rc2 2011-11-10 09:59:36 -08:00
John Johansen
bca67d3d27 apparmor-2.7~rc2 2011-11-10 09:51:18 -08:00
John Johansen
9c39909a9c Author: Michael (kensington)
When executing apparmor_status from rc functions and utils are not installed, this message is received:

AppArmor is enabled,
Install the apparmor-utils package to receive more detailed
status information here (or examine directly).

Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-11-10 09:43:10 -08:00
John Johansen
f278505db2 Author: Michael (kensington)
parser/Makefile has a number of issues.

* Some warnings are produced: "make[1]: warning: jobserver unavailable: using -j1. Add `+' to parent make rule."
* CXX is not always respected
* LDFLAGS are not always respected

modified to apply and retain $(Q) by John Johansen

Signed-off-by: John Johansen <john.johansen@canoncial.com>
2011-11-10 09:36:52 -08:00
John Johansen
e91cc118df Author: Michael (kensington)
When running installing apparmor-utils from Makefile, some warnings are
generated: make[1]: warning: jobserver unavailable: using -j1. Add
 `+' to parent make rule.

Use $(MAKE) instead of make

Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-11-10 09:27:29 -08:00
John Johansen
e1763ba13c Auther: Michael (kensington)
libapparmor python bindings fail to build with python-3.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-11-10 09:22:09 -08:00
Seth Arnold
cad0c3c8a9 Include mknod in file operations aa-logprof / aa-genprof processing 2011-11-08 19:06:49 -08:00
Christian Boltz
49103b30f2 Make abstractions/winbind work on 64bit systems (valid.dat etc. are in
/usr/lib64/samba/ in openSUSE 64bit installations)

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-11-01 18:35:29 +01:00
Christian Boltz
df47a59ecb Allow loading the libraries used for Samba "vfs objects = ..."
References: https://bugzilla.novell.com/show_bug.cgi?id=725967

Signed-off-by: Christian Boltz <apparmor@cboltz.de>

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-11-01 18:28:49 +01:00
Christian Boltz
091ec763f9 split off abstractions/ldapclient from abstractions/nameservice
Original openSUSE changelog entry:

Thu Jan  6 16:23:19 UTC 2011 - rhafer@suse.de

- Splitted ldap related things from nameservice into separate
  profile and added some missing paths (bnc#662761)
2011-11-01 17:08:37 +01:00
Christian Boltz
33b7c5316f create the directory /etc/apparmor.d/disable which is required by aa-disable
Acked-by: John Johansen <john.johansen@canonical.com>
2011-10-20 00:23:19 +02:00
Christian Boltz
986093cf8d More helpful error message for "aa-notify -p" if a user is not member of
the group configured in notify.conf / use_group=...

Acked-by: John Johansen <john.johansen@canonical.com>
2011-10-20 00:20:02 +02:00
John Johansen
33c62acc5c Update version to 2.7~rc1 2011-10-12 16:45:45 -07:00
Christian Boltz
8b671f013a add the option --display to set the $DISPLAY environment variable in aa-notify.
If $DISPLAY is not set and --display is not used, aa-notify prints a 
warning that notifications won't be shown (exact warning text depends if 
using sudo or not).

Acked-by: John Johansen <john.johansen@canonical.com>
2011-10-12 13:08:25 +02:00
Christian Boltz
5789ab84a6 allow read access for /proc/*/mounts in the dovecot profile
Reported by Tim Edwards in the opensuse-factory	mailinglist.


Acked-by: John Johansen <john.johansen@canonical.com>
2011-10-12 13:05:00 +02:00
Christian Boltz
6d6e04dc12 fix a syntax error in remove_profiles() in rc.apparmor.functions by
adding a forgotten pipe.

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-10-12 00:45:11 +02:00
John Johansen
7a7c99f3a1 Commit the example parser.conf file that was supposed to be part of
commit r1834

Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-10-09 20:15:03 -07:00
John Johansen
9896f5edbd Add an example parser.conf file
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees@ubuntu.com>
2011-10-07 14:43:54 -07:00
John Johansen
12a98135bf Provide a more user friendly error message when cache is
requested and fails to be created.  Also don't make the
warning output conditional on the showcache flag as we
should be showing warning/errors by default.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-10-07 14:42:55 -07:00
Jamie Strandboge
a30dfb6b19 utils/aa-notify:
- set HOME (and DISPLAY) only once on startup to avoid NSS lookups

Acked-by: Jamie Strandboge <jamie@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
Acked-by: Christian Boltz <opensuse@cboltz.de>
2011-09-30 18:00:52 -05:00
Christian Boltz
a4d4eddd92 while aa-eventd is deprecated and no longer installed, parser/Makefile
happily continued to install the initscript for aa-eventd.

This was reported by Jiri Srain as part of 
https://bugzilla.novell.com/show_bug.cgi?id=720617

This commit removes the lines that install the aaeventd initscript
from parser/Makefile.

Acked-by: John Johansen <john.johansen@canonical.com>
2011-10-01 00:23:39 +02:00
Christian Boltz
b393893c7a sudo resets the environment variable $HOME and removes $DISPLAY on
openSUSE, which results in non-working desktop notifications in aa-notify
because $notify_exe is unable to connect to DBUS to display the message.

This patch sets the correct value for $HOME.

The code for setting $DISPLAY is still under discussion, therefore only
a TODO note is included in this commit for $DISPLAY.

Acked-By: John Johansen <john.johansen@canonical.com>
2011-09-24 13:19:10 +02:00
John Johansen
572fb7e943 AppArmor_2.7-beta2
Signed-off-by: John Johansen@canonical.com
2011-09-15 13:55:48 -07:00
John Johansen
7173d7a6a5 Remove extra space insert at from of ${TAG_VERSION} when doing the ~ to -
substitution.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-09-15 13:28:01 -07:00
John Johansen
7b577d72b5 Remove ~ based tags again
Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-09-15 12:50:10 -07:00
Christian Boltz
5a140c2e5c while debugging the issue in rc.apparmor.suse (see patch for
rc.apparmor.suse), I noticed that "rcapparmor restart" is
totally silent.

The attached patch prints a message in __apparmor_restart().

It also replaces the hardcoded "return 0" with $?. I'm quite sure this
won't catch all errors, but it's still better than the hardcoded success
message.

Acked-by: John Johansen <john.johansen@canonical.com>
2011-09-15 21:23:25 +02:00
Christian Boltz
e922cdb113 sbin.syslog-ng profile:
- fix permissions for additional-log-sockets.conf (the comma in {var/,} 
  was at the wrong place, which broke the /var/run/ case)
- add read permissions for /sys/devices/system/cpu/online
  (that was even new for Peter, but I trust him not to post faked 
  audit.log lines ;-)

Acked-by: John Johansen <john.johansen@canonical.com>
2011-09-15 21:21:57 +02:00
Christian Boltz
189da8236c the last patch to the usr.sbin.dnsmasq profile (r1758) contained a wrong
filename.

References: https://bugzilla.novell.com/show_bug.cgi?id=678749#c11

Acked-by: John Johansen <john.johansen@canonical.com>
2011-09-15 20:58:54 +02:00
Christian Boltz
2c62d802b6 It looks like rc.apparmor.functions renamed "aa_log_action_begin()" to
"aa_log_action_start()" (probably for 2.6), but the rc.apparmor.suse 
initscript was not updated.

This patch fixes the function name in the rc.apparmor.suse initscript.

References: https://bugzilla.novell.com/show_bug.cgi?id=717707

Acked-by: John Johansen <john.johansen@canonical.com>
2011-09-15 20:20:23 +02:00
Christian Boltz
23df761b70 Make the VENDOR_PERL directoy autodetected in utils/Makefile instead of
hardcoding it.

Acked-by: Kees Cook <kees@ubuntu.com>
2011-09-15 20:17:58 +02:00
Christian Boltz
6a80641ee2 perl modules don't need execute permissions.
This patch installs the perl modules with mode 644 instead of 755.

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-09-13 20:58:24 +02:00
Christian Boltz
02e1e94ab9 add a missing "make install" call to README in the libapparmor section.
Acked-by: John Johansen <john.johansen@canonical.com>
2011-09-12 22:30:17 +02:00
Jamie Strandboge
f28b91b8cf add gwenview to abstractions/ubuntu-browsers.d/multimedia. This is the
default image viewer in Kubuntu (LP: #840973)
2011-09-07 15:00:45 -05:00
Jamie Strandboge
6849615de6 adjust ubuntu-email abstraction for thunderbird rapid release process 2011-09-02 13:21:06 -05:00
John Johansen
743f84099d Allow passing of DEBUG from make into the compile
Fix the build so
    
  make DEBUG=1
    
results in a compile with DEBUG turned on.
    
Also fix build errors in the compile with DEBUG is defined
    
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees.cook@canonical.com>
2011-09-01 11:57:54 -07:00
John Johansen
a041b1738c Remove apparmor_2.6.0~rc1 and apparmor_2.6.0~rc1 tags so git can sync
yet again!

Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-08-31 16:15:44 -07:00
John Johansen
1056ef8418 Add missing documentation updates for aa_is_confined and aa_get_con.
This documentation should have been checked in as part of the patches
that added aa_is_confined and aa_get_con.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-08-31 16:07:55 -07:00
John Johansen
ac77e10a0f Fix missing pid_t dependency in apparmor.h
The define for pid_t is missing in apparmor.h so that if it is included
in programs that don't also include sys/types.h the compile will break.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees.cook@canonical.com>
2011-08-31 16:01:54 -07:00
John Johansen
eae6f0525c Add missing introspection regression test that should have been checked in
with the introspection patches.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-08-31 15:58:27 -07:00
John Johansen
341b7e61da Add compatibility patches for linux kernel 3.1 2011-08-27 12:56:53 -07:00
John Johansen
eb6d2224bd Updated kernel compatibilty patches for linux 3.0 2011-08-27 12:25:22 -07:00
Christian Boltz
bb1b5f986b Add permissions needed for Active Directory authentification to Samba
profiles.

References: https://bugzilla.novell.com/show_bug.cgi?id=713728

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-27 20:50:42 +02:00
Steve Beattie
1c1cc65cbd Update useradd and userdel profiles to use wutmp abstraction. 2011-08-26 18:58:31 -07:00
Steve Beattie
5dedd16bf5 Cleanup no longer necessary wutmp permission now that it's covered by
the wutmp abstraction.
2011-08-26 18:51:03 -07:00
Christian Boltz
66d51b575d From: Jeff Mahoney <jeffm@suse.com>
Subject: apparmor-profiles: Add samba config files
References: bnc#679182 bnc#666450

Signed-off-by: Jeff Mahoney <jeffm@suse.com>

- updated to match trunk
- added changed path to nmbd profile (/var/cache/samba has moved to 
  /var/lib/samba on (at least) openSUSE 11.4), bnc#679182#c8
  For backward compability, it also allows /var/spool/samba.
- Note: The smbd profile already contains both locations.
by Christian Boltz <apparmor@cboltz.de>

updated according to the comments from Steve Beattie
by Christian Boltz <apparmor@cboltz.de>

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-27 01:52:27 +02:00
Christian Boltz
f26df713c0 Dovecot profile update:
- allow /var/spool/mail, not only the /var/mail symlink
- allow @{HOME}/Mail/
- allow capability fsetid, read access to /etc/lsb-release and 
  SuSE-release and k for /var/{lib,run}/dovecot in usr.bin.dovecot

References:
- dovecot: Added support for /var/spool/mail (bnc#691072)
- Updated dovecot profile (bnc#681267).

Patch taken from openSUSE:11.4:Update:Test, file apparmor-profiles-dovecot
updated to match trunk by Christian Boltz <apparmor@cboltz.de>

Change compared to the patch posted to the ML:
- link rule instead of adding l permissions for /var/lib/dovecot and 
  /var/run/dovecot (as proposed by John Johansen)

Acked-By: John Johansen <john.johansen@canonical.com> on IRC
2011-08-27 01:12:10 +02:00
Steve Beattie
b8f486dee9 Attached is a patch to make the initscript not fail if /tmp is full
by converting the comm(1) usage on temporary files to an embedded
awk script. On both Ubuntu and OpenSUSE, a version of awk (mawk in
Ubuntu, gawk in OpenSUSE) is either a direct or indirect dependency
on the minimal or base package set, and the original reporter also
mentioned that an awk-based solution would be palatable in a way that
converting to bash, or using perl or python here would not be.

In the embedded awk script, I've tried to avoid gawk or mawk specific
behaviors or extensions; e.g. this is the reason for the call to sort
on the output of the awk script, rather than using gawk's asort(). But
please let me know if you see anything that shouldn't be portable
across awk implementations.

An additional issue that is fixed in both scripts is handling child
profiles (e.g. hats) during reload. If child profiles are filtered
out (via grep -v '//') of the list to consider, then on reloading
a profile where a child profile has been removed or renamed, that
child profile will continue to stick around. However, if the profile
containing child profiles is removed entirely, if the initscript
attempts to unload the child profiles after the parent is removed,
this will fail because they were unloaded when the parent was unloaded.
Thus I removed any filtering of child profiles out, but do a post-awk
reverse sort which guarantees that any child profiles will be removed
before their parent is. I also added the LC_COLLATE=C (based on the
Ubuntu version) to the sort call to ensure a consistent sort order.

To restate, the problem with the existing code is that it creates
temporary files in $TMPDIR (by default /tmp) and if that partition
is full, problems with the reload action ensue. Alternate solutions
include switching the initscript to use bash and its <$() extension
or setting TMPDIR to /dev/shm/. The former is unpalatable to some
(particularly for an initscript), and for the latter, /dev/shm is
only guaranteed to exist on GNU libc based systems (glibc apparently
expects /dev/shm to exist for its POSIX shared memory implementation;
see shm_overview(7)).  So to me, awk (sans GNU extensions) looks to
be the least bad option here.

Bug: https://launchpad.net/bugs/775785
2011-08-26 15:55:43 -07:00
Christian Boltz
ac7e66684c Add various capabilities to smbd that are needed for the way powerpoint
handles files. See bnc#705319 for details.

References: https://bugzilla.novell.com/show_bug.cgi?id=705319

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-26 23:19:30 +02:00
Steve Beattie
be3d2bc1a4 Update traceroute profile to include installed path on Ubuntu/Debian. 2011-08-25 15:36:30 -07:00
Christian Boltz
8f28eebe5a Add capability setuid and setgid to nscd profile. Needed by unscd
to switch to a non-root user. unscd is installed as /usr/sbin/nscd
at least at openSUSE.

Original changelog entry from unscd package:
Mon Sep  7 17:30:36 CEST 2009 - pbaudis[at]suse.cz
- Provide the /etc/apparmor.d/usr.sbin.nscd file and make it allow
  for change to the nobody user [bnc#535467]

Currently the nscd package from glibc and the unscd package both contain
a usr.sbin.nscd profile which needs to maintained/updated manually.
With this patch, the profile could be moved back to the
apparmor-profiles package.


Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-24 00:57:42 +02:00
Christian Boltz
d17a87bd28 bnc#685674 - The "-I" flag of traceroute is blocked by apparmor
* Do Apr 07 2011 jeffm@suse.de
- Add raw network access to traceroute profile (bnc#685674).

Acked-By: Steve Beattie <steve@nxnw.org>
2011-08-23 02:10:14 +02:00
Christian Boltz
59eb0af705 nscd: allow mkdir for /var/run/nscd/
References: https://bugzilla.novell.com/show_bug.cgi?id=647718

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-23 01:22:41 +02:00
Christian Boltz
7d5840d449 apparmor.vim:
- allow "deny [filename] x" rules
- add support for "make clean"

Acked-by: John Johansen <john.johansen@canonical.com>
2011-08-21 23:49:25 +02:00
John Johansen
fb7f0ddaaa Remove tags
apparmor_2.6.0~rc1
  apparmor_2.6.1~rc1

that have crept in yet again!!!
2011-08-19 16:57:30 -07:00
John Johansen
4bdc7ea51a From: Jeff Mahoney <jeffm@suse.com>
Subject: apparmor-utils: Add support for creds and path operations
References: bnc#564316

 2.6.29 introduced the path security_operations and credentials

 This patch adds support for those operations to the log parser.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>

Resolved merge conflict and removal operation already supported by
the log parser.
Acked-by: John Johansen <john.johansen@canonical.com>
2011-08-18 16:29:48 -07:00
Kees Cook
3ac9f7e676 Due to the Ubuntu multiarch project, internal paths to things keep
changing. Since we arguably shouldn't be hardcoding this kind of
thing, this changes a path around to use the C preprocessor to do the
work of finding the kernel definitions.

Signed-off-by: Kees Cook <kees.cook@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2011-08-18 18:26:02 -05:00
Kees Cook
843ef93202 As /var/log/messages has been phased out, start preferring syslog
instead.

Needed at least on upgraded Ubuntu machines that went from messages to
syslog recently. If this causes problems, we can easily revert it.

Signed-off-by: Kees Cook <kees.cook@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2011-08-18 18:17:22 -05:00
Kees Cook
00726ed51a The printk ratelimit needs to be disabled when running genprof via
syslog, otherwise audit events will get dropped.

This runs the risk of having the kernel log wrap around, but that
is a less common case that what this solves. This is a work-around
that will go away when complain info takes a different path in the
future.
2011-08-18 18:15:41 -05:00
Christian Boltz
0cbad5b99b Allow pux and PUx permissions in apparmor.vim
I intentionally don't allow pUx and Pux since the behaviour of those is 
very unexpected (the first letter decides if the environment is cleaned 
up or not - at least that's the result of the discussion in April) and 
the average user won't know this.

Acked-by: John Johansen <john.johansen@canonical.com>
2011-08-19 00:28:10 +02:00
Christian Boltz
d180252c52 This commit adds "capability syslog" to the syslogd and syslog-ng
profiles.

It also adds a comment to the klogd profile that capability sys_admin is 
only needed for backward compatibility with older kernels.

Acked-by: John Johansen <john.johansen@canonical.com>
2011-08-19 00:27:03 +02:00
Christian Boltz
ca5cd28d48 add syslog capability to apparmor.vim
Acked-by: John Johansen <john.johansen@canonical.com>
2011-08-19 00:24:48 +02:00
Christian Boltz
12e0d064cd openSUSE patch klog-needs-CAP_SYSLOG
This patch adds the syslog capability to parser/parser_misc.c
and to the sbin.klogd profile.
2011-08-18 23:54:24 +02:00
Steve Beattie
386a5abc7d logprof and genprof were creating Px and Cx execute permissions with
the modifiers as lowercase (meaning to pass on sensitive environment
variables to the exec'ed process) even if the user told them not to
when prompted. This patch fixes the issue.
2011-08-17 16:26:41 -07:00
Jamie Strandboge
084233a207 utils/aa-notify:
aa-notify would abort if it could not stat the logfile, as can happen
when using auditd and the directory perms for the logfile do not allow access
(x). Add raise_privileges() and drop_privileges() helper functions and adjust
get_logfile_size() and get_logfile_inode() to raise then drop privileges if the
logfile parent directory is not executable. Also adjust reopen_logfile() to use
these helpers.

When error checking in these helpers, use '$> == ...' instead of '$> = ... or
die...' since perl always dies when raising privs in this manner even though
the euid did change (and $!, $@, $^E, and $? are all the same). Not sure why
this is happening but the '==' check should be sufficient.
2011-08-17 13:47:52 -05:00
Jamie Strandboge
94e665b3fa utils/aa-notify:
aa-notify would abort if it could not stat the logfile, as can happen
when using auditd and the directory perms for the logfile do not allow access
(x). Add raise_privileges() and drop_privileges() helper functions and adjust
get_logfile_size() and get_logfile_inode() to raise then drop privileges if the
logfile parent directory is not executable. Also adjust reopen_logfile() to use
these helpers.

When error checking, use '$> == ...' instead of '$> = ... or die...' since perl
always dies when raising privs in this manner even though the euid did change
(and $!, $@, $^E, and $? are all the same). Not sure why this is happening but
the '==' check should be sufficient.
2011-08-17 09:48:12 -05:00
Jamie Strandboge
0a5c4fa159 utils/aa-notify:
- drop supplemental group privileges too. While POSIX::setgid() works nice in
  that it will set both the real uid and euid, it doesn't do anything with the
  supplemental groups (sigh). Instead, assign to $( and $) in a manner that
  clears the supplemental groups.
2011-08-17 08:35:52 -05:00
Jamie Strandboge
9fddd7a716 utils/aa-notify.pod: update to clarify '-u' argument when using '-p' 2011-08-17 07:49:00 -05:00
Steve Beattie
92473dfc46 This patch adds multiarch support for the X DRI paths.
Bug: https://launchpad.net/bugs/826914
2011-08-16 22:47:34 -07:00
Steve Beattie
8ee0292d8e This patch fixes up a conflicting execute permission that was added
in merging in the opensuse fixes to the dhclient profile. It does so
by merging them, using the profile for dhclient-script if it exists
and inheriting dhclient's profile if it does not.
2011-08-16 16:35:57 -07:00
Christian Boltz
174105b9fa From: Jeff Mahoney <jeffm@suse.com>
Subject: apparmor-utils: Fix handling of files in /
References: bnc#397883

 The separate handling of files and directories with realpath is broken.

 For files e.g. /foo, $dir ends up being empty since the / is eaten by
 the regex. realpath resolves an empty argument as the current directory,
 resulting in an incorrect path.

 There's no explanation of why the separate handling was used in the
 first place.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-17 00:06:07 +02:00
Christian Boltz
7f45708c86 Merge k permission for /var/log/lastlog into abstractions/wutmp
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-16 12:26:44 +02:00
Christian Boltz
015229d551 From: Jeff Mahoney <jeffm@suse.com>
Subject: Fix for sshd profile
References: bnc#457072

 Without this patch, sshd won't work in enforce mode.

 libselinux accesses /proc/filesystems to determine if it's enabled
 bash won't execute
 audit_control is probably from libselinux too

Updated by Christian Boltz <apparmor@cboltz.de>:
- add /proc/*/oom_adj and oom_score_adj rw
- add /var/log/btmp r
- add /var/log/lastlog k
- removed capability sys_ptrace - doesn't seem to be needed
- changed all login shells to rUx, not only bash
- removed /proc/filesystems (already part of abstractions/base)

Acked-By: John Johansen <john.johansen@canonical.com>
with the following note:
 ACK because I don't see a choice right now but for the 3.0 release
 (next year) I'll ask you to retest and add newer audit controls.
2011-08-13 22:43:33 +02:00
Christian Boltz
267e9610c7 openSUSE patch to remove the "-f" parameter from startproc in rc.aaeventd.suse /
start_aa_event().

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-13 14:22:35 +02:00
Christian Boltz
32797f5780 openSUSE patch to add systemd support to rc.apparmor.functions
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-13 14:15:58 +02:00
Christian Boltz
ce85512673 openSUSE patch with some changes in the initscripts.
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-13 14:13:49 +02:00
Christian Boltz
5f630f2c5d From: Jeff Mahoney <jeffm@suse.com>
Subject: apparmor-utils: Add check_for_apparmor helper.

 This should be an alias but those get complicated quickly in perl.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-13 14:11:20 +02:00
Christian Boltz
4fc9106d37 From: Jeff Mahoney <jeffm@suse.com>
Subject: apparmor-utils: setprofileflags() drops leading whitespace
References: bnc#480795

 setprofileflags() drops leading whitespace for subprofiles. writeheader()
 properly indents subprofiles 2 spaces per nesting level but when
 genprof sets the profile to enforce mode at completion, the whitespace
 is removed.

 This patch adds the whitespace globbing to the regexp and uses it to
 prefix the sub-profile with the correct spacing.

 Reported at: https://bugzilla.novell.com/show_bug.cgi?id=480795

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-13 14:09:53 +02:00
Christian Boltz
614c051a71 moved rules for /var/lib/libvirt/dnsmasq/ in dnsmasq profile to one place
Pre-Acked by Steve Beattie <sbeattie@ubuntu.com>
2011-08-12 22:14:00 +02:00
Kees Cook
94f06d23c0 add missing libraries/libapparmor/doc/aa_find_mountpoint.pod from https://lists.ubuntu.com/archives/apparmor/2011-July/001280.html 2011-08-10 22:52:05 -07:00
Kees Cook
a8e0b0d0a3 add missed libraries/libapparmor/doc/aa_getcon.pod from https://lists.ubuntu.com/archives/apparmor/2011-July/001282.html 2011-08-10 22:49:16 -07:00
John Johansen
0f3263f710 Gah really make it beta1 instead of RC1 2011-08-10 15:56:17 -07:00
John Johansen
2a81cadd27 Update version to 2.7-beta1. No new feature except maybe pending
feature freeze exception for dbus mediation.
2011-08-10 15:54:51 -07:00
John Johansen
f69822107f Update apparmor's handling of rlimits for cpu limit and more natural units
Allow for rlimit cpu to specified which is now supported by the kernel.

Previously the rlimit units where limited to K, M, G and would fail when
KB, MB, GB where used.  Allow for both, also allow for units on lengths
of time, by specifying "seconds", "minutes", "hours".. or any unique subset
eg. "s", "sec", "m", "min", "h", "hour" ..

NOTE:
This patch does not extend rlimits to be able to handle setting of tasks
that are confined by other profiles.

 Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-08-10 15:53:39 -07:00
John Johansen
4dec6cab65 Add the ability for the parser to have a basic conf file, that defaults
to /etc/apparmor/parser.conf (NOTE option to allow changing this is not
provided currently).

Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-08-09 06:52:43 -07:00
John Johansen
0287ce0c9d Function to test if apparmor support is enabled.
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees.cook@canonical.com>
2011-08-09 06:48:56 -07:00
John Johansen
aae597bfde Library function to find the apparmorfs filesystem mount point
Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-08-09 06:48:17 -07:00
John Johansen
8347fb69c2 Library interface for tasks introspecting confinement.
Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-08-09 06:47:40 -07:00
John Johansen
685632db0c Add the ability to read proc attr interfaces
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees.cook@canonical.com>
2011-08-09 06:45:51 -07:00
Christian Boltz
5169b4556a Replace the last occurrence of /var/run with /{,var/}run/
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-09 12:20:50 +02:00
Steve Beattie
3f099da257 .bzrignore: ignore the generated testfiles in the parser testsuite, all
25000+ of them. I swore we used to have this already.
2011-08-09 01:17:42 -07:00
Steve Beattie
d4c4cbe62b It's possible that git doesn't like to create empty directories, so
create the generated_* directories themselves if they don't exist before
running the script to generate them.

Also modify the default invocation of prove to add -f, which reports the
details of failing test cases.
2011-08-09 01:10:19 -07:00
Steve Beattie
c66975ffc5 Make parser's 'tests' target depend on the apparmor_parser binary having
been generated first; otherwise, 'make clean tests' fails.
2011-08-09 00:54:14 -07:00
Christian Boltz
c35ac067b2 My last commit (r1759) accidently did not include
/{,var/}run/cups/ rw,
Adding it now.
2011-08-09 00:42:38 +02:00
Christian Boltz
851a8c8b3a Various cupsd profile fixes/additions
Changed /var/run/cups/** rw, to
  /{,var/}run/cups/ rw,
  /{,var/}run/cups/** rw,
as requested by Steve Beattie

With this change:
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-08 23:18:54 +02:00
Christian Boltz
d081f7cb85 From: Jeff Mahoney <jeffm@suse.com>
Subject: dnsmasq: Profile fixes
References: bnc#666090 bnc#678749

Signed-off-by: Jeff Mahoney <jeffm@suse.com>

Updated to match master by
Christian Boltz <apparmor@cboltz.de>

Updated for systemd (/{,var/},run/ instead of /var/run/) by
Christian Boltz <apparmor@cboltz.de> as requested by Steve Beattie

With this change:
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
(final confirmation on IRC in #apparmor)
2011-08-08 23:13:15 +02:00
Christian Boltz
2c5534ca18 syslog-ng profile fixes/additions
updated to match master by
Christian Boltz <apparmor@cboltz.de>

updated to work with systemd (/{,var/}run/ instead of /var/run)
Christian Boltz <apparmor@cboltz.de> as requested by Steve Beattie

With this change:
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-08 22:59:28 +02:00
Christian Boltz
08fb58e10d From: Jeff Mahoney <jeffm@suse.com>
Subject: profiles: update dhclient
References: bnc#561152

Signed-off-by: Jeff Mahoney <jeffm@suse.com>

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-08 22:54:23 +02:00
Christian Boltz
13c3e40044 From: Jeff Mahoney <jeffm@suse.com>
Subject: dhcpd: Fix apparmor profile
References: bnc#692428

 This patch adds the network rules needed, corrects the path to dhcpd.leases,
 and adds the path for TSIG DNS keys.

Reported-by: Andrew Beames <suseforum@roocomputing.co.uk>
Signed-off-by: Jeff Mahoney <jeffm@suse.com>

updated to match trunk by
Christian Boltz <apparmor@cboltz.de>

updated to use
    /var/lib/dhcp/{db/,}dhcpd.leases*    rwl,
(instead of just /var/lib/dhcp/db/dhcpd.leases* rwl) to keep the profile
Ubuntu-compatible as requested by Steve Beattie.

With this change:
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-08 22:29:45 +02:00
Christian Boltz
b5e525b251 From: Jeff Mahoney <jeffm@suse.com>
Subject: profiles: Add openssl abstraction
References: bnc#623886

 Profiles that use openssl have been adding the openssl files piecemeal.

 This patch creates a new openssl abstraction that can be inherited by
 all profiles that use it.


Signed-off-by: Jeff Mahoney <jeffm@suse.com>

Patch for 
- profiles/apparmor.d/abstractions/ssl_certs 
- profiles/apparmor/profiles/extras/usr.sbin.httpd2-prefork (second chunk)
updated by Christian Boltz <apparmor@cboltz.de>
(didn't apply to trunk)

Acked-By: Steve Beattie <sbeattie@ubuntu.com>

Copyright header in profiles/apparmor.d/abstractions/openssl added by
Christian Boltz <apparmor@cboltz.de>
2011-08-08 22:22:03 +02:00
Christian Boltz
663698c7a6 From: Jeff Mahoney <jeffm@suse.com>
Subject: profile: ntpd -N needs sys_nice
References: bnc#657054

 ntpd -N allows the administrator to increase or decrease priority of the
 ntp server. Since the profile doesn't allow it, the operation is denied.

 This patch adds support for that operation.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-08-08 22:16:06 +02:00
Christian Boltz
957f1ae00f Fix syntax error (introduced in r1751) 2011-08-06 01:35:37 +02:00
Steve Beattie
763855fe9e From: Christian Boltz <apparmor@cboltz.de>
Based on https://bugzilla.novell.com/show_bug.cgi?id=643387

Update man profile and abstraction.
2011-08-05 13:12:35 -07:00
Steve Beattie
e6880f9584 Bug: https://bugs.launchpad.net/bugs/794974
Add openal config support to abstractions/audio.

Bug: https://launchpad.net/bugs/794974
2011-08-04 16:52:45 -07:00
Steve Beattie
786f592d7e From: Arkadiusz Miskiewicz <arekm@maven.pl>
Actually skip loading profiles that we report we're skipping.
2011-08-04 16:20:26 -07:00
Jamie Strandboge
38d83c3c76 fix LP: #815883 2011-07-27 09:30:18 -05:00
Jamie Strandboge
7897ba2293 fix LP: #815883
- update dnsmasq profile for new libvirt lease file path
2011-07-25 08:28:04 -05:00
John Johansen
a43931e987 Update the man page for change_hat/change_profile
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees.cook@canonical.com>
2011-07-22 05:20:06 -07:00
John Johansen
2fec4eb2b4 Make setprocattr more abstract so it will work for any pid
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees.cook@canonical.com>
2011-07-21 11:10:35 -07:00
John Johansen
3f59259dec Abstract out the construction of proc path generation more.
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees.cook@canonical.com>
2011-07-21 11:09:22 -07:00
John Johansen
2c110ed3d5 Abstract the libraries getting of the tid
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Kees Cook <kees.cook@canonical.com>
2011-07-21 11:06:57 -07:00
Marc Deslauriers
0d9a39bd26 This adds a blurb about the new profiles repository to aa-genprof,
along with a link to the wiki page. This helps users locate profiles
that possibly already exist for the application they are attempting to
confine, and suggests they contribute the profile when they're done.
2011-07-18 10:34:49 -04:00
Jamie Strandboge
5e273b0892 update profiles/apparmor.d/abstractions/audio to fixup shm patch from
yesterday. Thanks to Christian Boltz.
2011-07-15 13:53:35 -05:00
Jamie Strandboge
e91c83964a adjustments for /var/run -> /run, /var/lock -> /run/lock and /dev/shm ->
/run/shm transition (LP: #810270)
2011-07-14 11:29:54 -05:00
Jamie Strandboge
35acee98d5 and last one for /dev/shm to /run/shm 2011-07-14 08:21:01 -05:00
Jamie Strandboge
8434463621 make a similar change for /var/lock 2011-07-14 08:09:19 -05:00
Jamie Strandboge
20f117500f update for /var/run -> /run udev transition. For compatibility, distributions
(eg Ubuntu) are providing a symlink from /var/run to /run, so our profiles
should handle both situations.
2011-07-14 07:57:57 -05:00
Jamie Strandboge
850bce2754 Add /usr/share/gnome/applications/mimeinfo.cache to gnome abstraction 2011-07-12 15:24:52 -05:00
Jamie Strandboge
7c05b9ed91 profiles/apparmor.d/abstractions/gnome: add read access to
/usr/share/gnome/applications/mimeinfo.cache, which is now needed by
 Gnome applications.
2011-07-12 07:38:17 -05:00
John Johansen
5abb79d5be Add documentation directory and start of the dfa variables documentation
Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-06-27 10:32:04 +01:00
John Johansen
70a06835b8 Remove tags with ~ in them
Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-06-27 10:24:08 +01:00
Jamie Strandboge
2e73225586 allow owner read access to /var/run/lightdm/authority/[0-9]* in the X
abstraction
2011-06-22 22:26:14 -05:00
Jamie Strandboge
65c7473407 profiles/apparmor.d/abstractions/ssl_certs: allow access to
/usr/local/share/ca-certificates
2011-06-22 16:42:22 -05:00
John Johansen
901e67a83f Rename tags
apparmor_2.6.1~rc1 to apparmor_2.6.1-rc1
  apparmor_2.6.0~rc1 to apparmor_2.6.0-rc1

to be consistent with the new format and to enable mirroring to a git
repository
2011-06-02 21:08:42 -07:00
Steve Beattie
33c615e5f9 This patch adjusts the tag make target to use a separate version with
'~' replaced by '-'. This is needed for mirroring to git as git can't
handle '~'s embedded in tag or branch names.

Tested by setting up a separate tag_version target like so:

tag_version:
	echo ${TAG_VERSION}
2011-06-02 18:54:56 -07:00
Steve Beattie
fdae9784f4 Bug: https://bugs.launchpad.net/apparmor/+bug/788616
This patch fixes the init scripts helper functions file to
filter out the hat/child process separator as currently used
by the parser, '//' rather than what used to be used, the '^'
symbol. This fixes bugs where profiles that covered regexs (e.g.
'/usr/lib/firefox-4.0.1/firefox{,*[^s][^h]}') and thus were being
improperly filtered away and unloaded when reloading apparmor policy.
2011-06-01 07:52:35 -07:00
Kees Cook
25f7aa6621 adjust documentation "release" name to match other manpages.
Signed-off-by: Kees Cook <kees.cook@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-05-27 15:04:42 -07:00
Kees Cook
156a980c30 In some cases, it is desirable to build the parser without building the
binary portions (Hurd). This patch splits up the build targets so this is
possible:

"main" becomes "arch"

"indep" is created and depends on "docs"
po building is moved from "main" to "indep"

"all" has "tests" removed (standard build practices are to "make" then
"make check" so I think "tests"/"check" should stay separate from "all").

redundant chunk is removed (this exists twice in the Makefile):
-.SILENT: check
-check: tests

"install" is split into "install-indep" and "install-arch"

"install-arch" requires "arch" and only installs the binaries

"install-indep" requires "indep" and only install non-binaries

Additionally, update the README to mention the "check" target both for the
parser and the utils.

Signed-off-by: Kees Cook <kees.cook@canonical.com>
Acked-by: Steve Beattie <steve@nxnw.org>
2011-05-27 14:57:43 -07:00
Kees Cook
e9d5d491b8 define PATH_MAX when missing from limits.h
Some non-Linux systems do not define PATH_MAX (Hurd). Since I have no
interest in supporting a fully dynamic PATH_MAX in AppArmor, work around
this by just defining a static value that matches Linux's limits.h value.

Signed-off-by: Kees Cook <kees.cook@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-05-27 14:20:03 -07:00
Marc Deslauriers
b9bfc10d0f - added new aa-status tool rewritten in Python
- moved old Perl aa-status tool to the deprecated directory
2011-05-27 15:08:50 -04:00
Steve Beattie
12876afa07 This patch fixes up a bit that leaked into kees' commit rev 1727
to support a top level make clean target.
2011-05-23 16:05:10 -07:00
Kees Cook
e66e56b020 Add pending local commits. 2011-05-23 11:30:11 -07:00
Kees Cook
6a7a20da88 include explicit parser_common.o rule, thanks to Steve Beattie 2011-05-23 11:29:41 -07:00
Kees Cook
35cdeebe19 fix sprintf arguments 2011-05-23 11:28:26 -07:00
Steve Beattie
c2fdcf7d39 This patch makes it possible to override the default set of language
translations to be built via the LANGS make argument whitelist. For
example:

  cd parser; make all install "LANGS=en_US fr"

will build and install the en_US and fr .mo files for the parser.
2011-05-20 13:34:29 -07:00
John Johansen
627638a6cf Add debugging dump for DFA partition minimization
Allow dumping out which states where dropped during partition minimization
and which state became the partitions representative state.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-05-20 09:26:44 -07:00
John Johansen
414e5bf560 Fix the dfa-graph dump
The dfa graph dump was broken by previous dfa cleanups so that the graph
transition target is the output of a pointer instead of the dfa state
number.
    
Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-05-20 09:24:40 -07:00
John Johansen
343749dc26 Add kernel patches for 2.6.39 2011-05-19 10:57:20 -07:00
Kees Cook
6a68aa2ecb [v2: added clean-ups, backed off on some of the build silencing]
This is a rather large rearrangement of how a subset of the parser global
variables are defined. Right now, there are unit tests built without
linking against parser_main.c. As a result, none of the globals defined in
parser_main.c could be used in the code that is built for unit tests
(misc, regex, symtab, variable). To get a clean build, either stubs needed
to be added to "#ifdef UNIT_TEST" blocks in each .c file, or we had to
depend on link-time optimizations that would throw out the unused routines.

First, this is a problem because all the compile-time warnings had to be
explicitly silenced, so reviewing the build logs becomes difficult on
failures, and we can potentially (in really unlucky situations) test
something that isn't actually part of the "real" parser.

Second, not all compilers will allow this kind of linking (e.g. mips gcc),
and the missing symbols at link time will fail the entire build even though
they're technically not needed.

To solve all of this, I've moved all of the global variables used in lex,
yacc, and main to parser_common.c, and adjusted the .h files. On top of
this, I made sure to fully link the tst builds so all symbols are resolved
(including aare lib) and removedonly  tst build-log silencing (for now,
deferring to another future patchset to consolidate the build silencing).

Signed-off-by: Kees Cook <kees.cook@canonical.com>
2011-05-13 02:12:49 -07:00
Jamie Strandboge
70bb296a70 profiles/apparmor.d/abstractions/dbus-session: Per discussion with John
Johansen, use 'ix' instead of 'Pix' for dbus-launch since if someone happens to
define a profile for dbus-launch and it is loosely confined, then users of this
abstraction could end up launching a program via dbus-launch in a less confined
manner than intended. This sort of thing should not be possible via an
abstraction (and people are always free to profile using Pix if they prefer).
2011-05-09 18:09:24 +02:00
Kees Cook
a25a2ec0d9 On Hurd and kfreebsd under Debian, I have disabled the build of the parser
since things go extremely badly when capabilities disappear. If someone
wants to work on it, I have some initial patch attempts, but it was getting
too time-consuming, so I back-burnered the parser. A very small change was
needed to get the libraries to build, and this is it.

Description: Workaround non-Linux environments to build everything but the
 parser.
Author: Kees Cook <kees@debian.org>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-05-02 13:38:12 -07:00
Kees Cook
1644ce31e7 Description: Improve generated test readability and build-time cleanup.
Author: Kees Cook <kees@debian.org>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-05-02 13:36:55 -07:00
Kees Cook
abcf66292d Description: adjust for missing or incorrect includes.
Author: Kees Cook <kees@ubuntu.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-05-02 13:34:58 -07:00
Kees Cook
42a5483ba9 Description: fix minor manpage type pointed out by lintian.
Author: Kees Cook <kees@debian.org>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-05-02 13:33:31 -07:00
Jamie Strandboge
83282f8700 add kwallet to ubuntu-browsers.d/user-files 2011-04-19 06:03:35 -05:00
Jamie Strandboge
8485c8e417 profiles/apparmor.d/abstractions/private-files:
- add zsh files (LP: #761217)
- add .inputrc (bash)
- add .login and .logout (csh, tcsh, etc)
2011-04-19 05:55:32 -05:00
Kees Cook
4670afee7c Nothing in deprecated/ should be built any more, much less shipped in the
tarball. Let's drop this from future tarball creation.

Signed-off-by: Kees Cook <kees.cook@canonical.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2011-04-18 18:09:29 -07:00
Jamie Strandboge
e946b88d82 ubuntu-browsers.d/multimedia: add f-spot, shotwell and digikam to image viewers
since they are invoked when using sites such as Facebook
2011-04-18 09:32:14 -05:00
Jamie Strandboge
780ae4663d profiles/apparmor.d/abstractions/private-files:
- add zsh files (LP: #761217)
- add .inputrc (bash)
- add .login and .logout (csh, tcsh, etc)
2011-04-18 08:55:50 -05:00
Christian Boltz
34676334b6 added some missing permissions to profiles/extra
ACKed by Kees Cook
2011-04-12 00:10:41 +02:00
Christian Boltz
d799edbcc6 create-apparmor.vim.sh:
- move cap_sys_module and cap_sys_rawio to "dangerous" capabilities
- sorted sdKapKeyDanger

Proposed by Seth Arnold,
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-04-09 18:47:42 +02:00
Jamie Strandboge
00f32d555d adjust ubuntu-media-players abstraction to allow (LP: #750381):
- reading of configs required by gnash
- owner writing of @{HOME}/.gnash
2011-04-07 09:50:20 -05:00
Steve Beattie
b7a8a01ac7 This patch fixes a compilation warning that was trying to tell us
about an actual bug in the parser; namely that when handling strings
encapsulated in quotes, that our handling of octals is busted. It
fixes this by fixing the case entries so that 3 digit octals will
get parsed correctly, rather than dropped.

It also adds a bunch of unit tests for the processquoted() function.
2011-04-05 20:55:19 -07:00
Steve Beattie
3a8546732a This patch fixes warnings emitted by the compiler when compiling on a
32bit arch, due to size_t objects being passed to fprintf with format
strings expecting longs. It does this by adjusting the fprintf rules
to expect size_t objects.
2011-04-05 20:53:35 -07:00
Steve Beattie
d656afa1d5 This patch fixes the parser's dfa generation library makefile to use
the default compilation rules when compiling C++ files, so that things
like CFLAGS et al will be honored. Without this, doing 'make DEBUG=y'
in the parser/ tree will not have its added -pg flag honored, breaking
profiling of the parser.
2011-04-05 20:51:02 -07:00
Christian Boltz
5b3190d39c Add files to generate apparmor.vim to bzr.
Basically the files will generate apparmor.vim as included in openSUSE 
11.4 (and posted here before at the end of january). The only difference 
is that the patch that Steve posted some days ago is already included 
(patch summary: sdGlob: first character of variable name has to be 
 :alpha:, followed by any number of :alnum: or _)
2011-04-05 23:56:14 +02:00
Christian Boltz
c5dca95504 apparmor.d.pod:
- fix ALPHANUMERIC to include 0...9 instead of 1...9
- enforce linebreaks for network example rules

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-04-01 20:35:14 +02:00
Steve Beattie
dce1d5d5e6 Bah, fix up screwed up testcase. 2011-03-29 02:46:09 -07:00
Steve Beattie
bb31faff1b This patch extends the stress profile generator to add variable
definition and references.
2011-03-28 21:07:44 -07:00
Steve Beattie
162b49b417 This patch attempts to address the over-aggressive behavior of the
stress tests on the parser, by dropping the maximum number of rules
each profile can have, as well as reducing the number of profiles to
generate by default to 50. It also cleans up the emitted profiles
a little, creates the profile names with the suffix .sd [1], fixes
stress.sh to actually honor the -p (alternate parser) argument, fixes
the profile flags generation to not generate duplicates flags, and
fixes the file rules to always start with a constant randomly-generated
prefix element (rather than a regex or variable) to greatly reduce
the possibility of X dominance collisions in the parser
2011-03-28 20:48:30 -07:00
Steve Beattie
f8b43d5ba9 The parser's lexer supports variables defined matching the regex
'[[:alpha:]][[:alnum:]_]*' (i.e. a single alpha followed by any number
of alphanumerics or underscores). Unfortunately, the code that expends
variables inside a profile does not match this, it incorrectly matched
'([[:alpha:]]|_)+' (one or more alphas or underscores). This patch
corrects the behavior there as well as synchronizing the expected
variable names in the apparmor.d manpage and apparmor.vim syntax file.

It also adds unit tests and testcases to verify the behavior.

Signed-off-by: Steve Beattie <sbeattie@ubuntu.com>
2011-03-28 10:52:02 -07:00
Steve Beattie
e8f297db5a Fix up the profiles make install target for the tunables/multiarch.d/
change.
2011-03-23 16:10:33 -07:00
Steve Beattie
4d332ff241 Fix multi-arch comment tyop, thanks to Kees for pointing it out. 2011-03-23 13:44:51 -07:00
Steve Beattie
83007d7600 Author: Jamie Strandboge <jamie@canonical.com>,
Steve Langasek <steve.langasek@linaro.org>,
 Steve Beattie <sbeattie@ubuntu.com>
Description: add multiarch support to abstractions
Bug-Ubuntu: https://bugs.launchpad.net/bugs/736870

This patch add multiarch support for common shared library locations, as
well as a tunables file and directory to ease adding addiotional
multiarch paths.

Bug: https://launchpad.net/bugs/736870
2011-03-23 12:24:11 -07:00
Steve Beattie
5f72ae98eb Update the toplevel README file to discuss the needed apparmor
compatibility patches, mention the profile consistency check, and
remove some variables that no longer need to be set at build time.
2011-03-18 23:13:58 -07:00
Steve Beattie
940ead1fb8 disable the local include in the extras firefox profile; the build
process does not generate local files for things in extras, and even if
it did, this one is named in a non-standard fashion (usr.bin.firefox vs.
usr.lib.firefox.firefox).
2011-03-18 22:38:55 -07:00
Steve Beattie
bb28ca5371 profiles/Makefile: fix 'check' target to iterate over the profiles in
the extras directory as intended and fail the make if a parse failure
occurs. Also, set the default parser and logprof to be the intree ones;
the system ones can still be used by setting environment variables.
Finally, have the 'all' target generate the local files. Also, set the
parser base directory to the apparmor.d directory (rather than as an
added include, to avoid outside contamination from system profiles and
includes).

With these changes, make && make check should verify the profile set is
compilable and mostly consistent. (Alas, the current profiles are not
quite consistent).
2011-03-18 22:31:26 -07:00
Steve Beattie
0edae73a9a This fixes the apparmor apache2 module to link correctly against the
built libapparmor, as well as working around libtool so that the
libapparmor library build directory does not get added as an rpath to
the module.

Bug: https://launchpad.net/bugs/737074
2011-03-17 23:32:34 -07:00
Steve Beattie
19a2d6d169 Reduce the number of network protocols filtered from the parser.
https://bugs.launchpad.net/bugs/732837

Bug: https://launchpad.net/bugs/732837
2011-03-17 10:50:53 -07:00
Steve Beattie
f3b847c4b0 Fix from PLD/Arkadiusz Miskiewicz <arekm@maven.pl> to the initscript
helper functions to correct some log messages and to unload hats first,
before their parents.
2011-03-17 10:21:06 -07:00
John Johansen
1a2484e5bc Finish renaming regexp to regex
Signed-off-by: John Johansen <john.johansen@canonical.com>
2011-03-13 06:01:21 -07:00
John Johansen
6ed55cb1d5 Update Makefile dependencies
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-03-13 06:00:31 -07:00
John Johansen
099f19f99c Update the licence for apparmor_re.h
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-03-13 05:59:48 -07:00
John Johansen
7d2a6b53d4 Lindent parse + hand cleanups
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@gmail.com>
2011-03-13 05:58:54 -07:00
John Johansen
6f0c68a4d4 Lindent + some hand cleanups expr-tree
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@gmail.com>
2011-03-13 05:57:39 -07:00
John Johansen
9a377bb9da Lindent + some hand cleanups hfa
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@gmail.com>
2011-03-13 05:55:25 -07:00
John Johansen
3cfe47d3f0 Lindent + hand cleanups compressed-dfa
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-03-13 05:54:18 -07:00
John Johansen
84c0bba1ef Lindent + hand cleanups aare_rules
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-03-13 05:53:08 -07:00
John Johansen
6aad970d1c Split out compressed dfa "transition table" compression
Split hfa into hfa and compressed_hfa files.  The hfa portion focuses on
creating an manipulating hfas, while compressed_hfa is used for creating
compressed hfas that can be used/reused at run time with much less memory
usage than the full blown hfa.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-03-13 05:50:34 -07:00
John Johansen
298a36bffb Split out aare_rules which are used to encapsulate creating the dfa
Split out the aare_rule bits that encapsulate the convertion of apparmor
rules into the final compressed dfa.

This patch will not compile because of the it needs hfa to export an interface
but hfa is going to be split so just delay until hfa and transtable are
split and they can each export their own interface.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-03-13 05:49:15 -07:00
John Johansen
846cee5066 Split out parsing and expression trees from regexp.y
Start of splitting regexp.y into logical components instead of the mess
it is today.  Split out the expr-tree and parsing components from regexp.y
int expr-tree.x and parse.y and since regexp.y no longer does parsing
rename it to hfa.cc

Some code cleanups snuck their way into this patch and since I am to
lazy to redo it, I have left them in.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2011-03-13 05:46:29 -07:00
Steve Beattie
50760ef05b Adjust version to indicate that 2.6 has branched off and that trunk is
working towards a 2.7.0 or 3.0.0 release.
2011-03-10 10:25:55 -08:00
190 changed files with 9708 additions and 4107 deletions

View File

@@ -1,3 +1,4 @@
apparmor-*
parser/po/*.mo
parser/af_names.h
parser/cap_names.h
@@ -5,6 +6,7 @@ parser/tst_misc
parser/tst_regex
parser/tst_symtab
parser/tst_variable
parser/tst/simple_tests/generated_*/*
parser/parser_lex.c
parser/parser_version.h
parser/parser_yacc.c

View File

@@ -10,10 +10,6 @@ DIRS=parser \
changehat/libapparmor \
changehat/mod_apparmor \
changehat/pam_apparmor \
management/apparmor-dbus \
management/applets/apparmorapplet-gnome \
management/yastui \
common \
tests
REPO_URL?=lp:apparmor
@@ -24,12 +20,17 @@ REPO_URL?=lp:apparmor
RELEASE_DIR=apparmor-${VERSION}
__SETUP_DIR?=.
# We create a separate version for tags because git can't handle tags
# with embedded ~s in them. No spaces around '-' or they'll get
# embedded in ${VERSION}
TAG_VERSION=$(subst ~,-,${VERSION})
.PHONY: tarball
tarball: clean
REPO_VERSION=`$(value REPO_VERSION_CMD)` ; \
make export_dir __EXPORT_DIR=${RELEASE_DIR} __REPO_VERSION=$${REPO_VERSION} ; \
make setup __SETUP_DIR=${RELEASE_DIR} ; \
tar cvzf ${RELEASE_DIR}.tar.gz ${RELEASE_DIR}
tar --exclude deprecated -cvzf ${RELEASE_DIR}.tar.gz ${RELEASE_DIR}
.PHONY: snapshot
snapshot: clean
@@ -37,7 +38,7 @@ snapshot: clean
SNAPSHOT_DIR=apparmor-${VERSION}~$${REPO_VERSION} ;\
make export_dir __EXPORT_DIR=$${SNAPSHOT_DIR} __REPO_VERSION=$${REPO_VERSION} ; \
make setup __SETUP_DIR=$${SNAPSHOT_DIR} ; \
tar cvzf $${SNAPSHOT_DIR}.tar.gz $${SNAPSHOT_DIR} ;
tar --exclude deprecated -cvzf $${SNAPSHOT_DIR}.tar.gz $${SNAPSHOT_DIR} ;
.PHONY: export_dir
@@ -49,6 +50,9 @@ export_dir:
.PHONY: clean
clean:
-rm -rf ${RELEASE_DIR} ./apparmor-${VERSION}~*
for dir in $(DIRS); do \
make -C $$dir clean; \
done
.PHONY: setup
setup:
@@ -56,4 +60,5 @@ setup:
.PHONY: tag
tag:
bzr tag apparmor_${VERSION}
bzr tag apparmor_${TAG_VERSION}

42
README
View File

@@ -30,13 +30,26 @@ AppArmor consists of several different parts:
changehat/ source for using changehat with Apache, PAM and Tomcat
common/ common makefile rules
desktop/ empty
kernel-patches/ patches for various kernel versions
kernel-patches/ compatibility patches for various kernel versions
libraries/ libapparmor source and language bindings
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
--------------------------------------
While most of the kernel AppArmor code has been accepted in the
upstream Linux kernel, a few important pieces were not included. These
missing pieces unfortunately are important bits for AppArmor userspace
and kernel interaction; therefore we have included compatibility
patches in the kernel-patches/ subdirectory, versioned by upstream
kernel (2.6.37 patches should apply cleanly to 2.6.38 source).
Without these patches applied to the kernel, the AppArmor userspace
will not function correctly.
------------------------------------------
Building and Installing AppArmor Userspace
@@ -49,43 +62,52 @@ the following order.
libapparmor:
$ cd ./libraries/libapparmor
$ sh ./autogen.sh
$ sh ./configure --prefix=/usr --with-perl
$ sh ./configure --prefix=/usr --with-perl # see below
$ make
$ make check
$ make install
[optional arguments to libapparmor's configure include --with-python
and --with-ruby, to generate python and ruby bindings to libapparmor,
respectively.]
Utilities:
$ cd utils
$ make
$ make check
$ make install
parser:
$ cd parser
$ make
$ make tests # not strictly necessary as they are run during the
# build by default
$ make check
$ make install
Apache mod_apparmor:
$ cd changehat/mod_apparmor
$ LIBS="-lapparmor" make
$ make # depends on libapparmor having been built first
$ make install
PAM AppArmor:
$ cd changehat/pam_apparmor
$ LIBS="-lapparmor -lpam" make
$ 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 and the utils, if you only with to build/use
some of the locale languages, you can override the default by passing
the LANGS arguments to make; e.g. make all install "LANGS=en_US fr".]
-------------------
AppArmor Testsuites
@@ -124,6 +146,14 @@ For details on structure and adding tests, see libraries/libapparmor/README.
$ cd libraries/libapparmor
$ make check
Profile checks
--------------
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
------------

View File

@@ -41,12 +41,15 @@ APXS:=$(shell if [ -x "/usr/sbin/apxs2" ] ; then \
fi )
APXS_INSTALL_DIR=$(shell ${APXS} -q LIBEXECDIR)
DESTDIR=
LIBAPPARMOR_FLAGS="-I../../libraries/libapparmor/src -L../../libraries/libapparmor/src/.libs -lapparmor"
# Need to pass -Wl twice here to get past both apxs2 and libtool, as
# libtool will add the path to the RPATH of the library if passed -L/some/path
LIBAPPARMOR_FLAGS=-I../../libraries/libapparmor/src -Wl,-Wl,-L../../libraries/libapparmor/src/.libs
LDLIBS=-lapparmor
all: $(TARGET) ${MANPAGES} ${HTMLMANPAGES}
%.so: %.c
${APXS} ${LIBAPPARMOR_FLAGS} -c $<
${APXS} ${LIBAPPARMOR_FLAGS} -c $< ${LDLIBS}
mv .libs/$@ .
.PHONY: install

View File

@@ -37,4 +37,4 @@ clean:
rm -f tomcat_apparmor.spec ${NAME}-*.tar.gz Make.rules
install: $(SPECFILE)
ant -Dversion=$(VERSION) -Drelease=$(RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni
ant -Dversion=$(VERSION) -Drelease=$(MAN_RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni

View File

@@ -37,4 +37,4 @@ clean:
rm -f tomcat_apparmor.spec ${NAME}-*.tar.gz Make.rules
install: $(SPECFILE)
ant -Dversion=$(VERSION) -Drelease=$(RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni
ant -Dversion=$(VERSION) -Drelease=$(MAN_RELEASE) -Dcatalina_home=${CATALINA_HOME} -Dinstall_lib=${LIB} install_jar install_jni

View File

@@ -27,7 +27,8 @@ XGETTEXT_ARGS=--copyright-holder="NOVELL, Inc." --msgid-bugs-address=apparmor@li
# pass in the list of sources in the SOURCES variable
PARENT_SOURCES=$(foreach source, ${SOURCES}, ../${source})
LANGS=$(patsubst %.po, %, $(wildcard *.po))
# Can override by passing LANGS=whatever here
LANGS?=$(patsubst %.po, %, $(wildcard *.po))
TARGET_MOS=$(foreach lang, $(filter-out $(DISABLED_LANGS),$(LANGS)), ${lang}.mo)
.PHONY: all

View File

@@ -1 +1 @@
2.6.0
2.7.0

218
deprecated/utils/aa-status Normal file
View File

@@ -0,0 +1,218 @@
#!/usr/bin/perl -w
# ------------------------------------------------------------------
#
# Copyright (C) 2005-2006 Novell/SUSE
#
# 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.
#
# ------------------------------------------------------------------
use strict;
use Getopt::Long;
use Cwd 'abs_path';
my $confdir = "/etc/apparmor";
my $sd_mountpoint;
my $check_enabled = 0;
my $count_enforced = 0;
my $count_profiled = 0;
my $count_complain = 0;
my $verbose = 0;
my $help;
GetOptions(
'complaining' => \$count_complain,
'enabled' => \$check_enabled,
'enforced' => \$count_enforced,
'profiled' => \$count_profiled,
'verbose|v' => \$verbose,
'help|h' => \$help,
) or usage();
sub usage {
print "Usage: $0 [OPTIONS]\n";
print "Displays various information about the currently loaded AppArmor policy.\n";
print "OPTIONS (one only):\n";
print " --enabled returns error code if subdomain not enabled\n";
print " --profiled prints the number of loaded policies\n";
print " --enforced prints the number of loaded enforcing policies\n";
print " --complaining prints the number of loaded non-enforcing policies\n";
print " --verbose (default) displays multiple data points about loaded policy set\n";
print " --help this message\n";
exit;
}
$verbose = 1 if ($count_complain + $check_enabled + $count_enforced + $count_profiled == 0);
usage() if $help or ($count_complain + $check_enabled + $count_enforced + $count_profiled + $verbose > 1);
sub is_subdomain_loaded() {
return 1 if (-d "/sys/module/apparmor");
if(open(MODULES, "/proc/modules")) {
while(<MODULES>) {
return 1 if m/^(subdomain|apparmor)\s+/;
}
}
return 0;
}
sub find_subdomainfs() {
my $sd_mountpoint;
if(open(MOUNTS, "/proc/mounts")) {
while(<MOUNTS>) {
$sd_mountpoint = "$1/apparmor" if m/^\S+\s+(\S+)\s+securityfs\s/ && -e "$1/apparmor";
$sd_mountpoint = "$1/subdomain" if m/^\S+\s+(\S+)\s+securityfs\s/ && -e "$1/subdomain";
$sd_mountpoint = $1 if m/^\S+\s+(\S+)\s+subdomainfs\s/ && -e "$1";
}
close(MOUNTS);
}
return $sd_mountpoint;
}
sub get_profiles {
my $mountpoint = shift;
my %profiles = ();
if (open(PROFILES, "$mountpoint/profiles")) {
while(<PROFILES>) {
$profiles{$1} = $2 if m/^([^\(]+)\s+\((\w+)\)$/;
}
close(PROFILES);
}
return (%profiles);
}
sub get_processes {
my %profiles = @_;
my %processes = ();
if (opendir(PROC, "/proc")) {
my $file;
while (defined($file = readdir(PROC))) {
if ($file =~ m/^\d+/) {
if (open(CURRENT, "/proc/$file/attr/current")) {
while (<CURRENT>) {
if (m/^([^\(]+)\s+\((\w+)\)$/) {
$processes{$file}{'profile'} = $1;
$processes{$file}{'mode'} = $2;
} elsif (grep(abs_path("/proc/$file/exe") eq $_ , keys(%profiles))) {
# keep only unconfined processes that have a profile defined
$processes{$file}{'profile'} = abs_path("/proc/$file/exe");
$processes{$file}{'mode'} = 'unconfined';
}
}
close(CURRENT);
}
}
}
closedir(PROC);
}
return (%processes);
}
my $is_loaded = is_subdomain_loaded();
if (!$is_loaded) {
print STDERR "apparmor module is not loaded.\n" if $verbose;
exit 1;
}
print "apparmor module is loaded.\n" if $verbose;
$sd_mountpoint = find_subdomainfs();
if (!$sd_mountpoint) {
print STDERR "apparmor filesystem is not mounted.\n" if $verbose;
exit 3;
}
if (! -r "$sd_mountpoint/profiles") {
print STDERR "You do not have enough privilege to read the profile set.\n" if $verbose;
exit 4;
}
#print "subdomainfs is at $sd_mountpoint.\n" if $verbose;
# processes is a hash table :
# * keys : processes pid
# * values : hash containing information about the running process:
# * 'profile' : name of the profile applied to the running process
# * 'mode' : mode of the profile applied to the running process
my %processes = ();
my %enforced_processes = ();
my %complain_processes = ();
my %unconfined_processes = ();
# profiles is a hash table :
# * keys : profile name
# * value : profile mode
my %profiles;
my @enforced_profiles = ();
my @complain_profiles = ();
%profiles = get_profiles($sd_mountpoint);
@enforced_profiles = grep { $profiles{$_} eq 'enforce' } keys %profiles;
@complain_profiles = grep { $profiles{$_} eq 'complain' } keys %profiles;
# we consider the case where no profiles are loaded to be "disabled" as well
my $rc = (keys(%profiles) == 0) ? 2 : 0;
if ($check_enabled) {
exit $rc;
}
if ($count_profiled) {
print scalar(keys(%profiles)). "\n";
exit $rc;
}
if ($count_enforced) {
print $#enforced_profiles + 1 . "\n";
exit $rc;
}
if ($count_complain) {
print $#complain_profiles + 1 . "\n";
exit $rc;
}
if ($verbose) {
print keys(%profiles) . " profiles are loaded.\n";
print $#enforced_profiles + 1 . " profiles are in enforce mode.\n";
for (sort(@enforced_profiles)) {
print " " . $_ . "\n";
}
print $#complain_profiles + 1 . " profiles are in complain mode.\n";
for (sort(@complain_profiles)) {
print " " . $_ . "\n";
}
}
%processes = get_processes(%profiles);
if ($verbose) {
for (keys(%processes)) {
$enforced_processes{$_} = $processes{$_} if $processes{$_}{'mode'} eq 'enforce';
$complain_processes{$_} = $processes{$_} if $processes{$_}{'mode'} eq 'complain';
# some early code uses unconfined instead of unconfined.
$unconfined_processes{$_} = $processes{$_} if $processes{$_}{'mode'} =~ /uncon(fi|strai)ned/;
}
print keys(%processes) . " processes have profiles defined.\n";
print keys(%enforced_processes) . " processes are in enforce mode :\n";
for (sort { $enforced_processes{$a}{'profile'} cmp $enforced_processes{$b}{'profile'} } keys(%enforced_processes)) {
print " " . $enforced_processes{$_}{'profile'} . " ($_) \n";
}
print keys(%complain_processes) . " processes are in complain mode.\n";
for (sort { $complain_processes{$a}{'profile'} cmp $complain_processes{$b}{'profile'} } keys(%complain_processes)) {
print " " . $complain_processes{$_}{'profile'} . " ($_) \n";
}
print keys(%unconfined_processes) . " processes are unconfined but have a profile defined.\n";
for (sort { $unconfined_processes{$a}{'profile'} cmp $unconfined_processes{$b}{'profile'} } keys(%unconfined_processes)) {
print " " . $unconfined_processes{$_}{'profile'} . " ($_) \n";
}
}
exit $rc;

Binary file not shown.

View File

@@ -0,0 +1,553 @@
From 0ae314bc92d8b22250f04f85e4bd36ee9ed30890 Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen@canonical.com>
Date: Mon, 4 Oct 2010 15:03:36 -0700
Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll
Add compatibility for v5 network rules.
Signed-off-by: John Johansen <john.johansen@canonical.com>
---
include/linux/lsm_audit.h | 4 +
security/apparmor/Makefile | 19 ++++-
security/apparmor/include/net.h | 40 +++++++++
security/apparmor/include/policy.h | 3 +
security/apparmor/lsm.c | 112 +++++++++++++++++++++++
security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++
security/apparmor/policy.c | 1 +
security/apparmor/policy_unpack.c | 48 ++++++++++-
8 files changed, 394 insertions(+), 3 deletions(-)
create mode 100644 security/apparmor/include/net.h
create mode 100644 security/apparmor/net.c
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
index 112a550..d5f3dd7 100644
--- a/include/linux/lsm_audit.h
+++ b/include/linux/lsm_audit.h
@@ -123,6 +123,10 @@ struct common_audit_data {
u32 denied;
uid_t ouid;
} fs;
+ struct {
+ int type, protocol;
+ struct sock *sk;
+ } net;
};
} apparmor_audit_data;
#endif
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index 2dafe50..7cefef9 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
- resource.o sid.o file.o
+ resource.o sid.o file.o net.o
-clean-files := capability_names.h rlim_names.h
+clean-files := capability_names.h rlim_names.h af_names.h
# Build a lower case string table of capability names
@@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\
sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\
echo "};" >> $@
+# Build a lower case string table of address family names.
+# Transform lines from
+# #define AF_INET 2 /* Internet IP Protocol */
+# to
+# [2] = "inet",
+quiet_cmd_make-af = GEN $@
+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
+ sed $< >> $@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \
+ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+).*/[\2] = "\L\1",/p';\
+ echo "};" >> $@
+
+
$(obj)/capability.o : $(obj)/capability_names.h
$(obj)/resource.o : $(obj)/rlim_names.h
+$(obj)/net.o : $(obj)/af_names.h
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
$(call cmd,make-caps)
$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h
$(call cmd,make-rlim)
+$(obj)/af_names.h : $(srctree)/include/linux/socket.h
+ $(call cmd,make-af)
\ No newline at end of file
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
new file mode 100644
index 0000000..3c7d599
--- /dev/null
+++ b/security/apparmor/include/net.h
@@ -0,0 +1,40 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation definitions.
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2010 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#ifndef __AA_NET_H
+#define __AA_NET_H
+
+#include <net/sock.h>
+
+/* struct aa_net - network confinement data
+ * @allowed: basic network families permissions
+ * @audit_network: which network permissions to force audit
+ * @quiet_network: which network permissions to quiet rejects
+ */
+struct aa_net {
+ u16 allow[AF_MAX];
+ u16 audit[AF_MAX];
+ u16 quiet[AF_MAX];
+};
+
+extern int aa_net_perm(int op, struct aa_profile *profile, u16 family,
+ int type, int protocol, struct sock *sk);
+extern int aa_revalidate_sk(int op, struct sock *sk);
+
+static inline void aa_free_net_rules(struct aa_net *new)
+{
+ /* NOP */
+}
+
+#endif /* __AA_NET_H */
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index aeda5cf..6776929 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -27,6 +27,7 @@
#include "capability.h"
#include "domain.h"
#include "file.h"
+#include "net.h"
#include "resource.h"
extern const char *profile_mode_names[];
@@ -145,6 +146,7 @@ struct aa_namespace {
* @size: the memory consumed by this profiles rules
* @file: The set of rules governing basic file access and domain transitions
* @caps: capabilities for the profile
+ * @net: network controls for the profile
* @rlimits: rlimits for the profile
*
* The AppArmor profile contains the basic confinement data. Each profile
@@ -181,6 +183,7 @@ struct aa_profile {
struct aa_file_rules file;
struct aa_caps caps;
+ struct aa_net net;
struct aa_rlimit rlimits;
};
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index ae3a698..05c018b 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -32,6 +32,7 @@
#include "include/context.h"
#include "include/file.h"
#include "include/ipc.h"
+#include "include/net.h"
#include "include/path.h"
#include "include/policy.h"
#include "include/procattr.h"
@@ -620,6 +621,104 @@ static int apparmor_task_setrlimit(struct task_struct *task,
return error;
}
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ if (kern)
+ return 0;
+
+ profile = __aa_current_profile();
+ if (!unconfined(profile))
+ error = aa_net_perm(OP_CREATE, profile, family, type, protocol,
+ NULL);
+ return error;
+}
+
+static int apparmor_socket_bind(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_BIND, sk);
+}
+
+static int apparmor_socket_connect(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_CONNECT, sk);
+}
+
+static int apparmor_socket_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_LISTEN, sk);
+}
+
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_ACCEPT, sk);
+}
+
+static int apparmor_socket_sendmsg(struct socket *sock,
+ struct msghdr *msg, int size)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_SENDMSG, sk);
+}
+
+static int apparmor_socket_recvmsg(struct socket *sock,
+ struct msghdr *msg, int size, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_RECVMSG, sk);
+}
+
+static int apparmor_socket_getsockname(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_GETSOCKNAME, sk);
+}
+
+static int apparmor_socket_getpeername(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_GETPEERNAME, sk);
+}
+
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
+ int optname)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_GETSOCKOPT, sk);
+}
+
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
+ int optname)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_SETSOCKOPT, sk);
+}
+
+static int apparmor_socket_shutdown(struct socket *sock, int how)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk);
+}
+
static struct security_operations apparmor_ops = {
.name = "apparmor",
@@ -651,6 +750,19 @@ static struct security_operations apparmor_ops = {
.getprocattr = apparmor_getprocattr,
.setprocattr = apparmor_setprocattr,
+ .socket_create = apparmor_socket_create,
+ .socket_bind = apparmor_socket_bind,
+ .socket_connect = apparmor_socket_connect,
+ .socket_listen = apparmor_socket_listen,
+ .socket_accept = apparmor_socket_accept,
+ .socket_sendmsg = apparmor_socket_sendmsg,
+ .socket_recvmsg = apparmor_socket_recvmsg,
+ .socket_getsockname = apparmor_socket_getsockname,
+ .socket_getpeername = apparmor_socket_getpeername,
+ .socket_getsockopt = apparmor_socket_getsockopt,
+ .socket_setsockopt = apparmor_socket_setsockopt,
+ .socket_shutdown = apparmor_socket_shutdown,
+
.cred_alloc_blank = apparmor_cred_alloc_blank,
.cred_free = apparmor_cred_free,
.cred_prepare = apparmor_cred_prepare,
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
new file mode 100644
index 0000000..1765901
--- /dev/null
+++ b/security/apparmor/net.c
@@ -0,0 +1,170 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2010 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#include "include/apparmor.h"
+#include "include/audit.h"
+#include "include/context.h"
+#include "include/net.h"
+#include "include/policy.h"
+
+#include "af_names.h"
+
+static const char *sock_type_names[] = {
+ "unknown(0)",
+ "stream",
+ "dgram",
+ "raw",
+ "rdm",
+ "seqpacket",
+ "dccp",
+ "unknown(7)",
+ "unknown(8)",
+ "unknown(9)",
+ "packet",
+};
+
+/* audit callback for net specific fields */
+static void audit_cb(struct audit_buffer *ab, void *va)
+{
+ struct common_audit_data *sa = va;
+
+ audit_log_format(ab, " family=");
+ if (address_family_names[sa->u.net.family]) {
+ audit_log_string(ab, address_family_names[sa->u.net.family]);
+ } else {
+ audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family);
+ }
+
+ audit_log_format(ab, " sock_type=");
+ if (sock_type_names[sa->aad.net.type]) {
+ audit_log_string(ab, sock_type_names[sa->aad.net.type]);
+ } else {
+ audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type);
+ }
+
+ audit_log_format(ab, " protocol=%d", sa->aad.net.protocol);
+}
+
+/**
+ * audit_net - audit network access
+ * @profile: profile being enforced (NOT NULL)
+ * @op: operation being checked
+ * @family: network family
+ * @type: network type
+ * @protocol: network protocol
+ * @sk: socket auditing is being applied to
+ * @error: error code for failure else 0
+ *
+ * Returns: %0 or sa->error else other errorcode on failure
+ */
+static int audit_net(struct aa_profile *profile, int op, u16 family, int type,
+ int protocol, struct sock *sk, int error)
+{
+ int audit_type = AUDIT_APPARMOR_AUTO;
+ struct common_audit_data sa;
+ if (sk) {
+ COMMON_AUDIT_DATA_INIT(&sa, NET);
+ } else {
+ COMMON_AUDIT_DATA_INIT(&sa, NONE);
+ }
+ /* todo fill in socket addr info */
+
+ sa.aad.op = op,
+ sa.u.net.family = family;
+ sa.u.net.sk = sk;
+ sa.aad.net.type = type;
+ sa.aad.net.protocol = protocol;
+ sa.aad.error = error;
+
+ if (likely(!sa.aad.error)) {
+ u16 audit_mask = profile->net.audit[sa.u.net.family];
+ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
+ !(1 << sa.aad.net.type & audit_mask)))
+ return 0;
+ audit_type = AUDIT_APPARMOR_AUDIT;
+ } else {
+ u16 quiet_mask = profile->net.quiet[sa.u.net.family];
+ u16 kill_mask = 0;
+ u16 denied = (1 << sa.aad.net.type) & ~quiet_mask;
+
+ if (denied & kill_mask)
+ audit_type = AUDIT_APPARMOR_KILL;
+
+ if ((denied & quiet_mask) &&
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
+ AUDIT_MODE(profile) != AUDIT_ALL)
+ return COMPLAIN_MODE(profile) ? 0 : sa.aad.error;
+ }
+
+ return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb);
+}
+
+/**
+ * aa_net_perm - very course network access check
+ * @op: operation being checked
+ * @profile: profile being enforced (NOT NULL)
+ * @family: network family
+ * @type: network type
+ * @protocol: network protocol
+ *
+ * Returns: %0 else error if permission denied
+ */
+int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type,
+ int protocol, struct sock *sk)
+{
+ u16 family_mask;
+ int error;
+
+ if ((family < 0) || (family >= AF_MAX))
+ return -EINVAL;
+
+ if ((type < 0) || (type >= SOCK_MAX))
+ return -EINVAL;
+
+ /* unix domain and netlink sockets are handled by ipc */
+ if (family == AF_UNIX || family == AF_NETLINK)
+ return 0;
+
+ family_mask = profile->net.allow[family];
+
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
+
+ return audit_net(profile, op, family, type, protocol, sk, error);
+}
+
+/**
+ * aa_revalidate_sk - Revalidate access to a sock
+ * @op: operation being checked
+ * @sk: sock being revalidated (NOT NULL)
+ *
+ * Returns: %0 else error if permission denied
+ */
+int aa_revalidate_sk(int op, struct sock *sk)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ /* aa_revalidate_sk should not be called from interrupt context
+ * don't mediate these calls as they are not task related
+ */
+ if (in_interrupt())
+ return 0;
+
+ profile = __aa_current_profile();
+ if (!unconfined(profile))
+ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type,
+ sk->sk_protocol, sk);
+
+ return error;
+}
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 4f0eade..4d5ce13 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile)
aa_free_file_rules(&profile->file);
aa_free_cap_rules(&profile->caps);
+ aa_free_net_rules(&profile->net);
aa_free_rlimit_rules(&profile->rlimits);
aa_free_sid(profile->sid);
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index e33aaf7..fa3f1b4 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -190,6 +190,19 @@ fail:
return 0;
}
+static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name)
+{
+ if (unpack_nameX(e, AA_U16, name)) {
+ if (!inbounds(e, sizeof(u16)))
+ return 0;
+ if (data)
+ *data = le16_to_cpu(get_unaligned((u16 *) e->pos));
+ e->pos += sizeof(u16);
+ return 1;
+ }
+ return 0;
+}
+
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
{
if (unpack_nameX(e, AA_U32, name)) {
@@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
{
struct aa_profile *profile = NULL;
const char *name = NULL;
- int error = -EPROTO;
+ size_t size = 0;
+ int i, error = -EPROTO;
kernel_cap_t tmpcap;
u32 tmp;
@@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
if (!unpack_rlimits(e, profile))
goto fail;
+ size = unpack_array(e, "net_allowed_af");
+ if (size) {
+
+ for (i = 0; i < size; i++) {
+ /* discard extraneous rules that this kernel will
+ * never request
+ */
+ if (i > AF_MAX) {
+ u16 tmp;
+ if (!unpack_u16(e, &tmp, NULL) ||
+ !unpack_u16(e, &tmp, NULL) ||
+ !unpack_u16(e, &tmp, NULL))
+ goto fail;
+ continue;
+ }
+ if (!unpack_u16(e, &profile->net.allow[i], NULL))
+ goto fail;
+ if (!unpack_u16(e, &profile->net.audit[i], NULL))
+ goto fail;
+ if (!unpack_u16(e, &profile->net.quiet[i], NULL))
+ goto fail;
+ }
+ if (!unpack_nameX(e, AA_ARRAYEND, NULL))
+ goto fail;
+ /*
+ * allow unix domain and netlink sockets they are handled
+ * by IPC
+ */
+ }
+ profile->net.allow[AF_UNIX] = 0xffff;
+ profile->net.allow[AF_NETLINK] = 0xffff;
+
/* get file rules */
profile->file.dfa = unpack_dfa(e);
if (IS_ERR(profile->file.dfa)) {
--
1.7.0.4

View File

@@ -0,0 +1,391 @@
From cdc6b35345e5bcfe92bb2b52ef003f94ceedd40d Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen@canonical.com>
Date: Thu, 22 Jul 2010 02:32:02 -0700
Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface
Signed-off-by: John Johansen <john.johansen@canonical.com>
---
security/apparmor/Kconfig | 9 +
security/apparmor/Makefile | 1 +
security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++
security/apparmor/apparmorfs.c | 18 ++-
security/apparmor/include/apparmorfs.h | 6 +
5 files changed, 319 insertions(+), 2 deletions(-)
create mode 100644 security/apparmor/apparmorfs-24.c
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
index 9b9013b..51ebf96 100644
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
boot.
If you are unsure how to answer this question, answer 1.
+
+config SECURITY_APPARMOR_COMPAT_24
+ bool "Enable AppArmor 2.4 compatability"
+ depends on SECURITY_APPARMOR
+ default y
+ help
+ This option enables compatability with AppArmor 2.4. It is
+ recommended if compatability with older versions of AppArmor
+ is desired.
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index 7cefef9..0bb604b 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
resource.o sid.o file.o net.o
+apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o
clean-files := capability_names.h rlim_names.h af_names.h
diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c
new file mode 100644
index 0000000..dc8c744
--- /dev/null
+++ b/security/apparmor/apparmorfs-24.c
@@ -0,0 +1,287 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2010 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ *
+ * This file contain functions providing an interface for <= AppArmor 2.4
+ * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24
+ * being set (see Makefile).
+ */
+
+#include <linux/security.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/namei.h>
+
+#include "include/apparmor.h"
+#include "include/audit.h"
+#include "include/context.h"
+#include "include/policy.h"
+
+
+/* apparmor/matching */
+static ssize_t aa_matching_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ const char matching[] = "pattern=aadfa audit perms=crwxamlk/ "
+ "user::other";
+
+ return simple_read_from_buffer(buf, size, ppos, matching,
+ sizeof(matching) - 1);
+}
+
+const struct file_operations aa_fs_matching_fops = {
+ .read = aa_matching_read,
+};
+
+/* apparmor/features */
+static ssize_t aa_features_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ const char features[] = "file=3.1 capability=2.0 network=1.0 "
+ "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1";
+
+ return simple_read_from_buffer(buf, size, ppos, features,
+ sizeof(features) - 1);
+}
+
+const struct file_operations aa_fs_features_fops = {
+ .read = aa_features_read,
+};
+
+/**
+ * __next_namespace - find the next namespace to list
+ * @root: root namespace to stop search at (NOT NULL)
+ * @ns: current ns position (NOT NULL)
+ *
+ * Find the next namespace from @ns under @root and handle all locking needed
+ * while switching current namespace.
+ *
+ * Returns: next namespace or NULL if at last namespace under @root
+ * NOTE: will not unlock root->lock
+ */
+static struct aa_namespace *__next_namespace(struct aa_namespace *root,
+ struct aa_namespace *ns)
+{
+ struct aa_namespace *parent;
+
+ /* is next namespace a child */
+ if (!list_empty(&ns->sub_ns)) {
+ struct aa_namespace *next;
+ next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
+ read_lock(&next->lock);
+ return next;
+ }
+
+ /* check if the next ns is a sibling, parent, gp, .. */
+ parent = ns->parent;
+ while (parent) {
+ read_unlock(&ns->lock);
+ list_for_each_entry_continue(ns, &parent->sub_ns, base.list) {
+ read_lock(&ns->lock);
+ return ns;
+ }
+ if (parent == root)
+ return NULL;
+ ns = parent;
+ parent = parent->parent;
+ }
+
+ return NULL;
+}
+
+/**
+ * __first_profile - find the first profile in a namespace
+ * @root: namespace that is root of profiles being displayed (NOT NULL)
+ * @ns: namespace to start in (NOT NULL)
+ *
+ * Returns: unrefcounted profile or NULL if no profile
+ */
+static struct aa_profile *__first_profile(struct aa_namespace *root,
+ struct aa_namespace *ns)
+{
+ for ( ; ns; ns = __next_namespace(root, ns)) {
+ if (!list_empty(&ns->base.profiles))
+ return list_first_entry(&ns->base.profiles,
+ struct aa_profile, base.list);
+ }
+ return NULL;
+}
+
+/**
+ * __next_profile - step to the next profile in a profile tree
+ * @profile: current profile in tree (NOT NULL)
+ *
+ * Perform a depth first taversal on the profile tree in a namespace
+ *
+ * Returns: next profile or NULL if done
+ * Requires: profile->ns.lock to be held
+ */
+static struct aa_profile *__next_profile(struct aa_profile *p)
+{
+ struct aa_profile *parent;
+ struct aa_namespace *ns = p->ns;
+
+ /* is next profile a child */
+ if (!list_empty(&p->base.profiles))
+ return list_first_entry(&p->base.profiles, typeof(*p),
+ base.list);
+
+ /* is next profile a sibling, parent sibling, gp, subling, .. */
+ parent = p->parent;
+ while (parent) {
+ list_for_each_entry_continue(p, &parent->base.profiles,
+ base.list)
+ return p;
+ p = parent;
+ parent = parent->parent;
+ }
+
+ /* is next another profile in the namespace */
+ list_for_each_entry_continue(p, &ns->base.profiles, base.list)
+ return p;
+
+ return NULL;
+}
+
+/**
+ * next_profile - step to the next profile in where ever it may be
+ * @root: root namespace (NOT NULL)
+ * @profile: current profile (NOT NULL)
+ *
+ * Returns: next profile or NULL if there isn't one
+ */
+static struct aa_profile *next_profile(struct aa_namespace *root,
+ struct aa_profile *profile)
+{
+ struct aa_profile *next = __next_profile(profile);
+ if (next)
+ return next;
+
+ /* finished all profiles in namespace move to next namespace */
+ return __first_profile(root, __next_namespace(root, profile->ns));
+}
+
+/**
+ * p_start - start a depth first traversal of profile tree
+ * @f: seq_file to fill
+ * @pos: current position
+ *
+ * Returns: first profile under current namespace or NULL if none found
+ *
+ * acquires first ns->lock
+ */
+static void *p_start(struct seq_file *f, loff_t *pos)
+ __acquires(root->lock)
+{
+ struct aa_profile *profile = NULL;
+ struct aa_namespace *root = aa_current_profile()->ns;
+ loff_t l = *pos;
+ f->private = aa_get_namespace(root);
+
+
+ /* find the first profile */
+ read_lock(&root->lock);
+ profile = __first_profile(root, root);
+
+ /* skip to position */
+ for (; profile && l > 0; l--)
+ profile = next_profile(root, profile);
+
+ return profile;
+}
+
+/**
+ * p_next - read the next profile entry
+ * @f: seq_file to fill
+ * @p: profile previously returned
+ * @pos: current position
+ *
+ * Returns: next profile after @p or NULL if none
+ *
+ * may acquire/release locks in namespace tree as necessary
+ */
+static void *p_next(struct seq_file *f, void *p, loff_t *pos)
+{
+ struct aa_profile *profile = p;
+ struct aa_namespace *root = f->private;
+ (*pos)++;
+
+ return next_profile(root, profile);
+}
+
+/**
+ * p_stop - stop depth first traversal
+ * @f: seq_file we are filling
+ * @p: the last profile writen
+ *
+ * Release all locking done by p_start/p_next on namespace tree
+ */
+static void p_stop(struct seq_file *f, void *p)
+ __releases(root->lock)
+{
+ struct aa_profile *profile = p;
+ struct aa_namespace *root = f->private, *ns;
+
+ if (profile) {
+ for (ns = profile->ns; ns && ns != root; ns = ns->parent)
+ read_unlock(&ns->lock);
+ }
+ read_unlock(&root->lock);
+ aa_put_namespace(root);
+}
+
+/**
+ * seq_show_profile - show a profile entry
+ * @f: seq_file to file
+ * @p: current position (profile) (NOT NULL)
+ *
+ * Returns: error on failure
+ */
+static int seq_show_profile(struct seq_file *f, void *p)
+{
+ struct aa_profile *profile = (struct aa_profile *)p;
+ struct aa_namespace *root = f->private;
+
+ if (profile->ns != root)
+ seq_printf(f, ":%s://", aa_ns_name(root, profile->ns));
+ seq_printf(f, "%s (%s)\n", profile->base.hname,
+ COMPLAIN_MODE(profile) ? "complain" : "enforce");
+
+ return 0;
+}
+
+static const struct seq_operations aa_fs_profiles_op = {
+ .start = p_start,
+ .next = p_next,
+ .stop = p_stop,
+ .show = seq_show_profile,
+};
+
+static int profiles_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &aa_fs_profiles_op);
+}
+
+static int profiles_release(struct inode *inode, struct file *file)
+{
+ return seq_release(inode, file);
+}
+
+const struct file_operations aa_fs_profiles_fops = {
+ .open = profiles_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = profiles_release,
+};
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 0848292..28c52ac 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void)
aafs_remove(".remove");
aafs_remove(".replace");
aafs_remove(".load");
-
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
+ aafs_remove("profiles");
+ aafs_remove("matching");
+ aafs_remove("features");
+#endif
securityfs_remove(aa_fs_dentry);
aa_fs_dentry = NULL;
}
@@ -218,7 +222,17 @@ int __init aa_create_aafs(void)
aa_fs_dentry = NULL;
goto error;
}
-
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
+ error = aafs_create("matching", 0444, &aa_fs_matching_fops);
+ if (error)
+ goto error;
+ error = aafs_create("features", 0444, &aa_fs_features_fops);
+ if (error)
+ goto error;
+#endif
+ error = aafs_create("profiles", 0440, &aa_fs_profiles_fops);
+ if (error)
+ goto error;
error = aafs_create(".load", 0640, &aa_fs_profile_load);
if (error)
goto error;
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index cb1e93a..14f955c 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -17,4 +17,10 @@
extern void __init aa_destroy_aafs(void);
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
+extern const struct file_operations aa_fs_matching_fops;
+extern const struct file_operations aa_fs_features_fops;
+extern const struct file_operations aa_fs_profiles_fops;
+#endif
+
#endif /* __AA_APPARMORFS_H */
--
1.7.0.4

View File

@@ -0,0 +1,68 @@
From f17b28f64b963c47e76737f7bb7f58ce3a7c5249 Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen@canonical.com>
Date: Tue, 20 Jul 2010 06:57:08 -0700
Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken userspace
The apparmor_parser when compiling policy could generate invalid dfas
that did not have sufficient padding to avoid invalid references, when
used by the kernel. The kernels check to verify the next/check table
size was broken meaning invalid dfas were being created by userspace
and not caught.
To remain compatible with old tools that are not fixed, pad the loaded
dfas next/check table. The dfa's themselves are valid except for the
high padding for potentially invalid transitions (high bounds error),
which have a maximimum is 256 entries. So just allocate an extra null filled
256 entries for the next/check tables. This will guarentee all bounds
are good and invalid transitions go to the null (0) state.
Signed-off-by: John Johansen <john.johansen@canonical.com>
---
security/apparmor/match.c | 17 +++++++++++++++++
1 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 06d764c..cf92856 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
if (bsize < tsize)
goto out;
+ /* Pad table allocation for next/check by 256 entries to remain
+ * backwards compatible with old (buggy) tools and remain safe without
+ * run time checks
+ */
+ if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK)
+ tsize += 256 * th.td_flags;
+
table = kvmalloc(tsize);
if (table) {
+ /* ensure the pad is clear, else there will be errors */
+ memset(table, 0, tsize);
*table = th;
if (th.td_flags == YYTD_DATA8)
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
@@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags)
goto out;
if (flags & DFA_FLAG_VERIFY_STATES) {
+ int warning = 0;
for (i = 0; i < state_count; i++) {
if (DEFAULT_TABLE(dfa)[i] >= state_count)
goto out;
/* TODO: do check that DEF state recursion terminates */
if (BASE_TABLE(dfa)[i] + 255 >= trans_count) {
+ if (warning)
+ continue;
+ printk(KERN_WARNING "AppArmor DFA next/check "
+ "upper bounds error fixed, upgrade "
+ "user space tools \n");
+ warning = 1;
+ } else if (BASE_TABLE(dfa)[i] >= trans_count) {
printk(KERN_ERR "AppArmor DFA next/check upper "
"bounds error\n");
goto out;
--
1.7.0.4

View File

@@ -0,0 +1,553 @@
From dc13dec93dbd04bfa7a9ba67df1b8ed3431d8d48 Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen@canonical.com>
Date: Wed, 10 Aug 2011 22:02:39 -0700
Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll
Add compatibility for v5 network rules.
Signed-off-by: John Johansen <john.johansen@canonical.com>
---
include/linux/lsm_audit.h | 4 +
security/apparmor/Makefile | 19 ++++-
security/apparmor/include/net.h | 40 +++++++++
security/apparmor/include/policy.h | 3 +
security/apparmor/lsm.c | 112 +++++++++++++++++++++++
security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++
security/apparmor/policy.c | 1 +
security/apparmor/policy_unpack.c | 48 ++++++++++-
8 files changed, 394 insertions(+), 3 deletions(-)
create mode 100644 security/apparmor/include/net.h
create mode 100644 security/apparmor/net.c
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
index 88e78de..c63979a 100644
--- a/include/linux/lsm_audit.h
+++ b/include/linux/lsm_audit.h
@@ -124,6 +124,10 @@ struct common_audit_data {
u32 denied;
uid_t ouid;
} fs;
+ struct {
+ int type, protocol;
+ struct sock *sk;
+ } net;
};
} apparmor_audit_data;
#endif
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index 2dafe50..7cefef9 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
- resource.o sid.o file.o
+ resource.o sid.o file.o net.o
-clean-files := capability_names.h rlim_names.h
+clean-files := capability_names.h rlim_names.h af_names.h
# Build a lower case string table of capability names
@@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\
sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\
echo "};" >> $@
+# Build a lower case string table of address family names.
+# Transform lines from
+# #define AF_INET 2 /* Internet IP Protocol */
+# to
+# [2] = "inet",
+quiet_cmd_make-af = GEN $@
+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
+ sed $< >> $@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \
+ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+).*/[\2] = "\L\1",/p';\
+ echo "};" >> $@
+
+
$(obj)/capability.o : $(obj)/capability_names.h
$(obj)/resource.o : $(obj)/rlim_names.h
+$(obj)/net.o : $(obj)/af_names.h
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
$(call cmd,make-caps)
$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h
$(call cmd,make-rlim)
+$(obj)/af_names.h : $(srctree)/include/linux/socket.h
+ $(call cmd,make-af)
\ No newline at end of file
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
new file mode 100644
index 0000000..3c7d599
--- /dev/null
+++ b/security/apparmor/include/net.h
@@ -0,0 +1,40 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation definitions.
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2010 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#ifndef __AA_NET_H
+#define __AA_NET_H
+
+#include <net/sock.h>
+
+/* struct aa_net - network confinement data
+ * @allowed: basic network families permissions
+ * @audit_network: which network permissions to force audit
+ * @quiet_network: which network permissions to quiet rejects
+ */
+struct aa_net {
+ u16 allow[AF_MAX];
+ u16 audit[AF_MAX];
+ u16 quiet[AF_MAX];
+};
+
+extern int aa_net_perm(int op, struct aa_profile *profile, u16 family,
+ int type, int protocol, struct sock *sk);
+extern int aa_revalidate_sk(int op, struct sock *sk);
+
+static inline void aa_free_net_rules(struct aa_net *new)
+{
+ /* NOP */
+}
+
+#endif /* __AA_NET_H */
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index aeda5cf..6776929 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -27,6 +27,7 @@
#include "capability.h"
#include "domain.h"
#include "file.h"
+#include "net.h"
#include "resource.h"
extern const char *profile_mode_names[];
@@ -145,6 +146,7 @@ struct aa_namespace {
* @size: the memory consumed by this profiles rules
* @file: The set of rules governing basic file access and domain transitions
* @caps: capabilities for the profile
+ * @net: network controls for the profile
* @rlimits: rlimits for the profile
*
* The AppArmor profile contains the basic confinement data. Each profile
@@ -181,6 +183,7 @@ struct aa_profile {
struct aa_file_rules file;
struct aa_caps caps;
+ struct aa_net net;
struct aa_rlimit rlimits;
};
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 3d2fd14..aa293ae 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -32,6 +32,7 @@
#include "include/context.h"
#include "include/file.h"
#include "include/ipc.h"
+#include "include/net.h"
#include "include/path.h"
#include "include/policy.h"
#include "include/procattr.h"
@@ -621,6 +622,104 @@ static int apparmor_task_setrlimit(struct task_struct *task,
return error;
}
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ if (kern)
+ return 0;
+
+ profile = __aa_current_profile();
+ if (!unconfined(profile))
+ error = aa_net_perm(OP_CREATE, profile, family, type, protocol,
+ NULL);
+ return error;
+}
+
+static int apparmor_socket_bind(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_BIND, sk);
+}
+
+static int apparmor_socket_connect(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_CONNECT, sk);
+}
+
+static int apparmor_socket_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_LISTEN, sk);
+}
+
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_ACCEPT, sk);
+}
+
+static int apparmor_socket_sendmsg(struct socket *sock,
+ struct msghdr *msg, int size)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_SENDMSG, sk);
+}
+
+static int apparmor_socket_recvmsg(struct socket *sock,
+ struct msghdr *msg, int size, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_RECVMSG, sk);
+}
+
+static int apparmor_socket_getsockname(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_GETSOCKNAME, sk);
+}
+
+static int apparmor_socket_getpeername(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_GETPEERNAME, sk);
+}
+
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
+ int optname)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_GETSOCKOPT, sk);
+}
+
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
+ int optname)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_SETSOCKOPT, sk);
+}
+
+static int apparmor_socket_shutdown(struct socket *sock, int how)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk);
+}
+
static struct security_operations apparmor_ops = {
.name = "apparmor",
@@ -652,6 +751,19 @@ static struct security_operations apparmor_ops = {
.getprocattr = apparmor_getprocattr,
.setprocattr = apparmor_setprocattr,
+ .socket_create = apparmor_socket_create,
+ .socket_bind = apparmor_socket_bind,
+ .socket_connect = apparmor_socket_connect,
+ .socket_listen = apparmor_socket_listen,
+ .socket_accept = apparmor_socket_accept,
+ .socket_sendmsg = apparmor_socket_sendmsg,
+ .socket_recvmsg = apparmor_socket_recvmsg,
+ .socket_getsockname = apparmor_socket_getsockname,
+ .socket_getpeername = apparmor_socket_getpeername,
+ .socket_getsockopt = apparmor_socket_getsockopt,
+ .socket_setsockopt = apparmor_socket_setsockopt,
+ .socket_shutdown = apparmor_socket_shutdown,
+
.cred_alloc_blank = apparmor_cred_alloc_blank,
.cred_free = apparmor_cred_free,
.cred_prepare = apparmor_cred_prepare,
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
new file mode 100644
index 0000000..1765901
--- /dev/null
+++ b/security/apparmor/net.c
@@ -0,0 +1,170 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2010 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#include "include/apparmor.h"
+#include "include/audit.h"
+#include "include/context.h"
+#include "include/net.h"
+#include "include/policy.h"
+
+#include "af_names.h"
+
+static const char *sock_type_names[] = {
+ "unknown(0)",
+ "stream",
+ "dgram",
+ "raw",
+ "rdm",
+ "seqpacket",
+ "dccp",
+ "unknown(7)",
+ "unknown(8)",
+ "unknown(9)",
+ "packet",
+};
+
+/* audit callback for net specific fields */
+static void audit_cb(struct audit_buffer *ab, void *va)
+{
+ struct common_audit_data *sa = va;
+
+ audit_log_format(ab, " family=");
+ if (address_family_names[sa->u.net.family]) {
+ audit_log_string(ab, address_family_names[sa->u.net.family]);
+ } else {
+ audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family);
+ }
+
+ audit_log_format(ab, " sock_type=");
+ if (sock_type_names[sa->aad.net.type]) {
+ audit_log_string(ab, sock_type_names[sa->aad.net.type]);
+ } else {
+ audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type);
+ }
+
+ audit_log_format(ab, " protocol=%d", sa->aad.net.protocol);
+}
+
+/**
+ * audit_net - audit network access
+ * @profile: profile being enforced (NOT NULL)
+ * @op: operation being checked
+ * @family: network family
+ * @type: network type
+ * @protocol: network protocol
+ * @sk: socket auditing is being applied to
+ * @error: error code for failure else 0
+ *
+ * Returns: %0 or sa->error else other errorcode on failure
+ */
+static int audit_net(struct aa_profile *profile, int op, u16 family, int type,
+ int protocol, struct sock *sk, int error)
+{
+ int audit_type = AUDIT_APPARMOR_AUTO;
+ struct common_audit_data sa;
+ if (sk) {
+ COMMON_AUDIT_DATA_INIT(&sa, NET);
+ } else {
+ COMMON_AUDIT_DATA_INIT(&sa, NONE);
+ }
+ /* todo fill in socket addr info */
+
+ sa.aad.op = op,
+ sa.u.net.family = family;
+ sa.u.net.sk = sk;
+ sa.aad.net.type = type;
+ sa.aad.net.protocol = protocol;
+ sa.aad.error = error;
+
+ if (likely(!sa.aad.error)) {
+ u16 audit_mask = profile->net.audit[sa.u.net.family];
+ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
+ !(1 << sa.aad.net.type & audit_mask)))
+ return 0;
+ audit_type = AUDIT_APPARMOR_AUDIT;
+ } else {
+ u16 quiet_mask = profile->net.quiet[sa.u.net.family];
+ u16 kill_mask = 0;
+ u16 denied = (1 << sa.aad.net.type) & ~quiet_mask;
+
+ if (denied & kill_mask)
+ audit_type = AUDIT_APPARMOR_KILL;
+
+ if ((denied & quiet_mask) &&
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
+ AUDIT_MODE(profile) != AUDIT_ALL)
+ return COMPLAIN_MODE(profile) ? 0 : sa.aad.error;
+ }
+
+ return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb);
+}
+
+/**
+ * aa_net_perm - very course network access check
+ * @op: operation being checked
+ * @profile: profile being enforced (NOT NULL)
+ * @family: network family
+ * @type: network type
+ * @protocol: network protocol
+ *
+ * Returns: %0 else error if permission denied
+ */
+int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type,
+ int protocol, struct sock *sk)
+{
+ u16 family_mask;
+ int error;
+
+ if ((family < 0) || (family >= AF_MAX))
+ return -EINVAL;
+
+ if ((type < 0) || (type >= SOCK_MAX))
+ return -EINVAL;
+
+ /* unix domain and netlink sockets are handled by ipc */
+ if (family == AF_UNIX || family == AF_NETLINK)
+ return 0;
+
+ family_mask = profile->net.allow[family];
+
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
+
+ return audit_net(profile, op, family, type, protocol, sk, error);
+}
+
+/**
+ * aa_revalidate_sk - Revalidate access to a sock
+ * @op: operation being checked
+ * @sk: sock being revalidated (NOT NULL)
+ *
+ * Returns: %0 else error if permission denied
+ */
+int aa_revalidate_sk(int op, struct sock *sk)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ /* aa_revalidate_sk should not be called from interrupt context
+ * don't mediate these calls as they are not task related
+ */
+ if (in_interrupt())
+ return 0;
+
+ profile = __aa_current_profile();
+ if (!unconfined(profile))
+ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type,
+ sk->sk_protocol, sk);
+
+ return error;
+}
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 4f0eade..4d5ce13 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile)
aa_free_file_rules(&profile->file);
aa_free_cap_rules(&profile->caps);
+ aa_free_net_rules(&profile->net);
aa_free_rlimit_rules(&profile->rlimits);
aa_free_sid(profile->sid);
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index d6d9a57..f4874c4 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -190,6 +190,19 @@ fail:
return 0;
}
+static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name)
+{
+ if (unpack_nameX(e, AA_U16, name)) {
+ if (!inbounds(e, sizeof(u16)))
+ return 0;
+ if (data)
+ *data = le16_to_cpu(get_unaligned((u16 *) e->pos));
+ e->pos += sizeof(u16);
+ return 1;
+ }
+ return 0;
+}
+
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
{
if (unpack_nameX(e, AA_U32, name)) {
@@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
{
struct aa_profile *profile = NULL;
const char *name = NULL;
- int error = -EPROTO;
+ size_t size = 0;
+ int i, error = -EPROTO;
kernel_cap_t tmpcap;
u32 tmp;
@@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
if (!unpack_rlimits(e, profile))
goto fail;
+ size = unpack_array(e, "net_allowed_af");
+ if (size) {
+
+ for (i = 0; i < size; i++) {
+ /* discard extraneous rules that this kernel will
+ * never request
+ */
+ if (i >= AF_MAX) {
+ u16 tmp;
+ if (!unpack_u16(e, &tmp, NULL) ||
+ !unpack_u16(e, &tmp, NULL) ||
+ !unpack_u16(e, &tmp, NULL))
+ goto fail;
+ continue;
+ }
+ if (!unpack_u16(e, &profile->net.allow[i], NULL))
+ goto fail;
+ if (!unpack_u16(e, &profile->net.audit[i], NULL))
+ goto fail;
+ if (!unpack_u16(e, &profile->net.quiet[i], NULL))
+ goto fail;
+ }
+ if (!unpack_nameX(e, AA_ARRAYEND, NULL))
+ goto fail;
+ /*
+ * allow unix domain and netlink sockets they are handled
+ * by IPC
+ */
+ }
+ profile->net.allow[AF_UNIX] = 0xffff;
+ profile->net.allow[AF_NETLINK] = 0xffff;
+
/* get file rules */
profile->file.dfa = unpack_dfa(e);
if (IS_ERR(profile->file.dfa)) {
--
1.7.5.4

View File

@@ -0,0 +1,391 @@
From a2515f25ad5a7833ddc5a032d34eee6a5ddee3a2 Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen@canonical.com>
Date: Wed, 10 Aug 2011 22:02:40 -0700
Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface
Signed-off-by: John Johansen <john.johansen@canonical.com>
---
security/apparmor/Kconfig | 9 +
security/apparmor/Makefile | 1 +
security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++
security/apparmor/apparmorfs.c | 18 ++-
security/apparmor/include/apparmorfs.h | 6 +
5 files changed, 319 insertions(+), 2 deletions(-)
create mode 100644 security/apparmor/apparmorfs-24.c
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
index 9b9013b..51ebf96 100644
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
boot.
If you are unsure how to answer this question, answer 1.
+
+config SECURITY_APPARMOR_COMPAT_24
+ bool "Enable AppArmor 2.4 compatability"
+ depends on SECURITY_APPARMOR
+ default y
+ help
+ This option enables compatability with AppArmor 2.4. It is
+ recommended if compatability with older versions of AppArmor
+ is desired.
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index 7cefef9..0bb604b 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
resource.o sid.o file.o net.o
+apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o
clean-files := capability_names.h rlim_names.h af_names.h
diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c
new file mode 100644
index 0000000..dc8c744
--- /dev/null
+++ b/security/apparmor/apparmorfs-24.c
@@ -0,0 +1,287 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2010 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ *
+ * This file contain functions providing an interface for <= AppArmor 2.4
+ * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24
+ * being set (see Makefile).
+ */
+
+#include <linux/security.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/namei.h>
+
+#include "include/apparmor.h"
+#include "include/audit.h"
+#include "include/context.h"
+#include "include/policy.h"
+
+
+/* apparmor/matching */
+static ssize_t aa_matching_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ const char matching[] = "pattern=aadfa audit perms=crwxamlk/ "
+ "user::other";
+
+ return simple_read_from_buffer(buf, size, ppos, matching,
+ sizeof(matching) - 1);
+}
+
+const struct file_operations aa_fs_matching_fops = {
+ .read = aa_matching_read,
+};
+
+/* apparmor/features */
+static ssize_t aa_features_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ const char features[] = "file=3.1 capability=2.0 network=1.0 "
+ "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1";
+
+ return simple_read_from_buffer(buf, size, ppos, features,
+ sizeof(features) - 1);
+}
+
+const struct file_operations aa_fs_features_fops = {
+ .read = aa_features_read,
+};
+
+/**
+ * __next_namespace - find the next namespace to list
+ * @root: root namespace to stop search at (NOT NULL)
+ * @ns: current ns position (NOT NULL)
+ *
+ * Find the next namespace from @ns under @root and handle all locking needed
+ * while switching current namespace.
+ *
+ * Returns: next namespace or NULL if at last namespace under @root
+ * NOTE: will not unlock root->lock
+ */
+static struct aa_namespace *__next_namespace(struct aa_namespace *root,
+ struct aa_namespace *ns)
+{
+ struct aa_namespace *parent;
+
+ /* is next namespace a child */
+ if (!list_empty(&ns->sub_ns)) {
+ struct aa_namespace *next;
+ next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
+ read_lock(&next->lock);
+ return next;
+ }
+
+ /* check if the next ns is a sibling, parent, gp, .. */
+ parent = ns->parent;
+ while (parent) {
+ read_unlock(&ns->lock);
+ list_for_each_entry_continue(ns, &parent->sub_ns, base.list) {
+ read_lock(&ns->lock);
+ return ns;
+ }
+ if (parent == root)
+ return NULL;
+ ns = parent;
+ parent = parent->parent;
+ }
+
+ return NULL;
+}
+
+/**
+ * __first_profile - find the first profile in a namespace
+ * @root: namespace that is root of profiles being displayed (NOT NULL)
+ * @ns: namespace to start in (NOT NULL)
+ *
+ * Returns: unrefcounted profile or NULL if no profile
+ */
+static struct aa_profile *__first_profile(struct aa_namespace *root,
+ struct aa_namespace *ns)
+{
+ for ( ; ns; ns = __next_namespace(root, ns)) {
+ if (!list_empty(&ns->base.profiles))
+ return list_first_entry(&ns->base.profiles,
+ struct aa_profile, base.list);
+ }
+ return NULL;
+}
+
+/**
+ * __next_profile - step to the next profile in a profile tree
+ * @profile: current profile in tree (NOT NULL)
+ *
+ * Perform a depth first taversal on the profile tree in a namespace
+ *
+ * Returns: next profile or NULL if done
+ * Requires: profile->ns.lock to be held
+ */
+static struct aa_profile *__next_profile(struct aa_profile *p)
+{
+ struct aa_profile *parent;
+ struct aa_namespace *ns = p->ns;
+
+ /* is next profile a child */
+ if (!list_empty(&p->base.profiles))
+ return list_first_entry(&p->base.profiles, typeof(*p),
+ base.list);
+
+ /* is next profile a sibling, parent sibling, gp, subling, .. */
+ parent = p->parent;
+ while (parent) {
+ list_for_each_entry_continue(p, &parent->base.profiles,
+ base.list)
+ return p;
+ p = parent;
+ parent = parent->parent;
+ }
+
+ /* is next another profile in the namespace */
+ list_for_each_entry_continue(p, &ns->base.profiles, base.list)
+ return p;
+
+ return NULL;
+}
+
+/**
+ * next_profile - step to the next profile in where ever it may be
+ * @root: root namespace (NOT NULL)
+ * @profile: current profile (NOT NULL)
+ *
+ * Returns: next profile or NULL if there isn't one
+ */
+static struct aa_profile *next_profile(struct aa_namespace *root,
+ struct aa_profile *profile)
+{
+ struct aa_profile *next = __next_profile(profile);
+ if (next)
+ return next;
+
+ /* finished all profiles in namespace move to next namespace */
+ return __first_profile(root, __next_namespace(root, profile->ns));
+}
+
+/**
+ * p_start - start a depth first traversal of profile tree
+ * @f: seq_file to fill
+ * @pos: current position
+ *
+ * Returns: first profile under current namespace or NULL if none found
+ *
+ * acquires first ns->lock
+ */
+static void *p_start(struct seq_file *f, loff_t *pos)
+ __acquires(root->lock)
+{
+ struct aa_profile *profile = NULL;
+ struct aa_namespace *root = aa_current_profile()->ns;
+ loff_t l = *pos;
+ f->private = aa_get_namespace(root);
+
+
+ /* find the first profile */
+ read_lock(&root->lock);
+ profile = __first_profile(root, root);
+
+ /* skip to position */
+ for (; profile && l > 0; l--)
+ profile = next_profile(root, profile);
+
+ return profile;
+}
+
+/**
+ * p_next - read the next profile entry
+ * @f: seq_file to fill
+ * @p: profile previously returned
+ * @pos: current position
+ *
+ * Returns: next profile after @p or NULL if none
+ *
+ * may acquire/release locks in namespace tree as necessary
+ */
+static void *p_next(struct seq_file *f, void *p, loff_t *pos)
+{
+ struct aa_profile *profile = p;
+ struct aa_namespace *root = f->private;
+ (*pos)++;
+
+ return next_profile(root, profile);
+}
+
+/**
+ * p_stop - stop depth first traversal
+ * @f: seq_file we are filling
+ * @p: the last profile writen
+ *
+ * Release all locking done by p_start/p_next on namespace tree
+ */
+static void p_stop(struct seq_file *f, void *p)
+ __releases(root->lock)
+{
+ struct aa_profile *profile = p;
+ struct aa_namespace *root = f->private, *ns;
+
+ if (profile) {
+ for (ns = profile->ns; ns && ns != root; ns = ns->parent)
+ read_unlock(&ns->lock);
+ }
+ read_unlock(&root->lock);
+ aa_put_namespace(root);
+}
+
+/**
+ * seq_show_profile - show a profile entry
+ * @f: seq_file to file
+ * @p: current position (profile) (NOT NULL)
+ *
+ * Returns: error on failure
+ */
+static int seq_show_profile(struct seq_file *f, void *p)
+{
+ struct aa_profile *profile = (struct aa_profile *)p;
+ struct aa_namespace *root = f->private;
+
+ if (profile->ns != root)
+ seq_printf(f, ":%s://", aa_ns_name(root, profile->ns));
+ seq_printf(f, "%s (%s)\n", profile->base.hname,
+ COMPLAIN_MODE(profile) ? "complain" : "enforce");
+
+ return 0;
+}
+
+static const struct seq_operations aa_fs_profiles_op = {
+ .start = p_start,
+ .next = p_next,
+ .stop = p_stop,
+ .show = seq_show_profile,
+};
+
+static int profiles_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &aa_fs_profiles_op);
+}
+
+static int profiles_release(struct inode *inode, struct file *file)
+{
+ return seq_release(inode, file);
+}
+
+const struct file_operations aa_fs_profiles_fops = {
+ .open = profiles_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = profiles_release,
+};
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 0848292..28c52ac 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void)
aafs_remove(".remove");
aafs_remove(".replace");
aafs_remove(".load");
-
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
+ aafs_remove("profiles");
+ aafs_remove("matching");
+ aafs_remove("features");
+#endif
securityfs_remove(aa_fs_dentry);
aa_fs_dentry = NULL;
}
@@ -218,7 +222,17 @@ int __init aa_create_aafs(void)
aa_fs_dentry = NULL;
goto error;
}
-
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
+ error = aafs_create("matching", 0444, &aa_fs_matching_fops);
+ if (error)
+ goto error;
+ error = aafs_create("features", 0444, &aa_fs_features_fops);
+ if (error)
+ goto error;
+#endif
+ error = aafs_create("profiles", 0440, &aa_fs_profiles_fops);
+ if (error)
+ goto error;
error = aafs_create(".load", 0640, &aa_fs_profile_load);
if (error)
goto error;
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index cb1e93a..14f955c 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -17,4 +17,10 @@
extern void __init aa_destroy_aafs(void);
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
+extern const struct file_operations aa_fs_matching_fops;
+extern const struct file_operations aa_fs_features_fops;
+extern const struct file_operations aa_fs_profiles_fops;
+#endif
+
#endif /* __AA_APPARMORFS_H */
--
1.7.5.4

View File

@@ -0,0 +1,69 @@
From 7a10d093f9779f42cb8d6affcb6a4436d3ebd6d3 Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen@canonical.com>
Date: Wed, 10 Aug 2011 22:02:41 -0700
Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken
userspace
The apparmor_parser when compiling policy could generate invalid dfas
that did not have sufficient padding to avoid invalid references, when
used by the kernel. The kernels check to verify the next/check table
size was broken meaning invalid dfas were being created by userspace
and not caught.
To remain compatible with old tools that are not fixed, pad the loaded
dfas next/check table. The dfa's themselves are valid except for the
high padding for potentially invalid transitions (high bounds error),
which have a maximimum is 256 entries. So just allocate an extra null filled
256 entries for the next/check tables. This will guarentee all bounds
are good and invalid transitions go to the null (0) state.
Signed-off-by: John Johansen <john.johansen@canonical.com>
---
security/apparmor/match.c | 17 +++++++++++++++++
1 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 94de6b4..081491e 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
if (bsize < tsize)
goto out;
+ /* Pad table allocation for next/check by 256 entries to remain
+ * backwards compatible with old (buggy) tools and remain safe without
+ * run time checks
+ */
+ if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK)
+ tsize += 256 * th.td_flags;
+
table = kvmalloc(tsize);
if (table) {
+ /* ensure the pad is clear, else there will be errors */
+ memset(table, 0, tsize);
*table = th;
if (th.td_flags == YYTD_DATA8)
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
@@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags)
goto out;
if (flags & DFA_FLAG_VERIFY_STATES) {
+ int warning = 0;
for (i = 0; i < state_count; i++) {
if (DEFAULT_TABLE(dfa)[i] >= state_count)
goto out;
/* TODO: do check that DEF state recursion terminates */
if (BASE_TABLE(dfa)[i] + 255 >= trans_count) {
+ if (warning)
+ continue;
+ printk(KERN_WARNING "AppArmor DFA next/check "
+ "upper bounds error fixed, upgrade "
+ "user space tools \n");
+ warning = 1;
+ } else if (BASE_TABLE(dfa)[i] >= trans_count) {
printk(KERN_ERR "AppArmor DFA next/check upper "
"bounds error\n");
goto out;
--
1.7.5.4

View File

@@ -0,0 +1,553 @@
From dc13dec93dbd04bfa7a9ba67df1b8ed3431d8d48 Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen@canonical.com>
Date: Wed, 10 Aug 2011 22:02:39 -0700
Subject: [PATCH 1/3] AppArmor: compatibility patch for v5 network controll
Add compatibility for v5 network rules.
Signed-off-by: John Johansen <john.johansen@canonical.com>
---
include/linux/lsm_audit.h | 4 +
security/apparmor/Makefile | 19 ++++-
security/apparmor/include/net.h | 40 +++++++++
security/apparmor/include/policy.h | 3 +
security/apparmor/lsm.c | 112 +++++++++++++++++++++++
security/apparmor/net.c | 170 ++++++++++++++++++++++++++++++++++++
security/apparmor/policy.c | 1 +
security/apparmor/policy_unpack.c | 48 ++++++++++-
8 files changed, 394 insertions(+), 3 deletions(-)
create mode 100644 security/apparmor/include/net.h
create mode 100644 security/apparmor/net.c
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
index 88e78de..c63979a 100644
--- a/include/linux/lsm_audit.h
+++ b/include/linux/lsm_audit.h
@@ -124,6 +124,10 @@ struct common_audit_data {
u32 denied;
uid_t ouid;
} fs;
+ struct {
+ int type, protocol;
+ struct sock *sk;
+ } net;
};
} apparmor_audit_data;
#endif
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index 2dafe50..7cefef9 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -4,9 +4,9 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
- resource.o sid.o file.o
+ resource.o sid.o file.o net.o
-clean-files := capability_names.h rlim_names.h
+clean-files := capability_names.h rlim_names.h af_names.h
# Build a lower case string table of capability names
@@ -44,9 +44,24 @@ cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ;\
sed -r -n "s/^\# ?define[ \t]+(RLIMIT_[A-Z0-9_]+).*/\1,/p" $< >> $@ ;\
echo "};" >> $@
+# Build a lower case string table of address family names.
+# Transform lines from
+# #define AF_INET 2 /* Internet IP Protocol */
+# to
+# [2] = "inet",
+quiet_cmd_make-af = GEN $@
+cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\
+ sed $< >> $@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \
+ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+).*/[\2] = "\L\1",/p';\
+ echo "};" >> $@
+
+
$(obj)/capability.o : $(obj)/capability_names.h
$(obj)/resource.o : $(obj)/rlim_names.h
+$(obj)/net.o : $(obj)/af_names.h
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
$(call cmd,make-caps)
$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h
$(call cmd,make-rlim)
+$(obj)/af_names.h : $(srctree)/include/linux/socket.h
+ $(call cmd,make-af)
\ No newline at end of file
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
new file mode 100644
index 0000000..3c7d599
--- /dev/null
+++ b/security/apparmor/include/net.h
@@ -0,0 +1,40 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation definitions.
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2010 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#ifndef __AA_NET_H
+#define __AA_NET_H
+
+#include <net/sock.h>
+
+/* struct aa_net - network confinement data
+ * @allowed: basic network families permissions
+ * @audit_network: which network permissions to force audit
+ * @quiet_network: which network permissions to quiet rejects
+ */
+struct aa_net {
+ u16 allow[AF_MAX];
+ u16 audit[AF_MAX];
+ u16 quiet[AF_MAX];
+};
+
+extern int aa_net_perm(int op, struct aa_profile *profile, u16 family,
+ int type, int protocol, struct sock *sk);
+extern int aa_revalidate_sk(int op, struct sock *sk);
+
+static inline void aa_free_net_rules(struct aa_net *new)
+{
+ /* NOP */
+}
+
+#endif /* __AA_NET_H */
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index aeda5cf..6776929 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -27,6 +27,7 @@
#include "capability.h"
#include "domain.h"
#include "file.h"
+#include "net.h"
#include "resource.h"
extern const char *profile_mode_names[];
@@ -145,6 +146,7 @@ struct aa_namespace {
* @size: the memory consumed by this profiles rules
* @file: The set of rules governing basic file access and domain transitions
* @caps: capabilities for the profile
+ * @net: network controls for the profile
* @rlimits: rlimits for the profile
*
* The AppArmor profile contains the basic confinement data. Each profile
@@ -181,6 +183,7 @@ struct aa_profile {
struct aa_file_rules file;
struct aa_caps caps;
+ struct aa_net net;
struct aa_rlimit rlimits;
};
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 3d2fd14..aa293ae 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -32,6 +32,7 @@
#include "include/context.h"
#include "include/file.h"
#include "include/ipc.h"
+#include "include/net.h"
#include "include/path.h"
#include "include/policy.h"
#include "include/procattr.h"
@@ -621,6 +622,104 @@ static int apparmor_task_setrlimit(struct task_struct *task,
return error;
}
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ if (kern)
+ return 0;
+
+ profile = __aa_current_profile();
+ if (!unconfined(profile))
+ error = aa_net_perm(OP_CREATE, profile, family, type, protocol,
+ NULL);
+ return error;
+}
+
+static int apparmor_socket_bind(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_BIND, sk);
+}
+
+static int apparmor_socket_connect(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_CONNECT, sk);
+}
+
+static int apparmor_socket_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_LISTEN, sk);
+}
+
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_ACCEPT, sk);
+}
+
+static int apparmor_socket_sendmsg(struct socket *sock,
+ struct msghdr *msg, int size)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_SENDMSG, sk);
+}
+
+static int apparmor_socket_recvmsg(struct socket *sock,
+ struct msghdr *msg, int size, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_RECVMSG, sk);
+}
+
+static int apparmor_socket_getsockname(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_GETSOCKNAME, sk);
+}
+
+static int apparmor_socket_getpeername(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_GETPEERNAME, sk);
+}
+
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
+ int optname)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_GETSOCKOPT, sk);
+}
+
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
+ int optname)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_SETSOCKOPT, sk);
+}
+
+static int apparmor_socket_shutdown(struct socket *sock, int how)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk);
+}
+
static struct security_operations apparmor_ops = {
.name = "apparmor",
@@ -652,6 +751,19 @@ static struct security_operations apparmor_ops = {
.getprocattr = apparmor_getprocattr,
.setprocattr = apparmor_setprocattr,
+ .socket_create = apparmor_socket_create,
+ .socket_bind = apparmor_socket_bind,
+ .socket_connect = apparmor_socket_connect,
+ .socket_listen = apparmor_socket_listen,
+ .socket_accept = apparmor_socket_accept,
+ .socket_sendmsg = apparmor_socket_sendmsg,
+ .socket_recvmsg = apparmor_socket_recvmsg,
+ .socket_getsockname = apparmor_socket_getsockname,
+ .socket_getpeername = apparmor_socket_getpeername,
+ .socket_getsockopt = apparmor_socket_getsockopt,
+ .socket_setsockopt = apparmor_socket_setsockopt,
+ .socket_shutdown = apparmor_socket_shutdown,
+
.cred_alloc_blank = apparmor_cred_alloc_blank,
.cred_free = apparmor_cred_free,
.cred_prepare = apparmor_cred_prepare,
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
new file mode 100644
index 0000000..1765901
--- /dev/null
+++ b/security/apparmor/net.c
@@ -0,0 +1,170 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor network mediation
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2010 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#include "include/apparmor.h"
+#include "include/audit.h"
+#include "include/context.h"
+#include "include/net.h"
+#include "include/policy.h"
+
+#include "af_names.h"
+
+static const char *sock_type_names[] = {
+ "unknown(0)",
+ "stream",
+ "dgram",
+ "raw",
+ "rdm",
+ "seqpacket",
+ "dccp",
+ "unknown(7)",
+ "unknown(8)",
+ "unknown(9)",
+ "packet",
+};
+
+/* audit callback for net specific fields */
+static void audit_cb(struct audit_buffer *ab, void *va)
+{
+ struct common_audit_data *sa = va;
+
+ audit_log_format(ab, " family=");
+ if (address_family_names[sa->u.net.family]) {
+ audit_log_string(ab, address_family_names[sa->u.net.family]);
+ } else {
+ audit_log_format(ab, " \"unknown(%d)\"", sa->u.net.family);
+ }
+
+ audit_log_format(ab, " sock_type=");
+ if (sock_type_names[sa->aad.net.type]) {
+ audit_log_string(ab, sock_type_names[sa->aad.net.type]);
+ } else {
+ audit_log_format(ab, "\"unknown(%d)\"", sa->aad.net.type);
+ }
+
+ audit_log_format(ab, " protocol=%d", sa->aad.net.protocol);
+}
+
+/**
+ * audit_net - audit network access
+ * @profile: profile being enforced (NOT NULL)
+ * @op: operation being checked
+ * @family: network family
+ * @type: network type
+ * @protocol: network protocol
+ * @sk: socket auditing is being applied to
+ * @error: error code for failure else 0
+ *
+ * Returns: %0 or sa->error else other errorcode on failure
+ */
+static int audit_net(struct aa_profile *profile, int op, u16 family, int type,
+ int protocol, struct sock *sk, int error)
+{
+ int audit_type = AUDIT_APPARMOR_AUTO;
+ struct common_audit_data sa;
+ if (sk) {
+ COMMON_AUDIT_DATA_INIT(&sa, NET);
+ } else {
+ COMMON_AUDIT_DATA_INIT(&sa, NONE);
+ }
+ /* todo fill in socket addr info */
+
+ sa.aad.op = op,
+ sa.u.net.family = family;
+ sa.u.net.sk = sk;
+ sa.aad.net.type = type;
+ sa.aad.net.protocol = protocol;
+ sa.aad.error = error;
+
+ if (likely(!sa.aad.error)) {
+ u16 audit_mask = profile->net.audit[sa.u.net.family];
+ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) &&
+ !(1 << sa.aad.net.type & audit_mask)))
+ return 0;
+ audit_type = AUDIT_APPARMOR_AUDIT;
+ } else {
+ u16 quiet_mask = profile->net.quiet[sa.u.net.family];
+ u16 kill_mask = 0;
+ u16 denied = (1 << sa.aad.net.type) & ~quiet_mask;
+
+ if (denied & kill_mask)
+ audit_type = AUDIT_APPARMOR_KILL;
+
+ if ((denied & quiet_mask) &&
+ AUDIT_MODE(profile) != AUDIT_NOQUIET &&
+ AUDIT_MODE(profile) != AUDIT_ALL)
+ return COMPLAIN_MODE(profile) ? 0 : sa.aad.error;
+ }
+
+ return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb);
+}
+
+/**
+ * aa_net_perm - very course network access check
+ * @op: operation being checked
+ * @profile: profile being enforced (NOT NULL)
+ * @family: network family
+ * @type: network type
+ * @protocol: network protocol
+ *
+ * Returns: %0 else error if permission denied
+ */
+int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type,
+ int protocol, struct sock *sk)
+{
+ u16 family_mask;
+ int error;
+
+ if ((family < 0) || (family >= AF_MAX))
+ return -EINVAL;
+
+ if ((type < 0) || (type >= SOCK_MAX))
+ return -EINVAL;
+
+ /* unix domain and netlink sockets are handled by ipc */
+ if (family == AF_UNIX || family == AF_NETLINK)
+ return 0;
+
+ family_mask = profile->net.allow[family];
+
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
+
+ return audit_net(profile, op, family, type, protocol, sk, error);
+}
+
+/**
+ * aa_revalidate_sk - Revalidate access to a sock
+ * @op: operation being checked
+ * @sk: sock being revalidated (NOT NULL)
+ *
+ * Returns: %0 else error if permission denied
+ */
+int aa_revalidate_sk(int op, struct sock *sk)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ /* aa_revalidate_sk should not be called from interrupt context
+ * don't mediate these calls as they are not task related
+ */
+ if (in_interrupt())
+ return 0;
+
+ profile = __aa_current_profile();
+ if (!unconfined(profile))
+ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type,
+ sk->sk_protocol, sk);
+
+ return error;
+}
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 4f0eade..4d5ce13 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -745,6 +745,7 @@ static void free_profile(struct aa_profile *profile)
aa_free_file_rules(&profile->file);
aa_free_cap_rules(&profile->caps);
+ aa_free_net_rules(&profile->net);
aa_free_rlimit_rules(&profile->rlimits);
aa_free_sid(profile->sid);
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index d6d9a57..f4874c4 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -190,6 +190,19 @@ fail:
return 0;
}
+static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name)
+{
+ if (unpack_nameX(e, AA_U16, name)) {
+ if (!inbounds(e, sizeof(u16)))
+ return 0;
+ if (data)
+ *data = le16_to_cpu(get_unaligned((u16 *) e->pos));
+ e->pos += sizeof(u16);
+ return 1;
+ }
+ return 0;
+}
+
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
{
if (unpack_nameX(e, AA_U32, name)) {
@@ -468,7 +481,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
{
struct aa_profile *profile = NULL;
const char *name = NULL;
- int error = -EPROTO;
+ size_t size = 0;
+ int i, error = -EPROTO;
kernel_cap_t tmpcap;
u32 tmp;
@@ -559,6 +573,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
if (!unpack_rlimits(e, profile))
goto fail;
+ size = unpack_array(e, "net_allowed_af");
+ if (size) {
+
+ for (i = 0; i < size; i++) {
+ /* discard extraneous rules that this kernel will
+ * never request
+ */
+ if (i >= AF_MAX) {
+ u16 tmp;
+ if (!unpack_u16(e, &tmp, NULL) ||
+ !unpack_u16(e, &tmp, NULL) ||
+ !unpack_u16(e, &tmp, NULL))
+ goto fail;
+ continue;
+ }
+ if (!unpack_u16(e, &profile->net.allow[i], NULL))
+ goto fail;
+ if (!unpack_u16(e, &profile->net.audit[i], NULL))
+ goto fail;
+ if (!unpack_u16(e, &profile->net.quiet[i], NULL))
+ goto fail;
+ }
+ if (!unpack_nameX(e, AA_ARRAYEND, NULL))
+ goto fail;
+ /*
+ * allow unix domain and netlink sockets they are handled
+ * by IPC
+ */
+ }
+ profile->net.allow[AF_UNIX] = 0xffff;
+ profile->net.allow[AF_NETLINK] = 0xffff;
+
/* get file rules */
profile->file.dfa = unpack_dfa(e);
if (IS_ERR(profile->file.dfa)) {
--
1.7.5.4

View File

@@ -0,0 +1,391 @@
From a2515f25ad5a7833ddc5a032d34eee6a5ddee3a2 Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen@canonical.com>
Date: Wed, 10 Aug 2011 22:02:40 -0700
Subject: [PATCH 2/3] AppArmor: compatibility patch for v5 interface
Signed-off-by: John Johansen <john.johansen@canonical.com>
---
security/apparmor/Kconfig | 9 +
security/apparmor/Makefile | 1 +
security/apparmor/apparmorfs-24.c | 287 ++++++++++++++++++++++++++++++++
security/apparmor/apparmorfs.c | 18 ++-
security/apparmor/include/apparmorfs.h | 6 +
5 files changed, 319 insertions(+), 2 deletions(-)
create mode 100644 security/apparmor/apparmorfs-24.c
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
index 9b9013b..51ebf96 100644
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -29,3 +29,12 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
boot.
If you are unsure how to answer this question, answer 1.
+
+config SECURITY_APPARMOR_COMPAT_24
+ bool "Enable AppArmor 2.4 compatability"
+ depends on SECURITY_APPARMOR
+ default y
+ help
+ This option enables compatability with AppArmor 2.4. It is
+ recommended if compatability with older versions of AppArmor
+ is desired.
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index 7cefef9..0bb604b 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
resource.o sid.o file.o net.o
+apparmor-$(CONFIG_SECURITY_APPARMOR_COMPAT_24) += apparmorfs-24.o
clean-files := capability_names.h rlim_names.h af_names.h
diff --git a/security/apparmor/apparmorfs-24.c b/security/apparmor/apparmorfs-24.c
new file mode 100644
index 0000000..dc8c744
--- /dev/null
+++ b/security/apparmor/apparmorfs-24.c
@@ -0,0 +1,287 @@
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor /sys/kernel/secrutiy/apparmor interface functions
+ *
+ * Copyright (C) 1998-2008 Novell/SUSE
+ * Copyright 2009-2010 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ *
+ * This file contain functions providing an interface for <= AppArmor 2.4
+ * compatibility. It is dependent on CONFIG_SECURITY_APPARMOR_COMPAT_24
+ * being set (see Makefile).
+ */
+
+#include <linux/security.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/namei.h>
+
+#include "include/apparmor.h"
+#include "include/audit.h"
+#include "include/context.h"
+#include "include/policy.h"
+
+
+/* apparmor/matching */
+static ssize_t aa_matching_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ const char matching[] = "pattern=aadfa audit perms=crwxamlk/ "
+ "user::other";
+
+ return simple_read_from_buffer(buf, size, ppos, matching,
+ sizeof(matching) - 1);
+}
+
+const struct file_operations aa_fs_matching_fops = {
+ .read = aa_matching_read,
+};
+
+/* apparmor/features */
+static ssize_t aa_features_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ const char features[] = "file=3.1 capability=2.0 network=1.0 "
+ "change_hat=1.5 change_profile=1.1 " "aanamespaces=1.1 rlimit=1.1";
+
+ return simple_read_from_buffer(buf, size, ppos, features,
+ sizeof(features) - 1);
+}
+
+const struct file_operations aa_fs_features_fops = {
+ .read = aa_features_read,
+};
+
+/**
+ * __next_namespace - find the next namespace to list
+ * @root: root namespace to stop search at (NOT NULL)
+ * @ns: current ns position (NOT NULL)
+ *
+ * Find the next namespace from @ns under @root and handle all locking needed
+ * while switching current namespace.
+ *
+ * Returns: next namespace or NULL if at last namespace under @root
+ * NOTE: will not unlock root->lock
+ */
+static struct aa_namespace *__next_namespace(struct aa_namespace *root,
+ struct aa_namespace *ns)
+{
+ struct aa_namespace *parent;
+
+ /* is next namespace a child */
+ if (!list_empty(&ns->sub_ns)) {
+ struct aa_namespace *next;
+ next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
+ read_lock(&next->lock);
+ return next;
+ }
+
+ /* check if the next ns is a sibling, parent, gp, .. */
+ parent = ns->parent;
+ while (parent) {
+ read_unlock(&ns->lock);
+ list_for_each_entry_continue(ns, &parent->sub_ns, base.list) {
+ read_lock(&ns->lock);
+ return ns;
+ }
+ if (parent == root)
+ return NULL;
+ ns = parent;
+ parent = parent->parent;
+ }
+
+ return NULL;
+}
+
+/**
+ * __first_profile - find the first profile in a namespace
+ * @root: namespace that is root of profiles being displayed (NOT NULL)
+ * @ns: namespace to start in (NOT NULL)
+ *
+ * Returns: unrefcounted profile or NULL if no profile
+ */
+static struct aa_profile *__first_profile(struct aa_namespace *root,
+ struct aa_namespace *ns)
+{
+ for ( ; ns; ns = __next_namespace(root, ns)) {
+ if (!list_empty(&ns->base.profiles))
+ return list_first_entry(&ns->base.profiles,
+ struct aa_profile, base.list);
+ }
+ return NULL;
+}
+
+/**
+ * __next_profile - step to the next profile in a profile tree
+ * @profile: current profile in tree (NOT NULL)
+ *
+ * Perform a depth first taversal on the profile tree in a namespace
+ *
+ * Returns: next profile or NULL if done
+ * Requires: profile->ns.lock to be held
+ */
+static struct aa_profile *__next_profile(struct aa_profile *p)
+{
+ struct aa_profile *parent;
+ struct aa_namespace *ns = p->ns;
+
+ /* is next profile a child */
+ if (!list_empty(&p->base.profiles))
+ return list_first_entry(&p->base.profiles, typeof(*p),
+ base.list);
+
+ /* is next profile a sibling, parent sibling, gp, subling, .. */
+ parent = p->parent;
+ while (parent) {
+ list_for_each_entry_continue(p, &parent->base.profiles,
+ base.list)
+ return p;
+ p = parent;
+ parent = parent->parent;
+ }
+
+ /* is next another profile in the namespace */
+ list_for_each_entry_continue(p, &ns->base.profiles, base.list)
+ return p;
+
+ return NULL;
+}
+
+/**
+ * next_profile - step to the next profile in where ever it may be
+ * @root: root namespace (NOT NULL)
+ * @profile: current profile (NOT NULL)
+ *
+ * Returns: next profile or NULL if there isn't one
+ */
+static struct aa_profile *next_profile(struct aa_namespace *root,
+ struct aa_profile *profile)
+{
+ struct aa_profile *next = __next_profile(profile);
+ if (next)
+ return next;
+
+ /* finished all profiles in namespace move to next namespace */
+ return __first_profile(root, __next_namespace(root, profile->ns));
+}
+
+/**
+ * p_start - start a depth first traversal of profile tree
+ * @f: seq_file to fill
+ * @pos: current position
+ *
+ * Returns: first profile under current namespace or NULL if none found
+ *
+ * acquires first ns->lock
+ */
+static void *p_start(struct seq_file *f, loff_t *pos)
+ __acquires(root->lock)
+{
+ struct aa_profile *profile = NULL;
+ struct aa_namespace *root = aa_current_profile()->ns;
+ loff_t l = *pos;
+ f->private = aa_get_namespace(root);
+
+
+ /* find the first profile */
+ read_lock(&root->lock);
+ profile = __first_profile(root, root);
+
+ /* skip to position */
+ for (; profile && l > 0; l--)
+ profile = next_profile(root, profile);
+
+ return profile;
+}
+
+/**
+ * p_next - read the next profile entry
+ * @f: seq_file to fill
+ * @p: profile previously returned
+ * @pos: current position
+ *
+ * Returns: next profile after @p or NULL if none
+ *
+ * may acquire/release locks in namespace tree as necessary
+ */
+static void *p_next(struct seq_file *f, void *p, loff_t *pos)
+{
+ struct aa_profile *profile = p;
+ struct aa_namespace *root = f->private;
+ (*pos)++;
+
+ return next_profile(root, profile);
+}
+
+/**
+ * p_stop - stop depth first traversal
+ * @f: seq_file we are filling
+ * @p: the last profile writen
+ *
+ * Release all locking done by p_start/p_next on namespace tree
+ */
+static void p_stop(struct seq_file *f, void *p)
+ __releases(root->lock)
+{
+ struct aa_profile *profile = p;
+ struct aa_namespace *root = f->private, *ns;
+
+ if (profile) {
+ for (ns = profile->ns; ns && ns != root; ns = ns->parent)
+ read_unlock(&ns->lock);
+ }
+ read_unlock(&root->lock);
+ aa_put_namespace(root);
+}
+
+/**
+ * seq_show_profile - show a profile entry
+ * @f: seq_file to file
+ * @p: current position (profile) (NOT NULL)
+ *
+ * Returns: error on failure
+ */
+static int seq_show_profile(struct seq_file *f, void *p)
+{
+ struct aa_profile *profile = (struct aa_profile *)p;
+ struct aa_namespace *root = f->private;
+
+ if (profile->ns != root)
+ seq_printf(f, ":%s://", aa_ns_name(root, profile->ns));
+ seq_printf(f, "%s (%s)\n", profile->base.hname,
+ COMPLAIN_MODE(profile) ? "complain" : "enforce");
+
+ return 0;
+}
+
+static const struct seq_operations aa_fs_profiles_op = {
+ .start = p_start,
+ .next = p_next,
+ .stop = p_stop,
+ .show = seq_show_profile,
+};
+
+static int profiles_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &aa_fs_profiles_op);
+}
+
+static int profiles_release(struct inode *inode, struct file *file)
+{
+ return seq_release(inode, file);
+}
+
+const struct file_operations aa_fs_profiles_fops = {
+ .open = profiles_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = profiles_release,
+};
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 0848292..28c52ac 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -187,7 +187,11 @@ void __init aa_destroy_aafs(void)
aafs_remove(".remove");
aafs_remove(".replace");
aafs_remove(".load");
-
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
+ aafs_remove("profiles");
+ aafs_remove("matching");
+ aafs_remove("features");
+#endif
securityfs_remove(aa_fs_dentry);
aa_fs_dentry = NULL;
}
@@ -218,7 +222,17 @@ int __init aa_create_aafs(void)
aa_fs_dentry = NULL;
goto error;
}
-
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
+ error = aafs_create("matching", 0444, &aa_fs_matching_fops);
+ if (error)
+ goto error;
+ error = aafs_create("features", 0444, &aa_fs_features_fops);
+ if (error)
+ goto error;
+#endif
+ error = aafs_create("profiles", 0440, &aa_fs_profiles_fops);
+ if (error)
+ goto error;
error = aafs_create(".load", 0640, &aa_fs_profile_load);
if (error)
goto error;
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index cb1e93a..14f955c 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -17,4 +17,10 @@
extern void __init aa_destroy_aafs(void);
+#ifdef CONFIG_SECURITY_APPARMOR_COMPAT_24
+extern const struct file_operations aa_fs_matching_fops;
+extern const struct file_operations aa_fs_features_fops;
+extern const struct file_operations aa_fs_profiles_fops;
+#endif
+
#endif /* __AA_APPARMORFS_H */
--
1.7.5.4

View File

@@ -0,0 +1,69 @@
From 7a10d093f9779f42cb8d6affcb6a4436d3ebd6d3 Mon Sep 17 00:00:00 2001
From: John Johansen <john.johansen@canonical.com>
Date: Wed, 10 Aug 2011 22:02:41 -0700
Subject: [PATCH 3/3] AppArmor: Allow dfa backward compatibility with broken
userspace
The apparmor_parser when compiling policy could generate invalid dfas
that did not have sufficient padding to avoid invalid references, when
used by the kernel. The kernels check to verify the next/check table
size was broken meaning invalid dfas were being created by userspace
and not caught.
To remain compatible with old tools that are not fixed, pad the loaded
dfas next/check table. The dfa's themselves are valid except for the
high padding for potentially invalid transitions (high bounds error),
which have a maximimum is 256 entries. So just allocate an extra null filled
256 entries for the next/check tables. This will guarentee all bounds
are good and invalid transitions go to the null (0) state.
Signed-off-by: John Johansen <john.johansen@canonical.com>
---
security/apparmor/match.c | 17 +++++++++++++++++
1 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 94de6b4..081491e 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -57,8 +57,17 @@ static struct table_header *unpack_table(char *blob, size_t bsize)
if (bsize < tsize)
goto out;
+ /* Pad table allocation for next/check by 256 entries to remain
+ * backwards compatible with old (buggy) tools and remain safe without
+ * run time checks
+ */
+ if (th.td_id == YYTD_ID_NXT || th.td_id == YYTD_ID_CHK)
+ tsize += 256 * th.td_flags;
+
table = kvmalloc(tsize);
if (table) {
+ /* ensure the pad is clear, else there will be errors */
+ memset(table, 0, tsize);
*table = th;
if (th.td_flags == YYTD_DATA8)
UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
@@ -134,11 +143,19 @@ static int verify_dfa(struct aa_dfa *dfa, int flags)
goto out;
if (flags & DFA_FLAG_VERIFY_STATES) {
+ int warning = 0;
for (i = 0; i < state_count; i++) {
if (DEFAULT_TABLE(dfa)[i] >= state_count)
goto out;
/* TODO: do check that DEF state recursion terminates */
if (BASE_TABLE(dfa)[i] + 255 >= trans_count) {
+ if (warning)
+ continue;
+ printk(KERN_WARNING "AppArmor DFA next/check "
+ "upper bounds error fixed, upgrade "
+ "user space tools \n");
+ warning = 1;
+ } else if (BASE_TABLE(dfa)[i] >= trans_count) {
printk(KERN_ERR "AppArmor DFA next/check upper "
"bounds error\n");
goto out;
--
1.7.5.4

View File

@@ -2,7 +2,7 @@
POD2MAN = pod2man
man_MANS = aa_change_hat.2 aa_change_profile.2
man_MANS = aa_change_hat.2 aa_change_profile.2 aa_getcon.2 aa_find_mountpoint.2
PODS = $(subst .2,.pod,$(man_MANS))
@@ -14,8 +14,6 @@ BUILT_SOURCES = $(man_MANS)
%.2: %.pod
$(POD2MAN) \
--section=2 \
--release="NOVELL/SUSE" \
--release="AppArmor $(VERSION)" \
--center="AppArmor" \
--date="2007-07-27" \
$< > $@
$

View File

@@ -29,7 +29,9 @@ aa_change_hat - change to or from a "hat" within a AppArmor profile
B<#include E<lt>sys/apparmor.hE<gt>>
B<int aa_change_hat (char *subprofile, unsigned long magic_token);>
B<int aa_change_hatv (char *subprofiles[], unsigned long magic_token);>
B<int aa_change_hat_vargs (unsigned long magic_token, ...);>
Link with B<-lapparmor> when compiling.
@@ -244,7 +246,8 @@ should be used.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2) and
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2),
aa_getcon(2) and
L<http://wiki.apparmor.net>.
=cut

View File

@@ -22,14 +22,16 @@
=head1 NAME
aa_change_profile - change to another profile within an AppArmor profile
aa_change_onexec - change to another profile at the next exec
aa_change_profile, aa_change_onexec - change a tasks profile
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
B<int aa_change_profile(const char *profile);>
B<int aa_change_onexec(const char *profile);>
Link with B<-lapparmor> when compiling.
=head1 DESCRIPTION

View File

@@ -0,0 +1,116 @@
# 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_is_enabled - determine if apparmor is available
aa_find_mountpoint - find where the apparmor interface filesystem is mounted
=head1 SYNOPSIS
B<#include E<lt>sys/apparmor.hE<gt>>
B<int aa_is_enabled(void);>
B<int aa_find_mountpoint(char **mnt);>
Link with B<-lapparmor> when compiling.
=head1 DESCRIPTION
The aa_is_enabled function returns true (1) if apparmor is enabled. If it
isn't it sets the errno to reflect the reason it is not enabled and returns 0.
The aa_find_mountpoint function finds where the apparmor filesystem is mounted
on the system, and returns a string containing the mount path. It is the
caller's responsibility to free(3) the returned path.
=head1 RETURN VALUE
B<aa_is_enabled>
On success 1 is returned. On error, 0 is returned, and errno(3) is set
appropriately.
B<aa_find_mountpoint>
On success zero is returned. On error, -1 is returned, and errno(3) is set
appropriately.
=head1 ERRORS
=over 4
B<aa_is_enabled>
=item B<ENOSYS>
AppArmor extensions to the system are not available.
=item B<ECANCELED>
AppArmor is available on the system but has been disabled at boot.
=item B<ENOENT>
AppArmor is available (and maybe even enforcing policy) but the interface is
not available.
=item B<ENOMEM>
Insufficient memory was available.
=item B<EPERM>
Did not have sufficient permissions to determine if AppArmor is enabled.
=item B<EACCES>
+Did not have sufficient permissions to determine if AppArmor is enabled.
B<aa_find_mountpoint>
=item B<ENOMEM>
Insufficient memory was available.
=item B<EACCES>
Access to the the required paths was denied.
=item B<ENOENT>
The apparmor filesystem mount could not be found
=back
=head1 BUGS
None known. If you find any, please report them at
L<http://https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), and
L<http://wiki.apparmor.net>.
=cut

View File

@@ -0,0 +1,112 @@
# 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_getprocattr_raw, aa_getprocattr - read and parse procattr data
aa_getcon, aa_gettaskcon - get task confinement information
aa_getpeercon - get the confinement of a socket's other end (peer)
=head1 SYNOPSIS
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 **buf, char **mode);>
B<int aa_gettaskcon(pid_t target, char **con, char **mode);>
B<int aa_getcon(char **con, char **mode);>
B<int aa_getpeercon(int fd, char **con);>
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 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()>.
The aa_gettaskcon function is like the aa_getcon function except it will work
for any arbitrary task in the system.
The aa_getpeercon function is similar to that of aa_gettaskcon except that
it returns the confinement information for task on the other end of a socket
connection.
The aa_getprocattr function is the backend for the aa_getcon and aa_gettaskcon
functions and handles the reading and parsing of the confinement data from
different arbitrary attr files and returns the processed results in
an allocated buffer.
The aa_getprocattr_raw() is the backend for the aa_getprocattr function and
does not handle buffer allocation.
=head1 RETURN VALUE
On success zero is returned. On error, -1 is returned, and
errno(3) is set appropriately.
=head1 ERRORS
=over 4
=item B<EINVAL>
The apparmor kernel module is not loaded or the communication via the
F</proc/*/attr/file> did not conform to protocol.
=item B<ENOMEM>
Insufficient kernel memory was available.
=item B<EACCES>
Access to the specified I<file/task> was denied.
=item B<ENOENT>
The specified I<file/task> does not exist or is not visible.
=item B<ERANGE>
The confinement data is to large to fit in the supplied buffer.
=back
=head1 BUGS
None known. If you find any, please report them at
L<http://https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
apparmor(7), apparmor.d(5), apparmor_parser(8), aa_change_profile(2) and
L<http://wiki.apparmor.net>.
=cut

View File

@@ -18,8 +18,14 @@
#ifndef _SYS_APPARMOR_H_
#define _SYS_APPARMOR_H 1
#include <sys/types.h>
__BEGIN_DECLS
/* Prototypes for apparmor state queries */
extern int aa_is_enabled(void);
extern int aa_find_mountpoint(char **mnt);
/* Prototypes for self directed domain transitions
* see <http://apparmor.net>
* Please see the change_hat(2) manpage for information.
@@ -34,6 +40,17 @@ 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, ...);
/* 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 **buf, 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 *buffer, int *size);
extern int aa_getpeercon(int fd, char **con);
#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)
#define __macroarg_count2(_,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,n,Y...) n

View File

@@ -246,7 +246,7 @@ key: TOK_KEY_OPERATION TOK_EQUALS TOK_QUOTED_STRING
{ ret_record->fsuid = $3;}
| TOK_KEY_OUID TOK_EQUALS TOK_DIGITS
{ ret_record->ouid = $3;}
| TOK_KEY_COMM TOK_EQUALS TOK_QUOTED_STRING
| TOK_KEY_COMM TOK_EQUALS safe_string
{ ret_record->comm = $3;}
| TOK_KEY_APPARMOR TOK_EQUALS apparmor_event
| TOK_KEY_CAPABILITY TOK_EQUALS TOK_DIGITS

View File

@@ -22,32 +22,283 @@
#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 "apparmor.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)
static int setprocattr(const char *path, const char *buf, int len)
/**
* 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;
}
/**
* 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 set will point to mode string within buffer if it is present
*
* 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, ctlerr = 0;
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') {
buf[size - 1] = 0;
} else if (len == 0) {
errno = ERANGE;
goto out2;
} else {
buf[size] = 0;
size++;
}
/*
* now separate the mode. If we don't find it just
* return NULL
*/
if (mode)
*mode = NULL;
if (strcmp(buf, "unconfined") != 0 &&
size > 4 && buf[size - 2] == ')') {
int pos = size - 3;
while (pos > 0 &&
!(buf[pos] == ' ' && buf[pos + 1] == '('))
pos--;
if (pos > 0) {
buf[pos] = 0; /* overwrite ' ' */
buf[size - 2] = 0; /* overwrite trailing ) */
if (mode)
*mode = &buf[pos + 2]; /* skip '(' */
}
}
}
rc = size;
out2:
(void)close(fd);
out:
return rc;
}
#define INITIAL_GUESS_SIZE 128
/**
* aa_getprocattr - get the contents of @attr for @tid into @buf
* @tid: tid of task to query
* @attr: which /proc/<tid>/attr/<attr> to query
* @buf: allocated buffer the result is stored in
* @mode: if set will point to mode string within buffer if it is present
*
* Returns: size of data read or -1 on error, and sets errno
*/
int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode)
{
int rc, size = INITIAL_GUESS_SIZE/2;
char *buffer = NULL;
if (!buf) {
errno = EINVAL;
return -1;
}
do {
size <<= 1;
buffer = realloc(buffer, size);
if (!buffer)
return -1;
memset(buffer, 0, size);
rc = aa_getprocattr_raw(tid, attr, buffer, size, mode);
} while (rc == -1 && errno == ERANGE);
if (rc == -1) {
free(buffer);
size = -1;
} else
*buf = buffer;
return size;
}
static int setprocattr(pid_t tid, const char *attr, const char *buf, int len)
{
int rc = -1;
int fd, ret;
char *ctl = NULL;
pid_t tid = syscall(SYS_gettid);
if (!buf) {
errno = EINVAL;
goto out;
}
ctlerr = asprintf(&ctl, path, tid);
if (ctlerr < 0) {
ctl = procattr_path(tid, attr);
if (!ctl)
goto out;
}
fd = open(ctl, O_WRONLY);
if (fd == -1) {
@@ -99,7 +350,7 @@ int aa_change_hat(const char *subprofile, unsigned long token)
goto out;
}
rc = setprocattr("/proc/%d/attr/current", buf, len);
rc = setprocattr(aa_gettid(), "current", buf, len);
out:
if (buf) {
/* clear local copy of magic token before freeing */
@@ -130,7 +381,7 @@ int aa_change_profile(const char *profile)
if (len < 0)
return -1;
rc = setprocattr("/proc/%d/attr/current", buf, len);
rc = setprocattr(aa_gettid(), "current", buf, len);
free(buf);
return rc;
@@ -151,7 +402,7 @@ int aa_change_onexec(const char *profile)
if (len < 0)
return -1;
rc = setprocattr("/proc/%d/attr/exec", buf, len);
rc = setprocattr(aa_gettid(), "exec", buf, len);
free(buf);
return rc;
@@ -212,7 +463,7 @@ int aa_change_hatv(const char *subprofiles[], unsigned long token)
/* step pos past trailing \0 */
pos++;
rc = setprocattr("/proc/%d/attr/current", buf, pos - buf);
rc = setprocattr(aa_gettid(), "current", buf, pos - buf);
out:
if (buf) {
@@ -251,3 +502,124 @@ int (aa_change_hat_vargs)(unsigned long token, int nhats, ...)
va_end(ap);
return aa_change_hatv(argv, token);
}
/**
* aa_gettaskcon - get the confinement for task @target in an allocated buffer
* @target: task to query
* @con: pointer to returned buffer with the confinement string
* @mode: if provided will point to the mode string in @con if present
*
* Returns: length of confinement data or -1 on error and sets errno
*
* Guarentees that @con and @mode are null terminated. The length returned
* is for all data including both @con and @mode, and maybe > than strlen(@con)
* even if @mode is NULL
*
* Caller is responsible for freeing the buffer returned in @con. @mode is
* always contained within @con's buffer and so NEVER do free(@mode)
*/
int aa_gettaskcon(pid_t target, char **con, char **mode)
{
return aa_getprocattr(target, "current", con, mode);
}
/**
* aa_getcon - get the confinement for current task in an allocated buffer
* @con: pointer to return buffer with the confinement if successful
* @mode: if provided will point to the mode string in @con if present
*
* Returns: length of confinement data or -1 on error and sets errno
*
* Guarentees that @con and @mode are null terminated. The length returned
* is for all data including both @con and @mode, and may > than strlen(@con)
* even if @mode is NULL
*
* Caller is responsible for freeing the buffer returned in @con. @mode is
* always contained within @con's buffer and so NEVER do free(@mode)
*/
int aa_getcon(char **con, char **mode)
{
return aa_gettaskcon(aa_gettid(), con, mode);
}
#ifndef SO_PEERSEC
#define SO_PEERSEC 31
#endif
/**
* aa_getpeercon_raw - get the confinement of the socket's peer (other end)
* @fd: socket to get peer confinement for
* @con: pointer to buffer to store confinement string
* @size: initially contains size of the buffer, returns size of data read
*
* Returns: length of confinement data including null termination or -1 on error
* if errno == ERANGE then @size will hold the size needed
*/
int aa_getpeercon_raw(int fd, char *buffer, int *size)
{
socklen_t optlen = *size;
int rc;
if (optlen <= 0 || buffer == NULL) {
errno = EINVAL;
return -1;
}
rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buffer, &optlen);
if (rc == -1 || optlen <= 0)
goto out;
/* check for null termination */
if (buffer[optlen - 1] != 0) {
if (optlen < *size) {
buffer[optlen] = 0;
optlen++;
} else {
/* buffer needs to be bigger by 1 */
rc = -1;
errno = ERANGE;
optlen++;
}
}
out:
*size = optlen;
return rc;
}
/**
* aa_getpeercon - get the confinement of the socket's peer (other end)
* @fd: socket to get peer confinement for
* @con: pointer to allocated buffer with the confinement string
*
* Returns: length of confinement data including null termination or -1 on error
*
* Caller is responsible for freeing the buffer returned.
*/
int aa_getpeercon(int fd, char **con)
{
int rc, size = INITIAL_GUESS_SIZE;
char *buffer = NULL;
if (!con) {
errno = EINVAL;
return -1;
}
do {
buffer = realloc(buffer, size);
if (!buffer)
return -1;
memset(buffer, 0, size);
rc = aa_getpeercon_raw(fd, buffer, &size);
} while (rc == -1 && errno == ERANGE);
if (rc == -1) {
free(buffer);
size = -1;
} else
*con = buffer;
return size;
}

View File

@@ -16,13 +16,22 @@ APPARMOR_1.0 {
APPARMOR_1.1 {
global:
aa_is_enabled;
aa_find_mountpoint;
aa_change_hat;
aa_change_hatv;
aa_change_hat_vargs;
aa_change_profile;
aa_change_onexec;
aa_gettaskcon;
aa_getcon;
aa_getpeercon_raw;
aa_getpeercon;
parse_record;
free_record;
aa_getprocattr_raw;
aa_getprocattr;
local:
*;
} APPARMOR_1.0;

View File

@@ -265,7 +265,7 @@ yy_flex_debug = 0;
{key_error} { return(TOK_KEY_ERROR); }
{key_fsuid} { return(TOK_KEY_FSUID); }
{key_ouid} { return(TOK_KEY_OUID); }
{key_comm} { return(TOK_KEY_COMM); }
{key_comm} { BEGIN(safe_string); return(TOK_KEY_COMM); }
{key_capability} { return(TOK_KEY_CAPABILITY); }
{key_capname} { return(TOK_KEY_CAPNAME); }
{key_offset} { return(TOK_KEY_OFFSET); }

View File

@@ -13,9 +13,17 @@
* are manually inserted here
*/
extern int aa_is_enabled(void);
extern int aa_find_mountpoint(char **mnt);
extern int aa_change_hat(const char *subprofile, unsigned long magic_token);
extern int aa_change_profile(const char *profile);
extern int aa_change_onexec(const char *profile);
extern int aa_change_hatv(const char *subprofiles[], unsigned long token);
extern int aa_change_hat_vargs(unsigned long token, int count, ...);
extern int aa_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 **con, char **mode);
extern int aa_getcon(char **con, char **mode);
extern int aa_getpeercon_raw(int fd, char *buffer, int *size);
extern int aa_getpeercon(int fd, char **con);

View File

@@ -13,7 +13,7 @@ setup(name = 'LibAppArmor',
ext_package = 'LibAppArmor',
ext_modules = [Extension('_LibAppArmor', ['libapparmor_wrap.c'],
include_dirs=['@top_srcdir@/src'],
extra_link_args = string.split('-L@top_builddir@/src/.libs -lapparmor'),
# static: extra_link_args = string.split('@top_builddir@/src/.libs/libapparmor.a'),
extra_link_args = '-L@top_builddir@/src/.libs -lapparmor'.split(),
# static: extra_link_args = '@top_builddir@/src/.libs/libapparmor.a'.split(),
)],
)

View File

@@ -0,0 +1 @@
type=AVC msg=audit(1322676143.201:455): apparmor="ALLOWED" operation="open" parent=10357 profile=2F686F6D652F73746576652F746D702F6D792070726F672E7368 name=2F686F6D652F73746576652F746D702F6D792070726F672E7368 pid=22918 comm=6D792070726F672E7368 requested_mask="r" denied_mask="r" fsuid=1000 ouid=1000

View File

@@ -0,0 +1,16 @@
START
File: test_multi/testcase_encoded_comm.in
Event type: AA_RECORD_ALLOWED
Audit ID: 1322676143.201:455
Operation: open
Mask: r
Denied Mask: r
fsuid: 1000
ouid: 1000
Profile: /home/steve/tmp/my prog.sh
Name: /home/steve/tmp/my prog.sh
Command: my prog.sh
Parent: 10357
PID: 22918
Epoch: 1322676143
Audit subid: 455

View File

@@ -0,0 +1 @@
Aug 23 17:29:45 hostname kernel: [289763.843292] type=1400 audit(1322614912.304:857): apparmor="ALLOWED" operation="getattr" parent=16001 profile=74657374207370616365 name="/lib/x86_64-linux-gnu/libdl-2.13.so" pid=17011 comm="bash" requested_mask="r" denied_mask="r" fsuid=0 ouid=0

View File

@@ -0,0 +1,16 @@
START
File: test_multi/testcase_encoded_profile.in
Event type: AA_RECORD_ALLOWED
Audit ID: 1322614912.304:857
Operation: getattr
Mask: r
Denied Mask: r
fsuid: 0
ouid: 0
Profile: test space
Name: /lib/x86_64-linux-gnu/libdl-2.13.so
Command: bash
Parent: 16001
PID: 17011
Epoch: 1322614912
Audit subid: 857

View File

@@ -1,15 +1,15 @@
This license applies to all source files within the AppArmor parser
package.
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@@ -18,7 +18,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
@@ -58,8 +58,8 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@@ -113,7 +113,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
@@ -171,7 +171,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
@@ -228,7 +228,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
@@ -258,7 +258,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@@ -280,9 +280,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
@@ -294,7 +294,7 @@ convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -306,17 +306,16 @@ the "copyright" line and a pointer to where the full notice is found.
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
@@ -339,5 +338,5 @@ necessary. Here is a sample; alter the names:
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -49,7 +49,7 @@ ifndef CFLAGS
CFLAGS = -g -O2 -pipe
ifdef DEBUG
CFLAGS += -pg
CFLAGS += -pg -D DEBUG
endif
endif #CFLAGS
@@ -73,29 +73,31 @@ EXTRA_CFLAGS+=-DPACKAGE=\"${NAME}\" -DLOCALEDIR=\"${LOCALEDIR}\"
# Compile-time configuration of the location of the config file
EXTRA_CFLAGS+=-DSUBDOMAIN_CONFDIR=\"${CONFDIR}\"
SRCS = parser_include.c parser_interface.c parser_lex.c parser_main.c \
parser_misc.c parser_merge.c parser_symtab.c parser_yacc.c \
parser_regex.c parser_variable.c parser_policy.c parser_alias.c
SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
parser_alias.c
HDRS = parser.h parser_include.h immunix.h
TOOLS = apparmor_parser
OBJECTS = parser_lex.o parser_yacc.o parser_main.o parser_interface.o \
parser_include.o parser_merge.o parser_symtab.o parser_misc.o \
parser_regex.o parser_variable.o parser_policy.o parser_alias.o
OBJECTS = $(SRCS:.c=.o)
AAREDIR= libapparmor_re
AAREOBJECTS = ${AAREDIR}/libapparmor_re.a
AAREOBJECT = ${AAREDIR}/libapparmor_re.a
AAREOBJECTS = $(AAREOBJECT) libstdc++.a
AARE_LDFLAGS=-static-libgcc -L.
LEX_C_FILES = parser_lex.c
YACC_C_FILES = parser_yacc.c parser_yacc.h
TESTS = tst_regex tst_misc tst_symtab tst_variable
TEST_FLAGS = -Wl,--warn-unresolved-symbols
DISABLED_TESTS =
TEST_OBJECTS = $(filter-out parser_lex.o, \
$(filter-out parser_yacc.o, \
$(filter-out parser_main.o, ${OBJECTS})))
TEST_CFLAGS = $(EXTRA_CFLAGS) -DUNIT_TEST -Wno-unused-result
TEST_OBJECTS = $(filter-out \
parser_lex.o \
parser_yacc.o \
parser_main.o, ${OBJECTS}) \
$(AAREOBJECTS)
TEST_LDFLAGS = $(AARE_LDFLAGS)
ifdef V
VERBOSE = 1
@@ -113,7 +115,7 @@ endif
export Q VERBOSE BUILD_OUTPUT
po/${NAME}.pot: ${SRCS} ${HDRS}
make -C po ${NAME}.pot NAME=${NAME} SOURCES="${SRCS} ${HDRS}"
$(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${SRCS} ${HDRS}"
techdoc.pdf: techdoc.tex
while pdflatex $< ${BUILD_OUTPUT} || exit 1 ; \
@@ -128,8 +130,7 @@ techdoc.txt: techdoc/index.html
# targets arranged this way so that people who don't want full docs can
# pick specific targets they want.
main: $(TOOLS)
$(Q)make -C po all
arch: $(TOOLS)
manpages: $(MANPAGES)
@@ -139,13 +140,20 @@ pdf: techdoc.pdf
docs: manpages htmlmanpages pdf
all: main docs tests
indep: docs
$(Q)$(MAKE) -C po all
all: arch indep
.PHONY: libstdc++.a
libstdc++.a:
rm -f ./libstdc++.a
ln -s `$(CXX) -print-file-name=libstdc++.a`
apparmor_parser: $(OBJECTS) $(AAREOBJECTS)
rm -f ./libstdc++.a
ln -s `g++ -print-file-name=libstdc++.a`
g++ $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(LIBS) \
${LEXLIB} $(AAREOBJECTS) -static-libgcc -L.
$(CXX) $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(LIBS) \
${LEXLIB} $(AAREOBJECTS) $(AARE_LDFLAGS)
parser_yacc.c parser_yacc.h: parser_yacc.y parser.h
$(YACC) $(YFLAGS) -o parser_yacc.c parser_yacc.y
@@ -189,6 +197,9 @@ parser_policy.o: parser_policy.c parser.h parser_yacc.h
parser_alias.o: parser_alias.c parser.h
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
parser_common.o: parser_common.c parser.h
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
parser_version.h: Makefile
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
@mv -f .ver $@
@@ -196,47 +207,37 @@ parser_version.h: Makefile
# These are the families that it doesn't make sense for apparmor 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_RXRPC PF_MAX PF_UNSPEC PF_UNIX PF_LOCAL PF_NETLINK PF_LLC PF_IUCV PF_TIPC PF_CAN PF_ISDN PF_PHONET
FILTER_FAMILIES=PF_MAX PF_UNSPEC PF_UNIX PF_LOCAL PF_NETLINK
__FILTER=$(shell echo $(strip $(FILTER_FAMILIES)) | sed -e 's/ /\\\|/g')
af_names.h: /usr/include/bits/socket.h
LC_ALL=C sed -n -e '/$(__FILTER)/d' -e "s/^\#define[ \\t]\\+PF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/#ifndef AF_\\1\\n# define AF_\\1 \\2\\n#endif\\nAA_GEN_NET_ENT(\"\\L\\1\", \\UAF_\\1)\\n/p" $< > $@
LC_ALL=C sed -n -e "s/^\#define[ \\t]\\+PF_MAX[ \\t]\\+\\([0-9]\\+\\)[ \\t]\\+.*/#define AA_AF_MAX \\1\n/p" $< >> $@
.PHONY: af_names.h
af_names.h:
echo "#include <sys/socket.h>" | cpp -dM | LC_ALL=C sed -n -e '/$(__FILTER)/d' -e "s/^\#define[ \\t]\\+PF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/#ifndef AF_\\1\\n# define AF_\\1 \\2\\n#endif\\nAA_GEN_NET_ENT(\"\\L\\1\", \\UAF_\\1)\\n/p" > $@
echo "#include <sys/socket.h>" | cpp -dM | LC_ALL=C sed -n -e "s/^\#define[ \\t]\\+PF_MAX[ \\t]\\+\\([0-9]\\+\\)\\+.*/#define AA_AF_MAX \\1\n/p" >> $@
# cat $@
cap_names.h: /usr/include/linux/capability.h
LC_ALL=C sed -n -e "/CAP_EMPTY_SET/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9xa-f]\\+\\)\\(.*\\)\$$/\{\"\\L\\1\", \\UCAP_\\1\},/p" $< > $@
tst_symtab: parser_symtab.c parser.h parser_variable.o
$(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< parser_variable.o $(BUILD_OUTPUT)
tst_variable: parser_variable.c parser.h $(filter-out parser_variable.o, ${TEST_OBJECTS})
$(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(filter-out parser_variable.o, ${TEST_OBJECTS}) $(BUILD_OUTPUT)
tst_misc: parser_misc.c parser.h parser_yacc.h af_names.h cap_names.h
$(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(BUILD_OUTPUT)
tst_regex: parser_regex.c parser.h parser_yacc.h
$(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(BUILD_OUTPUT)
tst_%: parser_%.c parser.h $(filter-out parser_%.o, ${TEST_OBJECTS})
$(CC) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS)
.SILENT: check
.PHONY: check
check: tests
.SILENT: tests
tests: ${TESTS}
sh -e -c 'for test in ${TESTS} ; do echo "*** running $${test}" && ./$${test} $(BUILD_OUTPUT) ; done'
$(Q)make -s -C tst tests
.SILENT: check
check: tests
tests: apparmor_parser ${TESTS}
sh -e -c 'for test in ${TESTS} ; do echo "*** running $${test}" && ./$${test}; done'
$(Q)$(MAKE) -s -C tst tests
# always need to rebuild.
.SILENT: $(AAREOBJECTS)
.PHONY: $(AAREOBJECTS)
$(AAREOBJECTS):
make -C $(AAREDIR) CFLAGS="$(EXTRA_CXXFLAGS)"
.SILENT: $(AAREOBJECT)
.PHONY: $(AAREOBJECT)
$(AAREOBJECT):
$(MAKE) -C $(AAREDIR) CFLAGS="$(EXTRA_CXXFLAGS)"
.PHONY: install-rhel4
install-rhel4: install-redhat
@@ -245,17 +246,14 @@ install-rhel4: install-redhat
install-redhat:
install -m 755 -d $(DESTDIR)/etc/init.d
install -m 755 rc.apparmor.$(subst install-,,$@) $(DESTDIR)/etc/init.d/apparmor
install -m 755 rc.aaeventd.redhat $(DESTDIR)/etc/init.d/aaeventd
.PHONY: install-suse
install-suse:
install -m 755 -d $(DESTDIR)/etc/init.d
install -m 755 rc.apparmor.$(subst install-,,$(@)) $(DESTDIR)/etc/init.d/boot.apparmor
install -m 755 rc.aaeventd.$(subst install-,,$(@)) $(DESTDIR)/etc/init.d/aaeventd
install -m 755 -d $(DESTDIR)/sbin
ln -sf /etc/init.d/boot.apparmor $(DESTDIR)/sbin/rcapparmor
ln -sf rcapparmor $(DESTDIR)/sbin/rcsubdomain
ln -sf /etc/init.d/aaeventd $(DESTDIR)/sbin/rcaaeventd
.PHONY: install-slackware
install-slackware:
@@ -270,22 +268,29 @@ install-debian:
.PHONY: install-unknown
install-unknown:
INSTALLDEPS=$(TOOLS)
INSTALLDEPS=arch
ifdef DISTRO
INSTALLDEPS+=install-$(DISTRO)
endif
.PHONY: install
install: $(INSTALLDEPS)
install: install-indep install-arch
.PHONY: install-arch
install-arch: $(INSTALLDEPS)
install -m 755 -d $(DESTDIR)/sbin
install -m 755 ${TOOLS} $(DESTDIR)/sbin
.PHONY: install-indep
install-indep:
install -m 755 -d $(INSTALL_CONFDIR)
install -m 644 subdomain.conf $(INSTALL_CONFDIR)
install -m 644 parser.conf $(INSTALL_CONFDIR)
install -m 755 -d ${DESTDIR}/var/lib/apparmor
install -m 755 -d $(APPARMOR_BIN_PREFIX)
install -m 755 rc.apparmor.functions $(APPARMOR_BIN_PREFIX)
make -C po install NAME=${NAME} DESTDIR=${DESTDIR}
make install_manpages DESTDIR=${DESTDIR}
$(MAKE) -C po install NAME=${NAME} DESTDIR=${DESTDIR}
$(MAKE) install_manpages DESTDIR=${DESTDIR}
.SILENT: clean
.PHONY: clean
@@ -296,15 +301,14 @@ clean: _clean
rm -f $(YACC_C_FILES)
rm -f parser_version.h
rm -f $(NAME)*.tar.gz $(NAME)*.tgz
rm -f libstdc++.a
rm -f af_names.h
rm -f cap_names.h
rm -rf techdoc.aux techdoc.log techdoc.pdf techdoc.toc techdor.txt techdoc/
make -s -C $(AAREDIR) clean
make -s -C po clean
make -s -C tst clean
$(MAKE) -s -C $(AAREDIR) clean
$(MAKE) -s -C po clean
$(MAKE) -s -C tst clean
.SILENT: dist_clean
dist_clean:
@make clean
@$(MAKE) clean
@rm -f $(LEX_C_FILES) $(YACC_C_FILES)

View File

@@ -103,6 +103,7 @@ make install DESTDIR=${RPM_BUILD_ROOT} \
/etc/init.d/aaeventd
%endif
%config(noreplace) /etc/apparmor/subdomain.conf
%config(noreplace) /etc/apparmor/parser.conf
/var/lib/apparmor
%dir %attr(-, root, root) %{apparmor_bin_prefix}
%{apparmor_bin_prefix}/rc.apparmor.functions

View File

@@ -83,7 +83,7 @@ B<FILEGLOB> = (must start with '/' (after variable expansion), B<?*[]{}^> have s
B<ACCESS> = ( 'r' | 'w' | 'l' | 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx -> ' I<PROGRAMCHILD> | 'Cx -> ' I<PROGRAMCHILD> | 'm' ) [ I<ACCESS> ... ] (not all combinations are allowed; see below.)
B<VARIABLE> = '@{' I<ALPHA> [ I<ALPHANUMERIC> ... ] '}'
B<VARIABLE> = '@{' I<ALPHA> [ ( I<ALPHANUMERIC> | '_' ) ... ] '}'
B<VARIABLE ASSIGNMENT> = I<VARIABLE> ('=' | '+=') (space separated values)
@@ -91,7 +91,7 @@ B<ALIAS RULE> = I<ABS PATH> '->' I<REWRITTEN ABS PATH> ','
B<ALPHA> = ('a', 'b', 'c', ... 'z', 'A', 'B', ... 'Z')
B<ALPHANUMERIC> = ('1', '2', '3', ... '9', 'a', 'b', 'c', ... 'z', 'A', 'B', ... 'Z')
B<ALPHANUMERIC> = ('0', '1', '2', ... '9', 'a', 'b', 'c', ... 'z', 'A', 'B', ... 'Z')
=back
@@ -323,10 +323,10 @@ as further information is specified.
eg.
network, #allow access to all networking
network tcp, #allow access to tcp
network inet tcp, #allow access to tcp only for inet4 addresses
network inet6 tcp, #allow access to tcp only for inet6 addresses
network, #allow access to all networking
network tcp, #allow access to tcp
network inet tcp, #allow access to tcp only for inet4 addresses
network inet6 tcp, #allow access to tcp only for inet6 addresses
=head2 Variables

View File

@@ -199,6 +199,38 @@ Give a quick reference guide.
=back
=head1 CONFIG FILE
An optional config file /etc/apparmor/parser.conf can be used to specify the
default options for the parser, which then can be overridden using the command
line options.
The config file ignores leading whitespace and treats lines that begin with #
as comments. Config options are specified one per line using the same format
as the longform command line options (without the preceding --).
Eg.
#comment
optimize=no-expr-tree
optimize=compress-fast
As with the command line some options accumulate and others override, ie. when
there are conflicting versions of switch the last option is the one chosen.
Eg.
Optimize=no-minimize
Optimize=minimize
would result in Optimize=minimize being set.
The Include, Dump, and Optimize options accululate except for the inversion
option (no-X vs. X), and a couple options that work by setting/clearing
multiple options (compress-small). In that case the option will override
the flags it sets but will may accumulate with others.
All other options override previously set values.
=head1 BUGS
If you find any bugs, please report them at

View File

@@ -12,14 +12,21 @@ BISON := bison
all : ${TARGET}
libapparmor_re.a: regexp.o
libapparmor_re.a: parse.o expr-tree.o hfa.o compressed_hfa.o aare_rules.o
ar ${ARFLAGS} $@ $^
regexp.o : regexp.cc apparmor_re.h
$(LINK.cc) $< -c -o $@
expr-tree.o: expr-tree.cc expr-tree.h
regexp.cc : regexp.y flex-tables.h ../immunix.h
hfa.o: hfa.cc apparmor_re.h hfa.h ../immunix.h
aare_rules.o: aare_rules.cc aare_rules.h apparmor_re.h expr-tree.h hfa.h compressed_hfa.h parse.h ../immunix.h
compressed_hfa.o: compressed_hfa.cc compressed_hfa.h ../immunix.h
parse.o : parse.cc apparmor_re.h expr-tree.h
parse.cc : parse.y parse.h flex-tables.h ../immunix.h
${BISON} -o $@ $<
clean:
rm -f regexp.o regexp.cc regexp.so regexp.a regexp ${TARGET}
rm -f *.o parse.cc ${TARGET}

View File

@@ -0,0 +1,314 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* 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/>.
*
*
* Wrapper around the dfa to convert aa rules into a dfa
*/
#include <ostream>
#include <iostream>
#include <fstream>
#include <sstream>
#include <ext/stdio_filebuf.h>
#include <assert.h>
#include <stdlib.h>
#include "aare_rules.h"
#include "expr-tree.h"
#include "parse.h"
#include "hfa.h"
#include "compressed_hfa.h"
#include "../immunix.h"
struct aare_ruleset {
int reverse;
Node *root;
};
extern "C" aare_ruleset_t *aare_new_ruleset(int reverse)
{
aare_ruleset_t *container = (aare_ruleset_t *) malloc(sizeof(aare_ruleset_t));
if (!container)
return NULL;
container->root = NULL;
container->reverse = reverse;
return container;
}
extern "C" void aare_delete_ruleset(aare_ruleset_t *rules)
{
if (rules) {
if (rules->root)
rules->root->release();
free(rules);
}
}
extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny,
uint32_t perms, uint32_t audit, dfaflags_t flags)
{
return aare_add_rule_vec(rules, 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 */
extern "C" 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
}
extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny,
uint32_t perms, uint32_t audit,
int count, char **rulev, dfaflags_t flags)
{
Node *tree = NULL, *accept;
int exact_match;
assert(perms != 0);
if (regex_parse(&tree, rulev[0]))
return 0;
for (int i = 1; i < count; i++) {
Node *subtree = NULL;
Node *node = new CharNode(0);
if (!node)
return 0;
tree = new CatNode(tree, node);
if (regex_parse(&subtree, rulev[i]))
return 0;
tree = new CatNode(tree, subtree);
}
/*
* Check if we have an expression with or without wildcards. This
* determines how exec modifiers are merged in accept_perms() based
* on how we split permission bitmasks here.
*/
exact_match = 1;
for (depth_first_traversal i(tree); i; i++) {
if (dynamic_cast<StarNode *>(*i) ||
dynamic_cast<PlusNode *>(*i) ||
dynamic_cast<AnyCharNode *>(*i) ||
dynamic_cast<CharSetNode *>(*i) ||
dynamic_cast<NotCharSetNode *>(*i))
exact_match = 0;
}
if (rules->reverse)
flip_tree(tree);
/* 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)
//if (perms & ALL_AA_EXEC_TYPE && (!perms & AA_EXEC_BITS))
// fprintf(stderr, "adding X rule without MAY_EXEC: 0x%x %s\n", perms, rulev[0]);
//if (perms & ALL_EXEC_TYPE)
// fprintf(stderr, "adding X rule %s 0x%x\n", rulev[0], perms);
//if (audit)
//fprintf(stderr, "adding rule with audit bits set: 0x%x %s\n", audit, rulev[0]);
//if (perms & AA_CHANGE_HAT)
// fprintf(stderr, "adding change_hat rule %s\n", rulev[0]);
/* 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 ... */
if (flags & DFA_DUMP_RULE_EXPR) {
cerr << "rule: ";
cerr << rulev[0];
for (int i = 1; i < count; i++) {
cerr << "\\x00";
cerr << rulev[i];
}
cerr << " -> ";
tree->dump(cerr);
cerr << "\n\n";
}
if (rules->root)
rules->root = new AltNode(rules->root, new CatNode(tree, accept));
else
rules->root = new CatNode(tree, accept);
return 1;
}
/* create a dfa from the ruleset
* returns: buffer contain dfa tables, @size set to the size of the tables
* else NULL on failure
*/
extern "C" void *aare_create_dfa(aare_ruleset_t *rules, size_t *size,
dfaflags_t flags)
{
char *buffer = NULL;
label_nodes(rules->root);
if (flags & DFA_DUMP_TREE) {
cerr << "\nDFA: Expression Tree\n";
rules->root->dump(cerr);
cerr << "\n\n";
}
if (flags & DFA_CONTROL_TREE_SIMPLE) {
rules->root = simplify_tree(rules->root, flags);
if (flags & DFA_DUMP_SIMPLE_TREE) {
cerr << "\nDFA: Simplified Expression Tree\n";
rules->root->dump(cerr);
cerr << "\n\n";
}
}
stringstream stream;
try {
DFA dfa(rules->root, flags);
if (flags & DFA_DUMP_UNIQ_PERMS)
dfa.dump_uniq_perms("dfa");
if (flags & DFA_CONTROL_MINIMIZE) {
dfa.minimize(flags);
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
dfa.dump_uniq_perms("minimized dfa");
}
if (flags & DFA_CONTROL_REMOVE_UNREACHABLE)
dfa.remove_unreachable(flags);
if (flags & DFA_DUMP_STATES)
dfa.dump(cerr);
if (flags & DFA_DUMP_GRAPH)
dfa.dump_dot_graph(cerr);
map<uchar, uchar> eq;
if (flags & DFA_CONTROL_EQUIV) {
eq = dfa.equivalence_classes(flags);
dfa.apply_equivalence_classes(eq);
if (flags & DFA_DUMP_EQUIV) {
cerr << "\nDFA equivalence class\n";
dump_equivalence_classes(cerr, eq);
}
} else if (flags & DFA_DUMP_EQUIV)
cerr << "\nDFA did not generate an equivalence class\n";
TransitionTable transition_table(dfa, eq, flags);
if (flags & DFA_DUMP_TRANS_TABLE)
transition_table.dump(cerr);
transition_table.flex_table(stream, "");
}
catch(int error) {
*size = 0;
return NULL;
}
stringbuf *buf = stream.rdbuf();
buf->pubseekpos(0);
*size = buf->in_avail();
buffer = (char *)malloc(*size);
if (!buffer)
return NULL;
buf->sgetn(buffer, *size);
return buffer;
}

View File

@@ -0,0 +1,49 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* 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/>.
*
*
* Wrapper around the dfa to convert aa rules into a dfa
*/
#ifndef __LIBAA_RE_RULES_H
#define __LIBAA_RE_RULES_H
#include <stdint.h>
#include "apparmor_re.h"
#ifdef __cplusplus
extern "C" {
#endif
struct aare_ruleset;
typedef struct aare_ruleset aare_ruleset_t;
aare_ruleset_t *aare_new_ruleset(int reverse);
void aare_delete_ruleset(aare_ruleset_t *rules);
int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny, uint32_t perms,
uint32_t audit, dfaflags_t flags);
int aare_add_rule_vec(aare_ruleset_t *rules, int deny, uint32_t perms,
uint32_t audit, int count, char **rulev,
dfaflags_t flags);
void *aare_create_dfa(aare_ruleset_t *rules, size_t *size, dfaflags_t flags);
void aare_reset_matchflags(void);
#ifdef __cplusplus
}
#endif
#endif /* __LIBAA_RE_RULES_H */

View File

@@ -1,11 +1,20 @@
/*
* Copyright (c) 2003, 2004, 2005, 2006, 2007 Novell, Inc.
* (All rights reserved)
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* 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.
*/
* 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 APPARMOR_RE_H
#define APPARMOR_RE_H
@@ -21,6 +30,7 @@ typedef enum dfaflags {
DFA_CONTROL_REMOVE_UNREACHABLE = 1 << 7,
DFA_CONTROL_TRANS_HIGH = 1 << 8,
DFA_DUMP_MIN_PARTS = 1 << 13,
DFA_DUMP_UNIQ_PERMS = 1 << 14,
DFA_DUMP_MIN_UNIQ_PERMS = 1 << 15,
DFA_DUMP_TREE_STATS = 1 << 16,
@@ -41,25 +51,4 @@ typedef enum dfaflags {
DFA_DUMP_NODE_TO_DFA = 1 << 31,
} dfaflags_t;
#ifdef __cplusplus
extern "C" {
#endif
struct aare_ruleset;
typedef struct aare_ruleset aare_ruleset_t;
aare_ruleset_t *aare_new_ruleset(int reverse);
void aare_delete_ruleset(aare_ruleset_t *rules);
int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny,
uint32_t perms, uint32_t audit, dfaflags_t flags);
int aare_add_rule_vec(aare_ruleset_t *rules, int deny, uint32_t perms,
uint32_t audit, int count, char **rulev, dfaflags_t flags);
void *aare_create_dfa(aare_ruleset_t *rules, size_t *size, dfaflags_t flags);
void aare_reset_matchflags(void);
#ifdef __cplusplus
}
#endif
#endif /* APPARMOR_RE_H */

View File

@@ -0,0 +1,420 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* 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/>.
*
*
* Create a compressed hfa from and hfa
*/
#include <map>
#include <vector>
#include <ostream>
#include <iostream>
#include <fstream>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include "hfa.h"
#include "compressed_hfa.h"
void TransitionTable::init_free_list(vector<pair<size_t, size_t> > &free_list,
size_t prev, size_t start)
{
for (size_t i = start; i < free_list.size(); i++) {
if (prev)
free_list[prev].second = i;
free_list[i].first = prev;
prev = i;
}
free_list[free_list.size() - 1].second = 0;
}
/**
* new Construct the transition table.
*/
TransitionTable::TransitionTable(DFA &dfa, map<uchar, uchar> &eq,
dfaflags_t flags): eq(eq)
{
if (flags & DFA_DUMP_TRANS_PROGRESS)
fprintf(stderr, "Compressing trans table:\r");
if (eq.empty())
max_eq = 255;
else {
max_eq = 0;
for (map<uchar, uchar>::iterator i = eq.begin();
i != eq.end(); i++) {
if (i->second > max_eq)
max_eq = i->second;
}
}
/* Do initial setup adding up all the transitions and sorting by
* transition count.
*/
size_t optimal = 2;
multimap<size_t, State *> order;
vector<pair<size_t, size_t> > free_list;
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
if (*i == dfa.start || *i == dfa.nonmatching)
continue;
optimal += (*i)->cases.cases.size();
if (flags & DFA_CONTROL_TRANS_HIGH) {
size_t range = 0;
if ((*i)->cases.cases.size())
range =
(*i)->cases.cases.rbegin()->first -
(*i)->cases.begin()->first;
size_t ord = ((256 - (*i)->cases.cases.size()) << 8) | (256 - range);
/* reverse sort by entry count, most entries first */
order.insert(make_pair(ord, *i));
}
}
/* Insert the dummy nonmatching transition by hand */
next_check.push_back(make_pair(dfa.nonmatching, dfa.nonmatching));
default_base.push_back(make_pair(dfa.nonmatching, 0));
num.insert(make_pair(dfa.nonmatching, num.size()));
accept.resize(dfa.states.size());
accept2.resize(dfa.states.size());
next_check.resize(optimal);
free_list.resize(optimal);
accept[0] = 0;
accept2[0] = 0;
first_free = 1;
init_free_list(free_list, 0, 1);
insert_state(free_list, dfa.start, dfa);
accept[1] = 0;
accept2[1] = 0;
num.insert(make_pair(dfa.start, num.size()));
int count = 2;
if (!(flags & DFA_CONTROL_TRANS_HIGH)) {
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
if (*i != dfa.nonmatching && *i != dfa.start) {
insert_state(free_list, *i, dfa);
accept[num.size()] = (*i)->accept;
accept2[num.size()] = (*i)->audit;
num.insert(make_pair(*i, num.size()));
}
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
count++;
if (count % 100 == 0)
fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r",
count, dfa.states.size());
}
}
} else {
for (multimap<size_t, State *>::iterator i = order.begin();
i != order.end(); i++) {
if (i->second != dfa.nonmatching &&
i->second != dfa.start) {
insert_state(free_list, i->second, dfa);
accept[num.size()] = i->second->accept;
accept2[num.size()] = i->second->audit;
num.insert(make_pair(i->second, num.size()));
}
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
count++;
if (count % 100 == 0)
fprintf(stderr, "\033[2KCompressing trans table: insert state: %d/%zd\r",
count, dfa.states.size());
}
}
}
if (flags & (DFA_DUMP_TRANS_STATS | DFA_DUMP_TRANS_PROGRESS)) {
ssize_t size = 4 * next_check.size() + 6 * dfa.states.size();
fprintf(stderr, "\033[2KCompressed trans table: states %zd, next/check %zd, optimal next/check %zd avg/state %.2f, compression %zd/%zd = %.2f %%\n",
dfa.states.size(), next_check.size(), optimal,
(float)next_check.size() / (float)dfa.states.size(),
size, 512 * dfa.states.size(),
100.0 - ((float)size * 100.0 /(float)(512 * dfa.states.size())));
}
}
/**
* Does <cases> fit into position <base> of the transition table?
*/
bool TransitionTable::fits_in(vector<pair<size_t, size_t> > &free_list
__attribute__ ((unused)), size_t pos,
Cases &cases)
{
size_t c, base = pos - cases.begin()->first;
for (Cases::iterator i = cases.begin(); i != cases.end(); i++) {
c = base + i->first;
/* if it overflows the next_check array it fits in as we will
* resize */
if (c >= next_check.size())
return true;
if (next_check[c].second)
return false;
}
return true;
}
/**
* Insert <state> of <dfa> into the transition table.
*/
void TransitionTable::insert_state(vector<pair<size_t, size_t> > &free_list,
State *from, DFA &dfa)
{
State *default_state = dfa.nonmatching;
size_t base = 0;
int resize;
Cases &cases = from->cases;
size_t c = cases.begin()->first;
size_t prev = 0;
size_t x = first_free;
if (cases.otherwise)
default_state = cases.otherwise;
if (cases.cases.empty())
goto do_insert;
repeat:
resize = 0;
/* get the first free entry that won't underflow */
while (x && (x < c)) {
prev = x;
x = free_list[x].second;
}
/* try inserting until we succeed. */
while (x && !fits_in(free_list, x, cases)) {
prev = x;
x = free_list[x].second;
}
if (!x) {
resize = 256 - cases.begin()->first;
x = free_list.size();
/* set prev to last free */
} else if (x + 255 - cases.begin()->first >= next_check.size()) {
resize = (255 - cases.begin()->first - (next_check.size() - 1 - x));
for (size_t y = x; y; y = free_list[y].second)
prev = y;
}
if (resize) {
/* expand next_check and free_list */
size_t old_size = free_list.size();
next_check.resize(next_check.size() + resize);
free_list.resize(free_list.size() + resize);
init_free_list(free_list, prev, old_size);
if (!first_free)
first_free = old_size;;
if (x == old_size)
goto repeat;
}
base = x - c;
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
next_check[base + j->first] = make_pair(j->second, from);
size_t prev = free_list[base + j->first].first;
size_t next = free_list[base + j->first].second;
if (prev)
free_list[prev].second = next;
if (next)
free_list[next].first = prev;
if (base + j->first == first_free)
first_free = next;
}
do_insert:
default_base.push_back(make_pair(default_state, base));
}
/**
* Text-dump the transition table (for debugging).
*/
void TransitionTable::dump(ostream &os)
{
map<size_t, const State *> st;
for (map<const State *, size_t>::iterator i = num.begin(); i != num.end(); i++) {
st.insert(make_pair(i->second, i->first));
}
os << "size=" << default_base.size() << " (accept, default, base): {state} -> {default state}" << "\n";
for (size_t i = 0; i < default_base.size(); i++) {
os << i << ": ";
os << "(" << accept[i] << ", " << num[default_base[i].first]
<< ", " << default_base[i].second << ")";
if (st[i])
os << " " << *st[i];
if (default_base[i].first)
os << " -> " << *default_base[i].first;
os << "\n";
}
os << "size=" << next_check.size() << " (next, check): {check state} -> {next state} : offset from base\n";
for (size_t i = 0; i < next_check.size(); i++) {
if (!next_check[i].second)
continue;
os << i << ": ";
if (next_check[i].second) {
os << "(" << num[next_check[i].first] << ", "
<< num[next_check[i].second] << ")" << " "
<< *next_check[i].second << " -> "
<< *next_check[i].first << ": ";
size_t offs = i - default_base[num[next_check[i].second]].second;
if (eq.size())
os << offs;
else
os << (uchar) offs;
}
os << "\n";
}
}
/**
* Create a flex-style binary dump of the DFA tables. The table format
* was partly reverse engineered from the flex sources and from
* examining the tables that flex creates with its --tables-file option.
* (Only the -Cf and -Ce formats are currently supported.)
*/
#include "flex-tables.h"
#define YYTH_REGEX_MAGIC 0x1B5E783D
static inline size_t pad64(size_t i)
{
return (i + (size_t) 7) & ~(size_t) 7;
}
string fill64(size_t i)
{
const char zeroes[8] = { };
string fill(zeroes, (i & 7) ? 8 - (i & 7) : 0);
return fill;
}
template<class Iter> size_t flex_table_size(Iter pos, Iter end)
{
return pad64(sizeof(struct table_header) + sizeof(*pos) * (end - pos));
}
template<class Iter>
void write_flex_table(ostream &os, int id, Iter pos, Iter end)
{
struct table_header td = { 0, 0, 0, 0 };
size_t size = end - pos;
td.td_id = htons(id);
td.td_flags = htons(sizeof(*pos));
td.td_lolen = htonl(size);
os.write((char *)&td, sizeof(td));
for (; pos != end; ++pos) {
switch (sizeof(*pos)) {
case 4:
os.put((char)(*pos >> 24));
os.put((char)(*pos >> 16));
case 2:
os.put((char)(*pos >> 8));
case 1:
os.put((char)*pos);
}
}
os << fill64(sizeof(td) + sizeof(*pos) * size);
}
void TransitionTable::flex_table(ostream &os, const char *name)
{
const char th_version[] = "notflex";
struct table_set_header th = { 0, 0, 0, 0 };
/**
* Change the following two data types to adjust the maximum flex
* table size.
*/
typedef uint16_t state_t;
typedef uint32_t trans_t;
if (default_base.size() >= (state_t) - 1) {
cerr << "Too many states (" << default_base.size() << ") for "
"type state_t\n";
exit(1);
}
if (next_check.size() >= (trans_t) - 1) {
cerr << "Too many transitions (" << next_check.size()
<< ") for " "type trans_t\n";
exit(1);
}
/**
* Create copies of the data structures so that we can dump the tables
* using the generic write_flex_table() routine.
*/
vector<uint8_t> equiv_vec;
if (eq.size()) {
equiv_vec.resize(256);
for (map<uchar, uchar>::iterator i = eq.begin(); i != eq.end(); i++) {
equiv_vec[i->first] = i->second;
}
}
vector<state_t> default_vec;
vector<trans_t> base_vec;
for (DefaultBase::iterator i = default_base.begin(); i != default_base.end(); i++) {
default_vec.push_back(num[i->first]);
base_vec.push_back(i->second);
}
vector<state_t> next_vec;
vector<state_t> check_vec;
for (NextCheck::iterator i = next_check.begin(); i != next_check.end(); i++) {
next_vec.push_back(num[i->first]);
check_vec.push_back(num[i->second]);
}
/* Write the actual flex parser table. */
size_t hsize = pad64(sizeof(th) + sizeof(th_version) + strlen(name) + 1);
th.th_magic = htonl(YYTH_REGEX_MAGIC);
th.th_hsize = htonl(hsize);
th.th_ssize = htonl(hsize +
flex_table_size(accept.begin(), accept.end()) +
flex_table_size(accept2.begin(), accept2.end()) +
(eq.size() ? flex_table_size(equiv_vec.begin(), equiv_vec.end()) : 0) +
flex_table_size(base_vec.begin(), base_vec.end()) +
flex_table_size(default_vec.begin(), default_vec.end()) +
flex_table_size(next_vec.begin(), next_vec.end()) +
flex_table_size(check_vec.begin(), check_vec.end()));
os.write((char *)&th, sizeof(th));
os << th_version << (char)0 << name << (char)0;
os << fill64(sizeof(th) + sizeof(th_version) + strlen(name) + 1);
write_flex_table(os, YYTD_ID_ACCEPT, accept.begin(), accept.end());
write_flex_table(os, YYTD_ID_ACCEPT2, accept2.begin(), accept2.end());
if (eq.size())
write_flex_table(os, YYTD_ID_EC, equiv_vec.begin(),
equiv_vec.end());
write_flex_table(os, YYTD_ID_BASE, base_vec.begin(), base_vec.end());
write_flex_table(os, YYTD_ID_DEF, default_vec.begin(), default_vec.end());
write_flex_table(os, YYTD_ID_NXT, next_vec.begin(), next_vec.end());
write_flex_table(os, YYTD_ID_CHK, check_vec.begin(), check_vec.end());
}

View File

@@ -0,0 +1,56 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* 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/>.
*
*
* Create a compressed hfa from and hfa
*/
#ifndef __LIBAA_RE_COMPRESSED_HFA_H
#define __LIBAA_RE_COMPRESSED_HFA_H
#include <map>
#include <vector>
#include "hfa.h"
using namespace std;
class TransitionTable {
typedef vector<pair<const State *, size_t> > DefaultBase;
typedef vector<pair<const State *, const State *> > NextCheck;
public:
TransitionTable(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags);
void dump(ostream & os);
void flex_table(ostream &os, const char *name);
void init_free_list(vector<pair<size_t, size_t> > &free_list,
size_t prev, size_t start);
bool fits_in(vector<pair<size_t, size_t> > &free_list, size_t base,
Cases &cases);
void insert_state(vector<pair<size_t, size_t> > &free_list,
State *state, DFA &dfa);
private:
vector<uint32_t> accept;
vector<uint32_t> accept2;
DefaultBase default_base;
NextCheck next_check;
map<const State *, size_t> num;
map<uchar, uchar> &eq;
uchar max_eq;
size_t first_free;
};
#endif /* __LIBAA_RE_COMPRESSED_HFA_H */

View File

@@ -0,0 +1,602 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* 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/>.
*
*
* Functions to create/manipulate an expression tree for regular expressions
* that have been parsed.
*
* The expression tree can be used directly after the parse creates it, or
* it can be factored so that the set of important nodes is smaller.
* Having a reduced set of important nodes generally results in a dfa that
* is closer to minimum (fewer redundant states are created). It also
* results in fewer important nodes in a the state set during subset
* construction resulting in less memory used to create a dfa.
*
* Generally it is worth doing expression tree simplification before dfa
* construction, if the regular expression tree contains any alternations.
* Even if the regular expression doesn't simplification should be fast
* enough that it can be used with minimal overhead.
*/
#include <stdio.h>
#include <string.h>
#include "expr-tree.h"
#include "apparmor_re.h"
/* Use a single static EpsNode as it carries no node specific information */
EpsNode epsnode;
ostream &operator<<(ostream &os, uchar c)
{
const char *search = "\a\033\f\n\r\t|*+[](). ",
*replace = "aefnrt|*+[](). ", *s;
if ((s = strchr(search, c)) && *s != '\0') {
os << '\\' << replace[s - search];
} else if (c < 32 || c >= 127) {
os << '\\' << '0' << char ('0' + (c >> 6))
<< char ('0' + ((c >> 3) & 7)) << char ('0' + (c & 7));
} else {
os << (char)c;
}
return os;
}
/**
* Text-dump a state (for debugging).
*/
ostream &operator<<(ostream &os, const NodeSet &state)
{
os << '{';
if (!state.empty()) {
NodeSet::iterator i = state.begin();
for (;;) {
os << (*i)->label;
if (++i == state.end())
break;
os << ',';
}
}
os << '}';
return os;
}
ostream &operator<<(ostream &os, Node &node)
{
node.dump(os);
return os;
}
/**
* hash_NodeSet - generate a hash for the Nodes in the set
*/
unsigned long hash_NodeSet(NodeSet *ns)
{
unsigned long hash = 5381;
for (NodeSet::iterator i = ns->begin(); i != ns->end(); i++) {
hash = ((hash << 5) + hash) + (unsigned long)*i;
}
return hash;
}
/**
* label_nodes - label the node positions for pretty-printing debug output
*
* TODO: separate - node labels should be separate and optional, if not
* present pretty printing should use Node address
*/
void label_nodes(Node *root)
{
int nodes = 1;
for (depth_first_traversal i(root); i; i++)
i->label = nodes++;
}
/**
* Text-dump the syntax tree (for debugging).
*/
void Node::dump_syntax_tree(ostream &os)
{
for (depth_first_traversal i(this); i; i++) {
os << i->label << '\t';
if ((*i)->child[0] == 0)
os << **i << '\t' << (*i)->followpos << endl;
else {
if ((*i)->child[1] == 0)
os << (*i)->child[0]->label << **i;
else
os << (*i)->child[0]->label << **i
<< (*i)->child[1]->label;
os << '\t' << (*i)->firstpos << (*i)->lastpos << endl;
}
}
os << endl;
}
/*
* Normalize the regex parse tree for factoring and cancelations. Normalization
* reorganizes internal (alt and cat) nodes into a fixed "normalized" form that
* simplifies factoring code, in that it produces a canonicalized form for
* the direction being normalized so that the factoring code does not have
* to consider as many cases.
*
* left normalization (dir == 0) uses these rules
* (E | a) -> (a | E)
* (a | b) | c -> a | (b | c)
* (ab)c -> a(bc)
*
* right normalization (dir == 1) uses the same rules but reversed
* (a | E) -> (E | a)
* a | (b | c) -> (a | b) | c
* a(bc) -> (ab)c
*
* Note: This is written iteratively for a given node (the top node stays
* fixed and the children are rotated) instead of recursively.
* For a given node under examination rotate over nodes from
* dir to !dir. Until no dir direction node meets the criterial.
* Then recurse to the children (which will have a different node type)
* to make sure they are normalized.
* Normalization of a child node is guarenteed to not affect the
* normalization of the parent.
*
* For cat nodes the depth first traverse order is guarenteed to be
* maintained. This is not necessary for altnodes.
*
* Eg. For left normalization
*
* |1 |1
* / \ / \
* |2 T -> a |2
* / \ / \
* |3 c b |3
* / \ / \
* a b c T
*
*/
static void rotate_node(Node *t, int dir)
{
// (a | b) | c -> a | (b | c)
// (ab)c -> a(bc)
Node *left = t->child[dir];
t->child[dir] = left->child[dir];
left->child[dir] = left->child[!dir];
left->child[!dir] = t->child[!dir];
t->child[!dir] = left;
}
void normalize_tree(Node *t, int dir)
{
if (dynamic_cast<LeafNode *>(t))
return;
for (;;) {
if ((&epsnode == t->child[dir]) &&
(&epsnode != t->child[!dir]) &&
dynamic_cast<TwoChildNode *>(t)) {
// (E | a) -> (a | E)
// Ea -> aE
Node *c = t->child[dir];
t->child[dir] = t->child[!dir];
t->child[!dir] = c;
// Don't break here as 'a' may be a tree that
// can be pulled up.
} else if ((dynamic_cast<AltNode *>(t) &&
dynamic_cast<AltNode *>(t->child[dir])) ||
(dynamic_cast<CatNode *>(t) &&
dynamic_cast<CatNode *>(t->child[dir]))) {
// (a | b) | c -> a | (b | c)
// (ab)c -> a(bc)
rotate_node(t, dir);
} else if (dynamic_cast<AltNode *>(t) &&
dynamic_cast<CharSetNode *>(t->child[dir]) &&
dynamic_cast<CharNode *>(t->child[!dir])) {
// [a] | b -> b | [a]
Node *c = t->child[dir];
t->child[dir] = t->child[!dir];
t->child[!dir] = c;
} else {
break;
}
}
if (t->child[dir])
normalize_tree(t->child[dir], dir);
if (t->child[!dir])
normalize_tree(t->child[!dir], dir);
}
//charset conversion is disabled for now,
//it hinders tree optimization in some cases, so it need to be either
//done post optimization, or have extra factoring rules added
#if 0
static Node *merge_charset(Node *a, Node *b)
{
if (dynamic_cast<CharNode *>(a) && dynamic_cast<CharNode *>(b)) {
Chars chars;
chars.insert(dynamic_cast<CharNode *>(a)->c);
chars.insert(dynamic_cast<CharNode *>(b)->c);
CharSetNode *n = new CharSetNode(chars);
return n;
} else if (dynamic_cast<CharNode *>(a) &&
dynamic_cast<CharSetNode *>(b)) {
Chars *chars = &dynamic_cast<CharSetNode *>(b)->chars;
chars->insert(dynamic_cast<CharNode *>(a)->c);
return b;
} else if (dynamic_cast<CharSetNode *>(a) &&
dynamic_cast<CharSetNode *>(b)) {
Chars *from = &dynamic_cast<CharSetNode *>(a)->chars;
Chars *to = &dynamic_cast<CharSetNode *>(b)->chars;
for (Chars::iterator i = from->begin(); i != from->end(); i++)
to->insert(*i);
return b;
}
//return ???;
}
static Node *alt_to_charsets(Node *t, int dir)
{
/*
Node *first = NULL;
Node *p = t;
Node *i = t;
for (;dynamic_cast<AltNode *>(i);) {
if (dynamic_cast<CharNode *>(i->child[dir]) ||
dynamic_cast<CharNodeSet *>(i->child[dir])) {
if (!first) {
first = i;
p = i;
i = i->child[!dir];
} else {
first->child[dir] = merge_charset(first->child[dir],
i->child[dir]);
p->child[!dir] = i->child[!dir];
Node *tmp = i;
i = tmp->child[!dir];
tmp->child[!dir] = NULL;
tmp->release();
}
} else {
p = i;
i = i->child[!dir];
}
}
// last altnode of chain check other dir as well
if (first && (dynamic_cast<charNode *>(i) ||
dynamic_cast<charNodeSet *>(i))) {
}
*/
/*
if (dynamic_cast<CharNode *>(t->child[dir]) ||
dynamic_cast<CharSetNode *>(t->child[dir]))
char_test = true;
(char_test &&
(dynamic_cast<CharNode *>(i->child[dir]) ||
dynamic_cast<CharSetNode *>(i->child[dir])))) {
*/
return t;
}
#endif
static Node *basic_alt_factor(Node *t, int dir)
{
if (!dynamic_cast<AltNode *>(t))
return t;
if (t->child[dir]->eq(t->child[!dir])) {
// (a | a) -> a
Node *tmp = t->child[dir];
t->child[dir] = NULL;
t->release();
return tmp;
}
// (ab) | (ac) -> a(b|c)
if (dynamic_cast<CatNode *>(t->child[dir]) &&
dynamic_cast<CatNode *>(t->child[!dir]) &&
t->child[dir]->child[dir]->eq(t->child[!dir]->child[dir])) {
// (ab) | (ac) -> a(b|c)
Node *left = t->child[dir];
Node *right = t->child[!dir];
t->child[dir] = left->child[!dir];
t->child[!dir] = right->child[!dir];
right->child[!dir] = NULL;
right->release();
left->child[!dir] = t;
return left;
}
// a | (ab) -> a (E | b) -> a (b | E)
if (dynamic_cast<CatNode *>(t->child[!dir]) &&
t->child[dir]->eq(t->child[!dir]->child[dir])) {
Node *c = t->child[!dir];
t->child[dir]->release();
t->child[dir] = c->child[!dir];
t->child[!dir] = &epsnode;
c->child[!dir] = t;
return c;
}
// ab | (a) -> a (b | E)
if (dynamic_cast<CatNode *>(t->child[dir]) &&
t->child[dir]->child[dir]->eq(t->child[!dir])) {
Node *c = t->child[dir];
t->child[!dir]->release();
t->child[dir] = c->child[!dir];
t->child[!dir] = &epsnode;
c->child[!dir] = t;
return c;
}
return t;
}
static Node *basic_simplify(Node *t, int dir)
{
if (dynamic_cast<CatNode *>(t) && &epsnode == t->child[!dir]) {
// aE -> a
Node *tmp = t->child[dir];
t->child[dir] = NULL;
t->release();
return tmp;
}
return basic_alt_factor(t, dir);
}
/*
* assumes a normalized tree. reductions shown for left normalization
* aE -> a
* (a | a) -> a
** factoring patterns
* a | (a | b) -> (a | b)
* a | (ab) -> a (E | b) -> a (b | E)
* (ab) | (ac) -> a(b|c)
*
* returns t - if no simplifications were made
* a new root node - if simplifications were made
*/
Node *simplify_tree_base(Node *t, int dir, bool &mod)
{
if (dynamic_cast<ImportantNode *>(t))
return t;
for (int i = 0; i < 2; i++) {
if (t->child[i]) {
Node *c = simplify_tree_base(t->child[i], dir, mod);
if (c != t->child[i]) {
t->child[i] = c;
mod = true;
}
}
}
// only iterate on loop if modification made
for (;; mod = true) {
Node *tmp = basic_simplify(t, dir);
if (tmp != t) {
t = tmp;
continue;
}
/* all tests after this must meet 2 alt node condition */
if (!dynamic_cast<AltNode *>(t) ||
!dynamic_cast<AltNode *>(t->child[!dir]))
break;
// a | (a | b) -> (a | b)
// a | (b | (c | a)) -> (b | (c | a))
Node *p = t;
Node *i = t->child[!dir];
for (; dynamic_cast<AltNode *>(i); p = i, i = i->child[!dir]) {
if (t->child[dir]->eq(i->child[dir])) {
Node *tmp = t->child[!dir];
t->child[!dir] = NULL;
t->release();
t = tmp;
continue;
}
}
// last altnode of chain check other dir as well
if (t->child[dir]->eq(p->child[!dir])) {
Node *tmp = t->child[!dir];
t->child[!dir] = NULL;
t->release();
t = tmp;
continue;
}
//exact match didn't work, try factoring front
//a | (ac | (ad | () -> (a (E | c)) | (...)
//ab | (ac | (...)) -> (a (b | c)) | (...)
//ab | (a | (...)) -> (a (b | E)) | (...)
Node *pp;
int count = 0;
Node *subject = t->child[dir];
Node *a = subject;
if (dynamic_cast<CatNode *>(subject))
a = subject->child[dir];
for (pp = p = t, i = t->child[!dir];
dynamic_cast<AltNode *>(i);) {
if ((dynamic_cast<CatNode *>(i->child[dir]) &&
a->eq(i->child[dir]->child[dir])) ||
(a->eq(i->child[dir]))) {
// extract matching alt node
p->child[!dir] = i->child[!dir];
i->child[!dir] = subject;
subject = basic_simplify(i, dir);
if (dynamic_cast<CatNode *>(subject))
a = subject->child[dir];
else
a = subject;
i = p->child[!dir];
count++;
} else {
pp = p;
p = i;
i = i->child[!dir];
}
}
// last altnode in chain check other dir as well
if ((dynamic_cast<CatNode *>(i) &&
a->eq(i->child[dir])) || (a->eq(i))) {
count++;
if (t == p) {
t->child[dir] = subject;
t = basic_simplify(t, dir);
} else {
t->child[dir] = p->child[dir];
p->child[dir] = subject;
pp->child[!dir] = basic_simplify(p, dir);
}
} else {
t->child[dir] = i;
p->child[!dir] = subject;
}
if (count == 0)
break;
}
return t;
}
int debug_tree(Node *t)
{
int nodes = 1;
if (!dynamic_cast<ImportantNode *>(t)) {
if (t->child[0])
nodes += debug_tree(t->child[0]);
if (t->child[1])
nodes += debug_tree(t->child[1]);
}
return nodes;
}
static void count_tree_nodes(Node *t, struct node_counts *counts)
{
if (dynamic_cast<AltNode *>(t)) {
counts->alt++;
count_tree_nodes(t->child[0], counts);
count_tree_nodes(t->child[1], counts);
} else if (dynamic_cast<CatNode *>(t)) {
counts->cat++;
count_tree_nodes(t->child[0], counts);
count_tree_nodes(t->child[1], counts);
} else if (dynamic_cast<PlusNode *>(t)) {
counts->plus++;
count_tree_nodes(t->child[0], counts);
} else if (dynamic_cast<StarNode *>(t)) {
counts->star++;
count_tree_nodes(t->child[0], counts);
} else if (dynamic_cast<CharNode *>(t)) {
counts->charnode++;
} else if (dynamic_cast<AnyCharNode *>(t)) {
counts->any++;
} else if (dynamic_cast<CharSetNode *>(t)) {
counts->charset++;
} else if (dynamic_cast<NotCharSetNode *>(t)) {
counts->notcharset++;
}
}
#include "stdio.h"
#include "stdint.h"
#include "apparmor_re.h"
Node *simplify_tree(Node *t, dfaflags_t flags)
{
bool update;
if (flags & DFA_DUMP_TREE_STATS) {
struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0 };
count_tree_nodes(t, &counts);
fprintf(stderr,
"expr tree: c %d, [] %d, [^] %d, | %d, + %d, * %d, . %d, cat %d\n",
counts.charnode, counts.charset, counts.notcharset,
counts.alt, counts.plus, counts.star, counts.any,
counts.cat);
}
do {
update = false;
//default to right normalize first as this reduces the number
//of trailing nodes which might follow an internal *
//or **, which is where state explosion can happen
//eg. in one test this makes the difference between
// the dfa having about 7 thousands states,
// and it having about 1.25 million states
int dir = 1;
if (flags & DFA_CONTROL_TREE_LEFT)
dir = 0;
for (int count = 0; count < 2; count++) {
bool modified;
do {
modified = false;
if (flags & DFA_CONTROL_TREE_NORMAL)
normalize_tree(t, dir);
t = simplify_tree_base(t, dir, modified);
if (modified)
update = true;
} while (modified);
if (flags & DFA_CONTROL_TREE_LEFT)
dir++;
else
dir--;
}
} while (update);
if (flags & DFA_DUMP_TREE_STATS) {
struct node_counts counts = { 0, 0, 0, 0, 0, 0, 0, 0 };
count_tree_nodes(t, &counts);
fprintf(stderr,
"simplified expr tree: c %d, [] %d, [^] %d, | %d, + %d, * %d, . %d, cat %d\n",
counts.charnode, counts.charset, counts.notcharset,
counts.alt, counts.plus, counts.star, counts.any,
counts.cat);
}
return t;
}
/**
* Flip the children of all cat nodes. This causes strings to be matched
* back-forth.
*/
void flip_tree(Node *node)
{
for (depth_first_traversal i(node); i; i++) {
if (CatNode *cat = dynamic_cast<CatNode *>(*i)) {
swap(cat->child[0], cat->child[1]);
}
}
}
void dump_regex_rec(ostream &os, Node *tree)
{
if (tree->child[0])
dump_regex_rec(os, tree->child[0]);
os << *tree;
if (tree->child[1])
dump_regex_rec(os, tree->child[1]);
}
void dump_regex(ostream &os, Node *tree)
{
dump_regex_rec(os, tree);
os << endl;
}

View File

@@ -0,0 +1,612 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* 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/>.
*
*
* Functions to create/manipulate an expression tree for regular expressions
* that have been parsed.
*
* The expression tree can be used directly after the parse creates it, or
* it can be factored so that the set of important nodes is smaller.
* Having a reduced set of important nodes generally results in a dfa that
* is closer to minimum (fewer redundant states are created). It also
* results in fewer important nodes in a the state set during subset
* construction resulting in less memory used to create a dfa.
*
* Generally it is worth doing expression tree simplification before dfa
* construction, if the regular expression tree contains any alternations.
* Even if the regular expression doesn't simplification should be fast
* enough that it can be used with minimal overhead.
*/
#ifndef __LIBAA_RE_EXPR_H
#define __LIBAA_RE_EXPR_H
#include <map>
#include <set>
#include <stack>
#include <ostream>
#include <stdint.h>
#include "apparmor_re.h"
using namespace std;
typedef unsigned char uchar;
typedef set<uchar> Chars;
ostream &operator<<(ostream &os, uchar c);
/* Compute the union of two sets. */
template<class T> set<T> operator+(const set<T> &a, const set<T> &b)
{
set<T> c(a);
c.insert(b.begin(), b.end());
return c;
}
/**
* When creating DFAs from regex trees, a DFA state is constructed from
* a set of important nodes in the syntax tree. This includes AcceptNodes,
* which indicate that when a match ends in a particular state, the
* regular expressions that the AcceptNode belongs to match.
*/
class Node;
class ImportantNode;
typedef set<ImportantNode *> NodeSet;
/**
* Text-dump a state (for debugging).
*/
ostream &operator<<(ostream &os, const NodeSet &state);
/**
* Out-edges from a state to another: we store the follow-set of Nodes
* for each input character that is not a default match in
* cases (i.e., following a CharNode or CharSetNode), and default
* matches in otherwise as well as in all matching explicit cases
* (i.e., following an AnyCharNode or NotCharSetNode). This avoids
* enumerating all the explicit tranitions for default matches.
*/
typedef struct NodeCases {
typedef map<uchar, NodeSet *>::iterator iterator;
iterator begin() { return cases.begin(); }
iterator end() { return cases.end(); }
NodeCases(): otherwise(0) { }
map<uchar, NodeSet *> cases;
NodeSet *otherwise;
}
NodeCases;
ostream &operator<<(ostream &os, Node &node);
/* An abstract node in the syntax tree. */
class Node {
public:
Node(): nullable(false) { child[0] = child[1] = 0; }
Node(Node *left): nullable(false)
{
child[0] = left;
child[1] = 0;
}
Node(Node *left, Node *right): nullable(false)
{
child[0] = left;
child[1] = right;
}
virtual ~Node()
{
if (child[0])
child[0]->release();
if (child[1])
child[1]->release();
}
/**
* See the "Dragon Book" for an explanation of nullable, firstpos,
* lastpos, and followpos.
*/
virtual void compute_nullable() { }
virtual void compute_firstpos() = 0;
virtual void compute_lastpos() = 0;
virtual void compute_followpos() { }
virtual int eq(Node *other) = 0;
virtual ostream &dump(ostream &os) = 0;
void dump_syntax_tree(ostream &os);
bool nullable;
NodeSet firstpos, lastpos, followpos;
/* child 0 is left, child 1 is right */
Node *child[2];
unsigned int label; /* unique number for debug etc */
/**
* We indirectly release Nodes through a virtual function because
* accept and Eps Nodes are shared, and must be treated specially.
* We could use full reference counting here but the indirect release
* is sufficient and has less overhead
*/
virtual void release(void) { delete this; }
};
class InnerNode: public Node {
public:
InnerNode(): Node() { };
InnerNode(Node *left): Node(left) { };
InnerNode(Node *left, Node *right): Node(left, right) { };
};
class OneChildNode: public InnerNode {
public:
OneChildNode(Node *left): InnerNode(left) { };
};
class TwoChildNode: public InnerNode {
public:
TwoChildNode(Node *left, Node *right): InnerNode(left, right) { };
};
class LeafNode: public Node {
public:
LeafNode(): Node() { };
};
/* Match nothing (//). */
class EpsNode: public LeafNode {
public:
EpsNode(): LeafNode()
{
nullable = true;
label = 0;
}
void release(void)
{
/* don't delete Eps nodes because there is a single static
* instance shared by all trees. Look for epsnode in the code
*/
}
void compute_firstpos() { }
void compute_lastpos() { }
int eq(Node *other)
{
if (dynamic_cast<EpsNode *>(other))
return 1;
return 0;
}
ostream &dump(ostream &os)
{
return os << "[]";
}
};
/**
* Leaf nodes in the syntax tree are important to us: they describe the
* characters that the regular expression matches. We also consider
* AcceptNodes import: they indicate when a regular expression matches.
*/
class ImportantNode: public LeafNode {
public:
ImportantNode(): LeafNode() { }
void compute_firstpos() { firstpos.insert(this); }
void compute_lastpos() { lastpos.insert(this); }
virtual void follow(NodeCases &cases) = 0;
};
/* common base class for all the different classes that contain
* character information.
*/
class CNode: public ImportantNode {
public:
CNode(): ImportantNode() { }
};
/* Match one specific character (/c/). */
class CharNode: public CNode {
public:
CharNode(uchar c): c(c) { }
void follow(NodeCases &cases)
{
NodeSet **x = &cases.cases[c];
if (!*x) {
if (cases.otherwise)
*x = new NodeSet(*cases.otherwise);
else
*x = new NodeSet;
}
(*x)->insert(followpos.begin(), followpos.end());
}
int eq(Node *other)
{
CharNode *o = dynamic_cast<CharNode *>(other);
if (o) {
return c == o->c;
}
return 0;
}
ostream &dump(ostream &os)
{
return os << c;
}
uchar c;
};
/* Match a set of characters (/[abc]/). */
class CharSetNode: public CNode {
public:
CharSetNode(Chars &chars): chars(chars) { }
void follow(NodeCases &cases)
{
for (Chars::iterator i = chars.begin(); i != chars.end(); i++) {
NodeSet **x = &cases.cases[*i];
if (!*x) {
if (cases.otherwise)
*x = new NodeSet(*cases.otherwise);
else
*x = new NodeSet;
}
(*x)->insert(followpos.begin(), followpos.end());
}
}
int eq(Node *other)
{
CharSetNode *o = dynamic_cast<CharSetNode *>(other);
if (!o || chars.size() != o->chars.size())
return 0;
for (Chars::iterator i = chars.begin(), j = o->chars.begin();
i != chars.end() && j != o->chars.end(); i++, j++) {
if (*i != *j)
return 0;
}
return 1;
}
ostream &dump(ostream &os)
{
os << '[';
for (Chars::iterator i = chars.begin(); i != chars.end(); i++)
os << *i;
return os << ']';
}
Chars chars;
};
/* Match all except one character (/[^abc]/). */
class NotCharSetNode: public CNode {
public:
NotCharSetNode(Chars &chars): chars(chars) { }
void follow(NodeCases & cases)
{
if (!cases.otherwise)
cases.otherwise = new NodeSet;
for (Chars::iterator j = chars.begin(); j != chars.end(); j++) {
NodeSet **x = &cases.cases[*j];
if (!*x)
*x = new NodeSet(*cases.otherwise);
}
/* Note: Add to the nonmatching characters after copying away
* the old otherwise state for the matching characters.
*/
cases.otherwise->insert(followpos.begin(), followpos.end());
for (NodeCases::iterator i = cases.begin(); i != cases.end();
i++) {
if (chars.find(i->first) == chars.end())
i->second->insert(followpos.begin(),
followpos.end());
}
}
int eq(Node *other)
{
NotCharSetNode *o = dynamic_cast<NotCharSetNode *>(other);
if (!o || chars.size() != o->chars.size())
return 0;
for (Chars::iterator i = chars.begin(), j = o->chars.begin();
i != chars.end() && j != o->chars.end(); i++, j++) {
if (*i != *j)
return 0;
}
return 1;
}
ostream &dump(ostream &os)
{
os << "[^";
for (Chars::iterator i = chars.begin(); i != chars.end(); i++)
os << *i;
return os << ']';
}
Chars chars;
};
/* Match any character (/./). */
class AnyCharNode: public CNode {
public:
AnyCharNode() { }
void follow(NodeCases &cases)
{
if (!cases.otherwise)
cases.otherwise = new NodeSet;
cases.otherwise->insert(followpos.begin(), followpos.end());
for (NodeCases::iterator i = cases.begin(); i != cases.end();
i++)
i->second->insert(followpos.begin(), followpos.end());
}
int eq(Node *other)
{
if (dynamic_cast<AnyCharNode *>(other))
return 1;
return 0;
}
ostream &dump(ostream &os) { return os << "."; }
};
/**
* Indicate that a regular expression matches. An AcceptNode itself
* doesn't match anything, so it will never generate any transitions.
*/
class AcceptNode: public ImportantNode {
public:
AcceptNode() { }
void release(void)
{
/* don't delete AcceptNode via release as they are shared, and
* will be deleted when the table the are stored in is deleted
*/
}
void follow(NodeCases &cases __attribute__ ((unused)))
{
/* Nothing to follow. */
}
/* requires accept nodes to be common by pointer */
int eq(Node *other)
{
if (dynamic_cast<AcceptNode *>(other))
return (this == other);
return 0;
}
};
/* Match a node zero or more times. (This is a unary operator.) */
class StarNode: public OneChildNode {
public:
StarNode(Node *left): OneChildNode(left) { nullable = true; }
void compute_firstpos() { firstpos = child[0]->firstpos; }
void compute_lastpos() { lastpos = child[0]->lastpos; }
void compute_followpos()
{
NodeSet from = child[0]->lastpos, to = child[0]->firstpos;
for (NodeSet::iterator i = from.begin(); i != from.end(); i++) {
(*i)->followpos.insert(to.begin(), to.end());
}
}
int eq(Node *other)
{
if (dynamic_cast<StarNode *>(other))
return child[0]->eq(other->child[0]);
return 0;
}
ostream &dump(ostream &os)
{
os << '(';
child[0]->dump(os);
return os << ")*";
}
};
/* Match a node one or more times. (This is a unary operator.) */
class PlusNode: public OneChildNode {
public:
PlusNode(Node *left): OneChildNode(left) {
}
void compute_nullable() { nullable = child[0]->nullable; }
void compute_firstpos() { firstpos = child[0]->firstpos; }
void compute_lastpos() { lastpos = child[0]->lastpos; }
void compute_followpos()
{
NodeSet from = child[0]->lastpos, to = child[0]->firstpos;
for (NodeSet::iterator i = from.begin(); i != from.end(); i++) {
(*i)->followpos.insert(to.begin(), to.end());
}
}
int eq(Node *other) {
if (dynamic_cast<PlusNode *>(other))
return child[0]->eq(other->child[0]);
return 0;
}
ostream &dump(ostream &os) {
os << '(';
child[0]->dump(os);
return os << ")+";
}
};
/* Match a pair of consecutive nodes. */
class CatNode: public TwoChildNode {
public:
CatNode(Node *left, Node *right): TwoChildNode(left, right) { }
void compute_nullable()
{
nullable = child[0]->nullable && child[1]->nullable;
}
void compute_firstpos()
{
if (child[0]->nullable)
firstpos = child[0]->firstpos + child[1]->firstpos;
else
firstpos = child[0]->firstpos;
}
void compute_lastpos()
{
if (child[1]->nullable)
lastpos = child[0]->lastpos + child[1]->lastpos;
else
lastpos = child[1]->lastpos;
}
void compute_followpos()
{
NodeSet from = child[0]->lastpos, to = child[1]->firstpos;
for (NodeSet::iterator i = from.begin(); i != from.end(); i++) {
(*i)->followpos.insert(to.begin(), to.end());
}
}
int eq(Node *other)
{
if (dynamic_cast<CatNode *>(other)) {
if (!child[0]->eq(other->child[0]))
return 0;
return child[1]->eq(other->child[1]);
}
return 0;
}
ostream &dump(ostream &os)
{
child[0]->dump(os);
child[1]->dump(os);
return os;
}
};
/* Match one of two alternative nodes. */
class AltNode: public TwoChildNode {
public:
AltNode(Node *left, Node *right): TwoChildNode(left, right) { }
void compute_nullable()
{
nullable = child[0]->nullable || child[1]->nullable;
}
void compute_lastpos()
{
lastpos = child[0]->lastpos + child[1]->lastpos;
}
void compute_firstpos()
{
firstpos = child[0]->firstpos + child[1]->firstpos;
}
int eq(Node *other)
{
if (dynamic_cast<AltNode *>(other)) {
if (!child[0]->eq(other->child[0]))
return 0;
return child[1]->eq(other->child[1]);
}
return 0;
}
ostream &dump(ostream &os)
{
os << '(';
child[0]->dump(os);
os << '|';
child[1]->dump(os);
os << ')';
return os;
}
};
/* Traverse the syntax tree depth-first in an iterator-like manner. */
class depth_first_traversal {
stack<Node *>pos;
void push_left(Node *node) {
pos.push(node);
while (dynamic_cast<InnerNode *>(node)) {
pos.push(node->child[0]);
node = node->child[0];
}
}
public:
depth_first_traversal(Node *node) { push_left(node); }
Node *operator*() { return pos.top(); }
Node *operator->() { return pos.top(); }
operator bool() { return !pos.empty(); }
void operator++(int)
{
Node *last = pos.top();
pos.pop();
if (!pos.empty()) {
/* no need to dynamic cast, as we just popped a node so
* the top node must be an inner node */
InnerNode *node = (InnerNode *) (pos.top());
if (node->child[1] && node->child[1] != last) {
push_left(node->child[1]);
}
}
}
};
struct node_counts {
int charnode;
int charset;
int notcharset;
int alt;
int plus;
int star;
int any;
int cat;
};
extern EpsNode epsnode;
int debug_tree(Node *t);
Node *simplify_tree(Node *t, dfaflags_t flags);
void label_nodes(Node *root);
unsigned long hash_NodeSet(NodeSet *ns);
void flip_tree(Node *node);
/* Comparison operator for sets of <NodeSet *>.
* Compare set hashes, and if the sets have the same hash
* do compare pointer comparison on set of <Node *>, the pointer comparison
* allows us to determine which Sets of <Node *> we have seen already from
* new ones when constructing the DFA.
*/
struct deref_less_than {
bool operator()(pair<unsigned long, NodeSet *>const &lhs,
pair<unsigned long, NodeSet *>const &rhs)const
{
if (lhs.first == rhs.first)
return *(lhs.second) < *(rhs.second);
else
return lhs.first < rhs.first;
}
};
class MatchFlag: public AcceptNode {
public:
MatchFlag(uint32_t flag, uint32_t audit): flag(flag), audit(audit) { }
ostream &dump(ostream &os) { return os << '<' << flag << '>'; }
uint32_t flag;
uint32_t audit;
};
class ExactMatchFlag: public MatchFlag {
public:
ExactMatchFlag(uint32_t flag, uint32_t audit): MatchFlag(flag, audit) {}
};
class DenyMatchFlag: public MatchFlag {
public:
DenyMatchFlag(uint32_t flag, uint32_t quiet): MatchFlag(flag, quiet) {}
};
#endif /* __LIBAA_RE_EXPR */

View File

@@ -0,0 +1,869 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* 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/>.
*
*
* Base of implementation based on the Lexical Analysis chapter of:
* Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman:
* Compilers: Principles, Techniques, and Tools (The "Dragon Book"),
* Addison-Wesley, 1986.
*/
#include <list>
#include <vector>
#include <stack>
#include <set>
#include <map>
#include <ostream>
#include <iostream>
#include <fstream>
#include "expr-tree.h"
#include "hfa.h"
#include "../immunix.h"
ostream &operator<<(ostream &os, const State &state)
{
/* dump the state label */
os << '{';
os << state.label;
os << '}';
return os;
}
State *DFA::add_new_state(NodeMap &nodemap,
pair<unsigned long, NodeSet *> index,
NodeSet *nodes, dfa_stats_t &stats)
{
State *state = new State(nodemap.size(), nodes);
states.push_back(state);
nodemap.insert(make_pair(index, state));
stats.proto_sum += nodes->size();
if (nodes->size() > stats.proto_max)
stats.proto_max = nodes->size();
return state;
}
State *DFA::find_target_state(NodeMap &nodemap, list<State *> &work_queue,
NodeSet *nodes, dfa_stats_t &stats)
{
State *target;
pair<unsigned long, NodeSet *> index = make_pair(hash_NodeSet(nodes), nodes);
map<pair<unsigned long, NodeSet *>, State *, deref_less_than>::iterator x = nodemap.find(index);
if (x == nodemap.end()) {
/* set of nodes isn't known so create new state, and nodes to
* state mapping
*/
target = add_new_state(nodemap, index, nodes, stats);
work_queue.push_back(target);
} else {
/* set of nodes already has a mapping so free this one */
stats.duplicates++;
delete(nodes);
target = x->second;
}
return target;
}
void DFA::update_state_transitions(NodeMap &nodemap, list<State *> &work_queue,
State *state, dfa_stats_t &stats)
{
/* Compute possible transitions for state->nodes. This is done by
* iterating over all the nodes in state->nodes and combining the
* transitions.
*
* The resultant transition set is a mapping of characters to
* sets of nodes.
*/
NodeCases cases;
for (NodeSet::iterator i = state->nodes->begin(); i != state->nodes->end(); i++)
(*i)->follow(cases);
/* Now for each set of nodes in the computed transitions, make
* sure that there is a state that maps to it, and add the
* matching case to the state.
*/
/* check the default transition first */
if (cases.otherwise)
state->cases.otherwise = find_target_state(nodemap, work_queue,
cases.otherwise,
stats);;
/* For each transition from *from, check if the set of nodes it
* transitions to already has been mapped to a state
*/
for (NodeCases::iterator j = cases.begin(); j != cases.end(); j++) {
State *target;
target = find_target_state(nodemap, work_queue, j->second, stats);
/* Don't insert transition that the default transition
* already covers
*/
if (target != state->cases.otherwise)
state->cases.cases[j->first] = target;
}
}
/* WARNING: This routine can only be called from within DFA creation as
* the nodes value is only valid during dfa construction.
*/
void DFA::dump_node_to_dfa(void)
{
cerr << "Mapping of States to expr nodes\n"
" State <= Nodes\n"
"-------------------\n";
for (Partition::iterator i = states.begin(); i != states.end(); i++)
cerr << " " << (*i)->label << " <= " << *(*i)->nodes << "\n";
}
/**
* Construct a DFA from a syntax tree.
*/
DFA::DFA(Node *root, dfaflags_t flags): root(root)
{
dfa_stats_t stats = { 0, 0, 0 };
int i = 0;
if (flags & DFA_DUMP_PROGRESS)
fprintf(stderr, "Creating dfa:\r");
for (depth_first_traversal i(root); i; i++) {
(*i)->compute_nullable();
(*i)->compute_firstpos();
(*i)->compute_lastpos();
}
if (flags & DFA_DUMP_PROGRESS)
fprintf(stderr, "Creating dfa: followpos\r");
for (depth_first_traversal i(root); i; i++) {
(*i)->compute_followpos();
}
NodeMap nodemap;
NodeSet *emptynode = new NodeSet;
nonmatching = add_new_state(nodemap,
make_pair(hash_NodeSet(emptynode), emptynode),
emptynode, stats);
NodeSet *first = new NodeSet(root->firstpos);
start = add_new_state(nodemap, make_pair(hash_NodeSet(first), first),
first, stats);
/* the work_queue contains the states that need to have their
* transitions computed. This could be done with a recursive
* algorithm instead of a work_queue, but it would be slightly slower
* and consume more memory.
*
* TODO: currently the work_queue is treated in a breadth first
* search manner. Test using the work_queue in a depth first
* manner, this may help reduce the number of entries on the
* work_queue at any given time, thus reducing peak memory use.
*/
list<State *> work_queue;
work_queue.push_back(start);
while (!work_queue.empty()) {
if (i % 1000 == 0 && (flags & DFA_DUMP_PROGRESS))
fprintf(stderr, "\033[2KCreating dfa: queue %zd\tstates %zd\teliminated duplicates %d\r",
work_queue.size(), states.size(),
stats.duplicates);
i++;
State *from = work_queue.front();
work_queue.pop_front();
/* Update 'from's transitions, and if it transitions to any
* unknown State create it and add it to the work_queue
*/
update_state_transitions(nodemap, work_queue, from, stats);
} /* while (!work_queue.empty()) */
/* cleanup Sets of nodes used computing the DFA as they are no longer
* needed.
*/
for (depth_first_traversal i(root); i; i++) {
(*i)->firstpos.clear();
(*i)->lastpos.clear();
(*i)->followpos.clear();
}
if (flags & DFA_DUMP_NODE_TO_DFA)
dump_node_to_dfa();
for (NodeMap::iterator i = nodemap.begin(); i != nodemap.end(); i++)
delete i->first.second;
nodemap.clear();
if (flags & (DFA_DUMP_STATS))
fprintf(stderr, "\033[2KCreated dfa: states %zd,\teliminated duplicates %d,\tprotostate sets: longest %u, avg %u\n",
states.size(), stats.duplicates, stats.proto_max,
(unsigned int)(stats.proto_sum / states.size()));
}
DFA::~DFA()
{
for (Partition::iterator i = states.begin(); i != states.end(); i++)
delete *i;
}
void DFA::dump_uniq_perms(const char *s)
{
set<pair<uint32_t, uint32_t> > uniq;
for (Partition::iterator i = states.begin(); i != states.end(); i++)
uniq.insert(make_pair((*i)->accept, (*i)->audit));
cerr << "Unique Permission sets: " << s << " (" << uniq.size() << ")\n";
cerr << "----------------------\n";
for (set<pair<uint32_t, uint32_t> >::iterator i = uniq.begin();
i != uniq.end(); i++) {
cerr << " " << hex << i->first << " " << i->second << dec << "\n";
}
}
/* Remove dead or unreachable states */
void DFA::remove_unreachable(dfaflags_t flags)
{
set<State *> reachable;
list<State *> work_queue;
/* find the set of reachable states */
reachable.insert(nonmatching);
work_queue.push_back(start);
while (!work_queue.empty()) {
State *from = work_queue.front();
work_queue.pop_front();
reachable.insert(from);
if (from->cases.otherwise &&
(reachable.find(from->cases.otherwise) == reachable.end()))
work_queue.push_back(from->cases.otherwise);
for (Cases::iterator j = from->cases.begin(); j != from->cases.end(); j++) {
if (reachable.find(j->second) == reachable.end())
work_queue.push_back(j->second);
}
}
/* walk the set of states and remove any that aren't reachable */
if (reachable.size() < states.size()) {
int count = 0;
Partition::iterator i;
Partition::iterator next;
for (i = states.begin(); i != states.end(); i = next) {
next = i;
next++;
if (reachable.find(*i) == reachable.end()) {
if (flags & DFA_DUMP_UNREACHABLE) {
cerr << "unreachable: " << **i;
if (*i == start)
cerr << " <==";
if ((*i)->accept) {
cerr << " (0x" << hex
<< (*i)->accept << " "
<< (*i)->audit << dec
<< ')';
}
cerr << "\n";
}
State *current = *i;
states.erase(i);
delete(current);
count++;
}
}
if (count && (flags & DFA_DUMP_STATS))
cerr << "DFA: states " << states.size() << " removed "
<< count << " unreachable states\n";
}
}
/* test if two states have the same transitions under partition_map */
bool DFA::same_mappings(State *s1, State *s2)
{
if (s1->cases.otherwise && s1->cases.otherwise != nonmatching) {
if (!s2->cases.otherwise || s2->cases.otherwise == nonmatching)
return false;
Partition *p1 = s1->cases.otherwise->partition;
Partition *p2 = s2->cases.otherwise->partition;
if (p1 != p2)
return false;
} else if (s2->cases.otherwise && s2->cases.otherwise != nonmatching) {
return false;
}
if (s1->cases.cases.size() != s2->cases.cases.size())
return false;
for (Cases::iterator j1 = s1->cases.begin(); j1 != s1->cases.end(); j1++) {
Cases::iterator j2 = s2->cases.cases.find(j1->first);
if (j2 == s2->cases.end())
return false;
Partition *p1 = j1->second->partition;
Partition *p2 = j2->second->partition;
if (p1 != p2)
return false;
}
return true;
}
/* Do simple djb2 hashing against a States transition cases
* this provides a rough initial guess at state equivalence as if a state
* has a different number of transitions or has transitions on different
* cases they will never be equivalent.
* Note: this only hashes based off of the alphabet (not destination)
* as different destinations could end up being equiv
*/
size_t DFA::hash_trans(State *s)
{
unsigned long hash = 5381;
for (Cases::iterator j = s->cases.begin(); j != s->cases.end(); j++) {
hash = ((hash << 5) + hash) + j->first;
State *k = j->second;
hash = ((hash << 5) + hash) + k->cases.cases.size();
}
if (s->cases.otherwise && s->cases.otherwise != nonmatching) {
hash = ((hash << 5) + hash) + 5381;
State *k = s->cases.otherwise;
hash = ((hash << 5) + hash) + k->cases.cases.size();
}
hash = (hash << 8) | s->cases.cases.size();
return hash;
}
/* minimize the number of dfa states */
void DFA::minimize(dfaflags_t flags)
{
map<pair<uint64_t, size_t>, Partition *> perm_map;
list<Partition *> partitions;
/* Set up the initial partitions
* minimium of - 1 non accepting, and 1 accepting
* if trans hashing is used the accepting and non-accepting partitions
* can be further split based on the number and type of transitions
* a state makes.
* If permission hashing is enabled the accepting partitions can
* be further divided by permissions. This can result in not
* obtaining a truely minimized dfa but comes close, and can speedup
* minimization.
*/
int accept_count = 0;
int final_accept = 0;
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
uint64_t perm_hash = 0;
if (flags & DFA_CONTROL_MINIMIZE_HASH_PERMS) {
/* make every unique perm create a new partition */
perm_hash = ((uint64_t) (*i)->audit) << 32 |
(uint64_t) (*i)->accept;
} else if ((*i)->audit || (*i)->accept) {
/* combine all perms together into a single parition */
perm_hash = 1;
} /* else not an accept state so 0 for perm_hash */
size_t trans_hash = 0;
if (flags & DFA_CONTROL_MINIMIZE_HASH_TRANS)
trans_hash = hash_trans(*i);
pair<uint64_t, size_t> group = make_pair(perm_hash, trans_hash);
map<pair<uint64_t, size_t>, Partition *>::iterator p = perm_map.find(group);
if (p == perm_map.end()) {
Partition *part = new Partition();
part->push_back(*i);
perm_map.insert(make_pair(group, part));
partitions.push_back(part);
(*i)->partition = part;
if (perm_hash)
accept_count++;
} else {
(*i)->partition = p->second;
p->second->push_back(*i);
}
if ((flags & DFA_DUMP_PROGRESS) && (partitions.size() % 1000 == 0))
cerr << "\033[2KMinimize dfa: partitions "
<< partitions.size() << "\tinit " << partitions.size()
<< " (accept " << accept_count << ")\r";
}
/* perm_map is no longer needed so free the memory it is using.
* Don't remove - doing it manually here helps reduce peak memory usage.
*/
perm_map.clear();
int init_count = partitions.size();
if (flags & DFA_DUMP_PROGRESS)
cerr << "\033[2KMinimize dfa: partitions " << partitions.size()
<< "\tinit " << init_count << " (accept "
<< accept_count << ")\r";
/* Now do repartitioning until each partition contains the set of
* states that are the same. This will happen when the partition
* splitting stables. With a worse case of 1 state per partition
* ie. already minimized.
*/
Partition *new_part;
int new_part_count;
do {
new_part_count = 0;
for (list<Partition *>::iterator p = partitions.begin();
p != partitions.end(); p++) {
new_part = NULL;
State *rep = *((*p)->begin());
Partition::iterator next;
for (Partition::iterator s = ++(*p)->begin(); s != (*p)->end();) {
if (same_mappings(rep, *s)) {
++s;
continue;
}
if (!new_part) {
new_part = new Partition;
list<Partition *>::iterator tmp = p;
partitions.insert(++tmp, new_part);
new_part_count++;
}
new_part->push_back(*s);
s = (*p)->erase(s);
}
/* remapping partition_map for new_part entries
* Do not do this above as it messes up same_mappings
*/
if (new_part) {
for (Partition::iterator m = new_part->begin();
m != new_part->end(); m++) {
(*m)->partition = new_part;
}
}
if ((flags & DFA_DUMP_PROGRESS) && (partitions.size() % 100 == 0))
cerr << "\033[2KMinimize dfa: partitions "
<< partitions.size() << "\tinit "
<< init_count << " (accept "
<< accept_count << ")\r";
}
} while (new_part_count);
if (partitions.size() == states.size()) {
if (flags & DFA_DUMP_STATS)
cerr << "\033[2KDfa minimization no states removed: partitions "
<< partitions.size() << "\tinit " << init_count
<< " (accept " << accept_count << ")\n";
goto out;
}
/* Remap the dfa so it uses the representative states
* Use the first state of a partition as the representative state
* At this point all states with in a partion have transitions
* to states within the same partitions, however this can slow
* down compressed dfa compression as there are more states,
*/
if (flags & DFA_DUMP_MIN_PARTS)
cerr << "Partitions after minimization\n";
for (list<Partition *>::iterator p = partitions.begin();
p != partitions.end(); p++) {
/* representative state for this partition */
State *rep = *((*p)->begin());
if (flags & DFA_DUMP_MIN_PARTS)
cerr << *rep << " : ";
/* update representative state's transitions */
if (rep->cases.otherwise) {
Partition *partition = rep->cases.otherwise->partition;
rep->cases.otherwise = *partition->begin();
}
for (Cases::iterator c = rep->cases.begin(); c != rep->cases.end(); c++) {
Partition *partition = c->second->partition;
c->second = *partition->begin();
}
//if ((*p)->size() > 1)
//cerr << rep->label << ": ";
/* clear the state label for all non representative states,
* and accumulate permissions */
for (Partition::iterator i = ++(*p)->begin(); i != (*p)->end(); i++) {
//cerr << " " << (*i)->label;
if (flags & DFA_DUMP_MIN_PARTS)
cerr << **i << ", ";
(*i)->label = -1;
rep->accept |= (*i)->accept;
rep->audit |= (*i)->audit;
}
if (rep->accept || rep->audit)
final_accept++;
//if ((*p)->size() > 1)
//cerr << "\n";
if (flags & DFA_DUMP_MIN_PARTS)
cerr << "\n";
}
if (flags & DFA_DUMP_STATS)
cerr << "\033[2KMinimized dfa: final partitions "
<< partitions.size() << " (accept " << final_accept
<< ")" << "\tinit " << init_count << " (accept "
<< accept_count << ")\n";
/* make sure nonmatching and start state are up to date with the
* mappings */
{
Partition *partition = nonmatching->partition;
if (*partition->begin() != nonmatching) {
nonmatching = *partition->begin();
}
partition = start->partition;
if (*partition->begin() != start) {
start = *partition->begin();
}
}
/* Now that the states have been remapped, remove all states
* that are not the representive states for their partition, they
* will have a label == -1
*/
for (Partition::iterator i = states.begin(); i != states.end();) {
if ((*i)->label == -1) {
State *s = *i;
i = states.erase(i);
delete(s);
} else
i++;
}
out:
/* Cleanup */
while (!partitions.empty()) {
Partition *p = partitions.front();
partitions.pop_front();
delete(p);
}
}
/**
* text-dump the DFA (for debugging).
*/
void DFA::dump(ostream & os)
{
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
if (*i == start || (*i)->accept) {
os << **i;
if (*i == start)
os << " <==";
if ((*i)->accept) {
os << " (0x" << hex << (*i)->accept << " "
<< (*i)->audit << dec << ')';
}
os << "\n";
}
}
os << "\n";
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
if ((*i)->cases.otherwise)
os << **i << " -> " << (*i)->cases.otherwise << "\n";
for (Cases::iterator j = (*i)->cases.begin();
j != (*i)->cases.end(); j++) {
os << **i << " -> " << j->second << ": "
<< j->first << "\n";
}
}
os << "\n";
}
/**
* Create a dot (graphviz) graph from the DFA (for debugging).
*/
void DFA::dump_dot_graph(ostream & os)
{
os << "digraph \"dfa\" {" << "\n";
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
if (*i == nonmatching)
continue;
os << "\t\"" << **i << "\" [" << "\n";
if (*i == start) {
os << "\t\tstyle=bold" << "\n";
}
uint32_t perms = (*i)->accept;
if (perms) {
os << "\t\tlabel=\"" << **i << "\\n("
<< perms << ")\"" << "\n";
}
os << "\t]" << "\n";
}
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
Cases &cases = (*i)->cases;
Chars excluded;
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
if (j->second == nonmatching)
excluded.insert(j->first);
else {
os << "\t\"" << **i << "\" -> \"" << *j->second
<< "\" [" << "\n";
os << "\t\tlabel=\"" << j->first << "\"\n";
os << "\t]" << "\n";
}
}
if (cases.otherwise && cases.otherwise != nonmatching) {
os << "\t\"" << **i << "\" -> \"" << *cases.otherwise
<< "\" [" << "\n";
if (!excluded.empty()) {
os << "\t\tlabel=\"[^";
for (Chars::iterator i = excluded.begin();
i != excluded.end(); i++) {
os << *i;
}
os << "]\"" << "\n";
}
os << "\t]" << "\n";
}
}
os << '}' << "\n";
}
/**
* Compute character equivalence classes in the DFA to save space in the
* transition table.
*/
map<uchar, uchar> DFA::equivalence_classes(dfaflags_t flags)
{
map<uchar, uchar> classes;
uchar next_class = 1;
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
Cases & cases = (*i)->cases;
/* Group edges to the same next state together */
map<const State *, Chars> node_sets;
for (Cases::iterator j = cases.begin(); j != cases.end(); j++)
node_sets[j->second].insert(j->first);
for (map<const State *, Chars>::iterator j = node_sets.begin();
j != node_sets.end(); j++) {
/* Group edges to the same next state together by class */
map<uchar, Chars> node_classes;
bool class_used = false;
for (Chars::iterator k = j->second.begin();
k != j->second.end(); k++) {
pair<map<uchar, uchar>::iterator, bool> x = classes.insert(make_pair(*k, next_class));
if (x.second)
class_used = true;
pair<map<uchar, Chars>::iterator, bool> y = node_classes.insert(make_pair(x.first->second, Chars()));
y.first->second.insert(*k);
}
if (class_used) {
next_class++;
class_used = false;
}
for (map<uchar, Chars>::iterator k = node_classes.begin();
k != node_classes.end(); k++) {
/**
* If any other characters are in the same class, move
* the characters in this class into their own new
* class
*/
map<uchar, uchar>::iterator l;
for (l = classes.begin(); l != classes.end(); l++) {
if (l->second == k->first &&
k->second.find(l->first) == k->second.end()) {
class_used = true;
break;
}
}
if (class_used) {
for (Chars::iterator l = k->second.begin();
l != k->second.end(); l++) {
classes[*l] = next_class;
}
next_class++;
class_used = false;
}
}
}
}
if (flags & DFA_DUMP_EQUIV_STATS)
fprintf(stderr, "Equiv class reduces to %d classes\n",
next_class - 1);
return classes;
}
/**
* Text-dump the equivalence classes (for debugging).
*/
void dump_equivalence_classes(ostream &os, map<uchar, uchar> &eq)
{
map<uchar, Chars> rev;
for (map<uchar, uchar>::iterator i = eq.begin(); i != eq.end(); i++) {
Chars &chars = rev.insert(make_pair(i->second, Chars())).first->second;
chars.insert(i->first);
}
os << "(eq):" << "\n";
for (map<uchar, Chars>::iterator i = rev.begin(); i != rev.end(); i++) {
os << (int)i->first << ':';
Chars &chars = i->second;
for (Chars::iterator j = chars.begin(); j != chars.end(); j++) {
os << ' ' << *j;
}
os << "\n";
}
}
/**
* Replace characters with classes (which are also represented as
* characters) in the DFA transition table.
*/
void DFA::apply_equivalence_classes(map<uchar, uchar> &eq)
{
/**
* Note: We only transform the transition table; the nodes continue to
* contain the original characters.
*/
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
map<uchar, State *> tmp;
tmp.swap((*i)->cases.cases);
for (Cases::iterator j = tmp.begin(); j != tmp.end(); j++)
(*i)->cases.cases.
insert(make_pair(eq[j->first], j->second));
}
}
#if 0
typedef set <ImportantNode *>AcceptNodes;
map<ImportantNode *, AcceptNodes> dominance(DFA & dfa)
{
map<ImportantNode *, AcceptNodes> is_dominated;
for (States::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
AcceptNodes set1;
for (State::iterator j = (*i)->begin(); j != (*i)->end(); j++) {
if (AcceptNode * accept = dynamic_cast<AcceptNode *>(*j))
set1.insert(accept);
}
for (AcceptNodes::iterator j = set1.begin(); j != set1.end(); j++) {
pair<map<ImportantNode *, AcceptNodes>::iterator, bool> x = is_dominated.insert(make_pair(*j, set1));
if (!x.second) {
AcceptNodes & set2(x.first->second), set3;
for (AcceptNodes::iterator l = set2.begin();
l != set2.end(); l++) {
if (set1.find(*l) != set1.end())
set3.insert(*l);
}
set3.swap(set2);
}
}
}
return is_dominated;
}
#endif
static inline int diff_qualifiers(uint32_t perm1, uint32_t perm2)
{
return ((perm1 & AA_EXEC_TYPE) && (perm2 & AA_EXEC_TYPE) &&
(perm1 & AA_EXEC_TYPE) != (perm2 & AA_EXEC_TYPE));
}
/**
* Compute the permission flags that this state corresponds to. If we
* have any exact matches, then they override the execute and safe
* execute flags.
*/
uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error)
{
uint32_t perms = 0, exact_match_perms = 0;
uint32_t audit = 0, exact_audit = 0, quiet = 0, deny = 0;
if (error)
*error = 0;
for (NodeSet::iterator i = state->begin(); i != state->end(); i++) {
MatchFlag *match;
if (!(match = dynamic_cast<MatchFlag *>(*i)))
continue;
if (dynamic_cast<ExactMatchFlag *>(match)) {
/* exact match only ever happens with x */
if (!is_merged_x_consistent(exact_match_perms,
match->flag) && error)
*error = 1;;
exact_match_perms |= match->flag;
exact_audit |= match->audit;
} else if (dynamic_cast<DenyMatchFlag *>(match)) {
deny |= match->flag;
quiet |= match->audit;
} else {
if (!is_merged_x_consistent(perms, match->flag)
&& error)
*error = 1;
perms |= match->flag;
audit |= match->audit;
}
}
//if (audit || quiet)
//fprintf(stderr, "perms: 0x%x, audit: 0x%x exact: 0x%x eaud: 0x%x deny: 0x%x quiet: 0x%x\n", perms, audit, exact_match_perms, exact_audit, deny, quiet);
perms |= exact_match_perms & ~(AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE);
if (exact_match_perms & AA_USER_EXEC_TYPE) {
perms = (exact_match_perms & AA_USER_EXEC_TYPE) |
(perms & ~AA_USER_EXEC_TYPE);
audit = (exact_audit & AA_USER_EXEC_TYPE) |
(audit & ~AA_USER_EXEC_TYPE);
}
if (exact_match_perms & AA_OTHER_EXEC_TYPE) {
perms = (exact_match_perms & AA_OTHER_EXEC_TYPE) |
(perms & ~AA_OTHER_EXEC_TYPE);
audit = (exact_audit & AA_OTHER_EXEC_TYPE) |
(audit & ~AA_OTHER_EXEC_TYPE);
}
if (perms & AA_USER_EXEC & deny)
perms &= ~AA_USER_EXEC_TYPE;
if (perms & AA_OTHER_EXEC & deny)
perms &= ~AA_OTHER_EXEC_TYPE;
perms &= ~deny;
if (audit_ctl)
*audit_ctl = PACK_AUDIT_CTL(audit, quiet & deny);
// if (perms & AA_ERROR_BIT) {
// fprintf(stderr, "error bit 0x%x\n", perms);
// exit(255);
//}
//if (perms & AA_EXEC_BITS)
//fprintf(stderr, "accept perm: 0x%x\n", perms);
/*
if (perms & ~AA_VALID_PERMS)
yyerror(_("Internal error accumulated invalid perm 0x%llx\n"), perms);
*/
//if (perms & AA_CHANGE_HAT)
// fprintf(stderr, "change_hat 0x%x\n", perms);
if (*error)
fprintf(stderr, "profile has merged rule with conflicting x modifiers\n");
return perms;
}

143
parser/libapparmor_re/hfa.h Normal file
View File

@@ -0,0 +1,143 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* 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/>.
*
*
* Base of implementation based on the Lexical Analysis chapter of:
* Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman:
* Compilers: Principles, Techniques, and Tools (The "Dragon Book"),
* Addison-Wesley, 1986.
*/
#ifndef __LIBAA_RE_HFA_H
#define __LIBAA_RE_HFA_H
#include <list>
#include <map>
#include <vector>
#include <stdint.h>
#include "expr-tree.h"
class State;
/**
* State cases are identical to NodesCases except they map to State *
* instead of NodeSet.
* Out-edges from a state to another: we store the follow State
* for each input character that is not a default match in cases and
* default matches in otherwise as well as in all matching explicit cases
* This avoids enumerating all the explicit tranitions for default matches.
*/
typedef struct Cases {
typedef map<uchar, State *>::iterator iterator;
iterator begin() { return cases.begin(); }
iterator end() { return cases.end(); }
Cases(): otherwise(0) { }
map<uchar, State *> cases;
State *otherwise;
} Cases;
typedef list<State *> Partition;
uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error);
/*
* State - DFA individual state information
* label: a unique label to identify the state used for pretty printing
* the non-matching state is setup to have label == 0 and
* the start state is setup to have label == 1
* audit: the audit permission mask for the state
* accept: the accept permissions for the state
* cases: set of transitions from this state
* parition: Is a temporary work variable used during dfa minimization.
* it can be replaced with a map, but that is slower and uses more
* memory.
* nodes: Is a temporary work variable used during dfa creation. It can
* be replaced by using the nodemap, but that is slower
*/
class State {
public:
State(): label(0), audit(0), accept(0), cases(), nodes(NULL) { };
State(int l): label(l), audit(0), accept(0), cases(), nodes(NULL) { };
State(int l, NodeSet * n) throw(int):
label(l), audit(0), accept(0), cases(), nodes(n)
{
int error;
/* Compute permissions associated with the State. */
accept = accept_perms(nodes, &audit, &error);
if (error) {
//cerr << "Failing on accept perms " << error << "\n";
throw error;
}
};
int label;
uint32_t audit, accept;
Cases cases;
union {
Partition *partition;
NodeSet *nodes;
};
};
ostream &operator<<(ostream &os, const State &state);
typedef map<pair<unsigned long, NodeSet *>, State *, deref_less_than> NodeMap;
/* Transitions in the DFA. */
/* dfa_stats - structure to group various stats about dfa creation
* duplicates - how many duplicate NodeSets where encountered and discarded
* proto_max - maximum length of a NodeSet encountered during dfa construction
* proto_sum - sum of NodeSet length during dfa construction. Used to find
* average length.
*/
typedef struct dfa_stats {
unsigned int duplicates, proto_max, proto_sum;
} dfa_stats_t;
class DFA {
void dump_node_to_dfa(void);
State *add_new_state(NodeMap &nodemap,
pair<unsigned long, NodeSet *> index,
NodeSet *nodes, dfa_stats_t &stats);
void update_state_transitions(NodeMap &nodemap,
list<State *> &work_queue,
State *state, dfa_stats_t &stats);
State *find_target_state(NodeMap &nodemap, list<State *> &work_queue,
NodeSet *nodes, dfa_stats_t &stats);
public:
DFA(Node *root, dfaflags_t flags);
virtual ~DFA();
void remove_unreachable(dfaflags_t flags);
bool same_mappings(State *s1, State *s2);
size_t hash_trans(State *s);
void minimize(dfaflags_t flags);
void dump(ostream &os);
void dump_dot_graph(ostream &os);
void dump_uniq_perms(const char *s);
map<uchar, uchar> equivalence_classes(dfaflags_t flags);
void apply_equivalence_classes(map<uchar, uchar> &eq);
Node *root;
State *nonmatching, *start;
Partition states;
};
void dump_equivalence_classes(ostream &os, map<uchar, uchar> &eq);
#endif /* __LIBAA_RE_HFA_H */

View File

@@ -0,0 +1,27 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* 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/>.
*
*
* Parsing of regular expression into expression trees as implemented in
* expr-tree
*/
#ifndef __LIBAA_RE_PARSE_H
#define __LIBAA_RE_PARSE_H
int regex_parse(Node **tree, const char *rule);
#endif /* __LIBAA_RE_PARSE_H */

View File

@@ -0,0 +1,262 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* 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/>.
*
*
* Parsing of regular expression into expression trees as implemented in
* expr-tree
*/
%{
/* #define DEBUG_TREE */
#include "expr-tree.h"
%}
%union {
char c;
Node *node;
Chars *cset;
}
%{
void regex_error(Node **, const char *, const char *);
#define YYLEX_PARAM &text
int regex_lex(YYSTYPE *, const char **);
static inline Chars *insert_char(Chars* cset, uchar a)
{
cset->insert(a);
return cset;
}
static inline Chars* insert_char_range(Chars* cset, uchar a, uchar b)
{
if (a > b)
swap(a, b);
for (uchar i = a; i <= b; i++)
cset->insert(i);
return cset;
}
%}
%pure-parser
/* %error-verbose */
%parse-param {Node **root}
%parse-param {const char *text}
%name-prefix = "regex_"
%token <c> CHAR
%type <c> regex_char cset_char1 cset_char cset_charN
%type <cset> charset cset_chars
%type <node> regex expr terms0 terms qterm term
/**
* Note: destroy all nodes upon failure, but *not* the start symbol once
* parsing succeeds!
*/
%destructor { $$->release(); } expr terms0 terms qterm term
%%
/* FIXME: Does not parse "[--]", "[---]", "[^^-x]". I don't actually know
which precise grammer Perl regexs use, and rediscovering that
is proving to be painful. */
regex : /* empty */ { *root = $$ = &epsnode; }
| expr { *root = $$ = $1; }
;
expr : terms
| expr '|' terms0 { $$ = new AltNode($1, $3); }
| '|' terms0 { $$ = new AltNode(&epsnode, $2); }
;
terms0 : /* empty */ { $$ = &epsnode; }
| terms
;
terms : qterm
| terms qterm { $$ = new CatNode($1, $2); }
;
qterm : term
| term '*' { $$ = new StarNode($1); }
| term '+' { $$ = new PlusNode($1); }
;
term : '.' { $$ = new AnyCharNode; }
| regex_char { $$ = new CharNode($1); }
| '[' charset ']' { $$ = new CharSetNode(*$2);
delete $2; }
| '[' '^' charset ']'
{ $$ = new NotCharSetNode(*$3);
delete $3; }
| '[' '^' '^' cset_chars ']'
{ $4->insert('^');
$$ = new NotCharSetNode(*$4);
delete $4; }
| '(' regex ')' { $$ = $2; }
;
regex_char : CHAR
| '^' { $$ = '^'; }
| '-' { $$ = '-'; }
| ']' { $$ = ']'; }
;
charset : cset_char1 cset_chars
{ $$ = insert_char($2, $1); }
| cset_char1 '-' cset_charN cset_chars
{ $$ = insert_char_range($4, $1, $3); }
;
cset_chars : /* nothing */ { $$ = new Chars; }
| cset_chars cset_charN
{ $$ = insert_char($1, $2); }
| cset_chars cset_charN '-' cset_charN
{ $$ = insert_char_range($1, $2, $4); }
;
cset_char1 : cset_char
| ']' { $$ = ']'; }
| '-' { $$ = '-'; }
;
cset_charN : cset_char
| '^' { $$ = '^'; }
;
cset_char : CHAR
| '[' { $$ = '['; }
| '*' { $$ = '*'; }
| '+' { $$ = '+'; }
| '.' { $$ = '.'; }
| '|' { $$ = '|'; }
| '(' { $$ = '('; }
| ')' { $$ = ')'; }
;
%%
int octdigit(char c)
{
if (c >= '0' && c <= '7')
return c - '0';
return -1;
}
int hexdigit(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return 10 + c - 'A';
else if (c >= 'a' && c <= 'f')
return 10 + c - 'A';
else
return -1;
}
int regex_lex(YYSTYPE *val, const char **pos)
{
int c;
val->c = **pos;
switch(*(*pos)++) {
case '\0':
(*pos)--;
return 0;
case '*': case '+': case '.': case '|': case '^': case '-':
case '[': case ']': case '(' : case ')':
return *(*pos - 1);
case '\\':
val->c = **pos;
switch(*(*pos)++) {
case '\0':
(*pos)--;
/* fall through */
case '\\':
val->c = '\\';
break;
case '0':
val->c = 0;
if ((c = octdigit(**pos)) >= 0) {
val->c = c;
(*pos)++;
}
if ((c = octdigit(**pos)) >= 0) {
val->c = (val->c << 3) + c;
(*pos)++;
}
if ((c = octdigit(**pos)) >= 0) {
val->c = (val->c << 3) + c;
(*pos)++;
}
break;
case 'x':
val->c = 0;
if ((c = hexdigit(**pos)) >= 0) {
val->c = c;
(*pos)++;
}
if ((c = hexdigit(**pos)) >= 0) {
val->c = (val->c << 4) + c;
(*pos)++;
}
break;
case 'a':
val->c = '\a';
break;
case 'e':
val->c = 033 /* ESC */;
break;
case 'f':
val->c = '\f';
break;
case 'n':
val->c = '\n';
break;
case 'r':
val->c = '\r';
break;
case 't':
val->c = '\t';
break;
}
}
return CHAR;
}
void regex_error(Node ** __attribute__((unused)),
const char *text __attribute__((unused)),
const char *error __attribute__((unused)))
{
/* We don't want the library to print error messages. */
}

View File

@@ -1,10 +0,0 @@
#ifndef __REGEXP_H
#define __REGEXP_H
/**
* Flex file format, but without state compression and with negative
* match results in the YYTD_ID_DEF table instead.
*/
#define YYTH_REGEXP_MAGIC 0x1B5E783D
#endif /* __REGEXP_H */

File diff suppressed because it is too large Load Diff

58
parser/parser.conf Normal file
View File

@@ -0,0 +1,58 @@
# parser.conf is a global AppArmor config file for the apparmor_parser
#
# It can be used to specify the default options for the parser, which
# can then be overriden by options passed on the command line.
#
# Leading whitespace is ignored and lines that begin with # are treated
# as comments.
#
# Config options are specified one per line using the same format as the
# longform command line options (without the preceding --).
#
# If a value is specified twice the last version to appear is used.
## Suppress Warnings
#quiet
## Be verbose
#verbose
## Set include path
#Include /etc/apparmor.d/abstractions
## Set location of apparmor filesystem
#subdomainfs /sys/kernel/security/apparmor
## Set match-string to use - for forcing compiler to treat different kernels
## the same
# match-string "pattern=aadfa audit perms=crwxamlk/ user::other"
## Turn creating/updating of the cache on by default
#write-cache
## Show cache hits
#show-cache
## skip cached policy
#skip-cache
## skip reading cache but allow updating
#skip-read-cache
#### Set Optimizaions. Multiple Optimizations can be set, one per line ####
# For supported optimizations see
# apparmor_parser --help=O
## Turn on equivalence classes
#equiv
## Turn off expr tree simplification
#Optimize=no-expr-simplify
## Turn off DFA minimization
#Optimize=no-minimize
## Adjust compression
#Optimize=compress-small
#Optimize=compress-fast

View File

@@ -23,6 +23,7 @@
#include <sys/resource.h>
#include "immunix.h"
#include "libapparmor_re/apparmor_re.h"
#include "libapparmor_re/aare_rules.h"
typedef enum pattern_t pattern_t;
@@ -178,13 +179,8 @@ struct var_string {
#define FLAG_CHANGEHAT_1_4 2
#define FLAG_CHANGEHAT_1_5 3
extern int kernel_supports_network;
extern int net_af_max_override;
extern int flag_changehat_version;
extern int read_implies_exec;
extern dfaflags_t dfaflags;
extern int preprocess_only;
extern FILE *ofile;
#define PATH_CHROOT_REL 0x1
#define PATH_NS_REL 0x2
@@ -227,23 +223,34 @@ extern FILE *ofile;
#define list_last_entry(LIST, ENTRY) \
for ((ENTRY) = (LIST); (ENTRY) && (ENTRY)->next; (ENTRY) = (ENTRY)->next)
/* Some external definitions to make b0rken programs happy */
/* from parser_common.c */
extern int regex_type;
extern int perms_create;
extern int net_af_max_override;
extern int kernel_load;
extern int kernel_supports_network;
extern int flag_changehat_version;
extern int conf_verbose;
extern int conf_quiet;
extern int names_only;
extern int option;
extern int current_lineno;
extern dfaflags_t dfaflags;
extern char *progname;
extern char *subdomainbase;
extern char *profilename;
extern char *profile_namespace;
/* from parser_main */
extern int force_complain;
extern int conf_quiet;
extern int conf_verbose;
extern int kernel_load;
extern int regex_type;
extern int perms_create;
extern struct timespec mru_tstamp;
extern void update_mru_tstamp(FILE *file);
extern char *current_filename;
extern FILE *ofile;
extern int read_implies_exec;
extern void pwarn(char *fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
/* 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);
/* provided by parser_lex.l (cannot be used in tst builds) */
extern FILE *yyin;
extern void yyrestart(FILE *fp);
extern int yyparse(void);
@@ -339,3 +346,32 @@ extern void dump_policy_hats(struct codomain *cod);
extern void dump_policy_names(void);
extern int die_if_any_regex(void);
void free_policies(void);
#ifdef UNIT_TEST
/* For the unit-test builds, we must include function stubs for stuff that
* only exists in the excluded object files; everything else should live
* in parser_common.c.
*/
/* parser_yacc.y */
void yyerror(char *msg, ...)
{
va_list arg;
char buf[PATH_MAX];
va_start(arg, msg);
vsnprintf(buf, sizeof(buf), msg, arg);
va_end(arg);
PERROR(_("AppArmor parser error: %s\n"), buf);
exit(1);
}
#define MY_TEST(statement, error) \
if (!(statement)) { \
PERROR("FAIL: %s\n", error); \
rc = 1; \
}
#endif

76
parser/parser_common.c Normal file
View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2010, 2011
* 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 <stdlib.h>
#include <stdarg.h>
#include <libintl.h>
#include <locale.h>
#define _(s) gettext(s)
#include "parser.h"
int regex_type = AARE_DFA;
int perms_create = 0; /* perms contain create flag */
int net_af_max_override = -1; /* use kernel to determine af_max */
int kernel_load = 1;
int kernel_supports_network = 1; /* kernel supports network rules */
int flag_changehat_version = FLAG_CHANGEHAT_1_5;
int conf_verbose = 0;
int conf_quiet = 0;
int names_only = 0;
int current_lineno = 1;
int option = OPTION_ADD;
dfaflags_t dfaflags = DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_MINIMIZE_HASH_TRANS | DFA_CONTROL_MINIMIZE_HASH_PERMS;
char *subdomainbase = NULL;
char *progname = __FILE__;
char *profile_namespace = NULL;
char *profilename = NULL;
char *current_filename = NULL;
FILE *ofile = NULL;
#ifdef FORCE_READ_IMPLIES_EXEC
int read_implies_exec = 1;
#else
int read_implies_exec = 0;
#endif
void pwarn(char *fmt, ...)
{
va_list arg;
char *newfmt;
int rc;
if (conf_quiet || names_only || option == OPTION_REMOVE)
return;
rc = asprintf(&newfmt, _("Warning from %s (%s%sline %d): %s"),
profilename ? profilename : "stdin",
current_filename ? current_filename : "",
current_filename ? " " : "",
current_lineno,
fmt);
if (!newfmt)
return;
va_start(arg, fmt);
vfprintf(stderr, newfmt, arg);
va_end(arg);
free(newfmt);
}

View File

@@ -21,8 +21,6 @@
#define PARSER_INCLUDE_H
extern int preprocess_only;
extern int current_lineno;
extern char *current_filename;
extern int add_search_dir(char *dir);
extern void init_base_dir(void);

View File

@@ -54,9 +54,6 @@
#define YY_NO_INPUT
int current_lineno = 1;
char *current_filename = NULL;
struct ignored_suffix_t {
char * text;
int len;
@@ -511,7 +508,7 @@ LT_EQUAL <=
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
-?{NUMBER}[kKMG]? {
-?{NUMBER}[[:alpha:]]* {
DUMP_PREPROCESS;
yylval.var_val = strdup(yytext);
return TOK_VALUE;

View File

@@ -2,7 +2,7 @@
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
*
* Copyright (c) 2010
* Copyright (c) 2010, 2011
* Canonical, Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
@@ -19,6 +19,7 @@
* Ltd.
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -28,7 +29,6 @@
#include <fcntl.h>
#include <mntent.h>
#include <libintl.h>
#include <linux/limits.h>
#include <locale.h>
#define _(s) gettext(s)
@@ -36,6 +36,7 @@
/* #define DEBUG */
#include <unistd.h>
#include <limits.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -58,46 +59,26 @@
#define UNPRIVILEGED_OPS (!(PRIVILEGED_OPS))
const char *parser_title = "AppArmor parser";
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2010 Canonical Ltd.";
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2011 Canonical Ltd.";
char *progname;
int option = OPTION_ADD;
int opt_force_complain = 0;
int binary_input = 0;
int names_only = 0;
int dump_vars = 0;
int dump_expanded_vars = 0;
dfaflags_t dfaflags = DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_MINIMIZE_HASH_TRANS | DFA_CONTROL_MINIMIZE_HASH_PERMS;
int conf_verbose = 0;
int conf_quiet = 0;
int kernel_load = 1;
int show_cache = 0;
int skip_cache = 0;
int skip_read_cache = 0;
int write_cache = 0;
#ifdef FORCE_READ_IMPLIES_EXEC
int read_implies_exec = 1;
#else
int read_implies_exec = 0;
#endif
int preprocess_only = 0;
int skip_mode_force = 0;
struct timespec mru_tstamp;
char *subdomainbase = NULL;
char *match_string = NULL;
char *flags_string = NULL;
int regex_type = AARE_DFA;
int perms_create = 0; /* perms contain create flag */
int kernel_supports_network = 1; /* kernel supports network rules */
int net_af_max_override = -1; /* use kernel to determine af_max */
char *profile_namespace = NULL;
int flag_changehat_version = FLAG_CHANGEHAT_1_5;
FILE *ofile = NULL;
/* per-profile settings */
int force_complain = 0;
char *profilename = NULL;
struct option long_options[] = {
{"add", 0, 0, 'a'},
@@ -216,6 +197,8 @@ optflag_table_t dumpflag_table[] = {
DFA_DUMP_UNIQ_PERMS },
{ 1, "dfa-minimize-uniq-perms", "Dump unique perms post minimization",
DFA_DUMP_MIN_UNIQ_PERMS },
{ 1, "dfa-minimize-partitions", "Dump dfa minimization partitions",
DFA_DUMP_MIN_PARTS },
{ 1, "compress-progress", "Dump progress of compression",
DFA_DUMP_TRANS_PROGRESS | DFA_DUMP_TRANS_STATS },
{ 1, "compress-stats", "Dump stats on compression",
@@ -322,29 +305,240 @@ static void display_optimize(char *command)
print_flag_table(optflag_table);
}
void pwarn(char *fmt, ...)
/* Treat conf file like options passed on command line
*/
static int getopt_long_file(FILE *f, const struct option *longopts,
char **optarg, int *longindex)
{
va_list arg;
char *newfmt;
int rc;
static char line[256];
char *pos, *opt, *save;
int i;
if (conf_quiet || names_only || option == OPTION_REMOVE)
return;
for (;;) {
if (!fgets(line, 256, f))
return -1;
pos = line;
while (isblank(*pos))
pos++;
if (*pos == '#')
continue;
opt = strtok_r(pos, " \t\r\n=", &save);
/* blank line */
if (!opt)
continue;
rc = asprintf(&newfmt, _("Warning from %s (%s%sline %d): %s"),
profilename ? profilename : "stdin",
current_filename ? current_filename : "",
current_filename ? " " : "",
current_lineno,
fmt);
if (!newfmt)
return;
for (i = 0; longopts[i].name &&
strcmp(longopts[i].name, opt) != 0; i++) ;
if (!longopts[i].name) {
PERROR("%s: unknown option (%s) in config file.\n",
progname, opt);
/* skip it */
continue;
}
break;
}
va_start(arg, fmt);
vfprintf(stderr, newfmt, arg);
va_end(arg);
if (longindex)
*longindex = i;
free(newfmt);
if (*save) {
int len;
while(isblank(*save))
save++;
len = strlen(save) - 1;
if (save[len] == '\n')
save[len] = 0;
}
switch (longopts[i].has_arg) {
case 0:
*optarg = NULL;
break;
case 1:
if (!strlen(save)) {
*optarg = NULL;
return '?';
}
*optarg = save;
break;
case 2:
*optarg = save;
break;
default:
PERROR("%s: internal error bad longopt value\n", progname);
exit(1);
}
if (longopts[i].flag == NULL)
return longopts[i].val;
else
*longopts[i].flag = longopts[i].val;
return 0;
}
/* process a single argment from getopt_long
* Returns: 1 if an action arg, else 0
*/
static int process_arg(int c, char *optarg)
{
int count = 0;
switch (c) {
case 0:
PERROR("Assert, in getopt_long handling\n");
display_usage(progname);
exit(0);
break;
case 'a':
count++;
option = OPTION_ADD;
break;
case 'd':
debug++;
skip_read_cache = 1;
break;
case 'h':
if (!optarg) {
display_usage(progname);
} else if (strcmp(optarg, "Dump") == 0 ||
strcmp(optarg, "dump") == 0 ||
strcmp(optarg, "D") == 0) {
display_dump(progname);
} else if (strcmp(optarg, "Optimize") == 0 ||
strcmp(optarg, "optimize") == 0 ||
strcmp(optarg, "O") == 0) {
display_optimize(progname);
} else {
PERROR("%s: Invalid --help option %s\n",
progname, optarg);
exit(1);
}
exit(0);
break;
case 'r':
count++;
option = OPTION_REPLACE;
break;
case 'R':
count++;
option = OPTION_REMOVE;
skip_cache = 1;
break;
case 'V':
display_version();
exit(0);
break;
case 'I':
add_search_dir(optarg);
break;
case 'b':
set_base_dir(optarg);
break;
case 'B':
binary_input = 1;
skip_cache = 1;
break;
case 'C':
opt_force_complain = 1;
skip_cache = 1;
break;
case 'N':
names_only = 1;
skip_cache = 1;
break;
case 'S':
count++;
option = OPTION_STDOUT;
skip_read_cache = 1;
kernel_load = 0;
break;
case 'o':
count++;
option = OPTION_OFILE;
skip_read_cache = 1;
kernel_load = 0;
ofile = fopen(optarg, "w");
if (!ofile) {
PERROR("%s: Could not open file %s\n",
progname, optarg);
exit(1);
}
break;
case 'f':
subdomainbase = strndup(optarg, PATH_MAX);
break;
case 'D':
skip_read_cache = 1;
if (!optarg) {
dump_vars = 1;
} else if (strcmp(optarg, "variables") == 0) {
dump_vars = 1;
} else if (strcmp(optarg, "expanded-variables") == 0) {
dump_expanded_vars = 1;
} else if (!handle_flag_table(dumpflag_table, optarg,
&dfaflags)) {
PERROR("%s: Invalid --Dump option %s\n",
progname, optarg);
exit(1);
}
break;
case 'O':
skip_read_cache = 1;
if (!handle_flag_table(optflag_table, optarg,
&dfaflags)) {
PERROR("%s: Invalid --Optimize option %s\n",
progname, optarg);
exit(1);
}
break;
case 'm':
match_string = strdup(optarg);
break;
case 'q':
conf_verbose = 0;
conf_quiet = 1;
break;
case 'v':
conf_verbose = 1;
conf_quiet = 0;
break;
case 'n':
profile_namespace = strdup(optarg);
break;
case 'X':
read_implies_exec = 1;
break;
case 'K':
skip_cache = 1;
break;
case 'k':
show_cache = 1;
break;
case 'W':
write_cache = 1;
break;
case 'T':
skip_read_cache = 1;
break;
case 'Q':
kernel_load = 0;
break;
case 'p':
count++;
kernel_load = 0;
skip_cache = 1;
preprocess_only = 1;
skip_mode_force = 1;
break;
default:
display_usage(progname);
exit(0);
break;
}
return count;
}
static int process_args(int argc, char *argv[])
@@ -355,159 +549,7 @@ static int process_args(int argc, char *argv[])
while ((c = getopt_long(argc, argv, "adf:h::rRVvI:b:BCD:NSm:qQn:XKTWkO:po:", long_options, &o)) != -1)
{
switch (c) {
case 0:
PERROR("Assert, in getopt_long handling\n");
display_usage(progname);
exit(0);
break;
case 'a':
count++;
option = OPTION_ADD;
break;
case 'd':
debug++;
skip_read_cache = 1;
break;
case 'h':
if (!optarg) {
display_usage(progname);
} else if (strcmp(optarg, "Dump") == 0 ||
strcmp(optarg, "dump") == 0 ||
strcmp(optarg, "D") == 0) {
display_dump(progname);
} else if (strcmp(optarg, "Optimize") == 0 ||
strcmp(optarg, "optimize") == 0 ||
strcmp(optarg, "O") == 0) {
display_optimize(progname);
} else {
PERROR("%s: Invalid --help option %s\n",
progname, optarg);
exit(1);
}
exit(0);
break;
case 'r':
count++;
option = OPTION_REPLACE;
break;
case 'R':
count++;
option = OPTION_REMOVE;
skip_cache = 1;
break;
case 'V':
display_version();
exit(0);
break;
case 'I':
add_search_dir(optarg);
break;
case 'b':
set_base_dir(optarg);
break;
case 'B':
binary_input = 1;
skip_cache = 1;
break;
case 'C':
opt_force_complain = 1;
skip_cache = 1;
break;
case 'N':
names_only = 1;
skip_cache = 1;
break;
case 'S':
count++;
option = OPTION_STDOUT;
skip_read_cache = 1;
kernel_load = 0;
break;
case 'o':
count++;
option = OPTION_OFILE;
skip_read_cache = 1;
kernel_load = 0;
ofile = fopen(optarg, "w");
if (!ofile) {
PERROR("%s: Could not open file %s\n",
progname, optarg);
exit(1);
}
break;
case 'f':
subdomainbase = strndup(optarg, PATH_MAX);
break;
case 'D':
skip_read_cache = 1;
if (!optarg) {
dump_vars = 1;
} else if (strcmp(optarg, "variables") == 0) {
dump_vars = 1;
} else if (strcmp(optarg, "expanded-variables") == 0) {
dump_expanded_vars = 1;
} else if (!handle_flag_table(dumpflag_table, optarg,
&dfaflags)) {
PERROR("%s: Invalid --Dump option %s\n",
progname, optarg);
exit(1);
}
break;
case 'O':
skip_read_cache = 1;
if (!handle_flag_table(optflag_table, optarg,
&dfaflags)) {
PERROR("%s: Invalid --Optimize option %s\n",
progname, optarg);
exit(1);
}
break;
case 'm':
match_string = strdup(optarg);
break;
case 'q':
conf_verbose = 0;
conf_quiet = 1;
break;
case 'v':
conf_verbose = 1;
conf_quiet = 0;
break;
case 'n':
profile_namespace = strdup(optarg);
break;
case 'X':
read_implies_exec = 1;
break;
case 'K':
skip_cache = 1;
break;
case 'k':
show_cache = 1;
break;
case 'W':
write_cache = 1;
break;
case 'T':
skip_read_cache = 1;
break;
case 'Q':
kernel_load = 0;
break;
case 'p':
count++;
kernel_load = 0;
skip_cache = 1;
preprocess_only = 1;
skip_mode_force = 1;
break;
default:
display_usage(progname);
exit(0);
break;
}
count += process_arg(c, optarg);
}
if (count > 1) {
@@ -521,6 +563,21 @@ static int process_args(int argc, char *argv[])
return optind;
}
static int process_config_file(const char *name)
{
char *optarg;
FILE *f;
int c, o;
f = fopen(name, "r");
if (!f)
return 0;
while ((c = getopt_long_file(f, long_options, &optarg, &o)) != -1)
process_arg(c, optarg);
return 1;
}
static inline char *try_subdomainfs_mountpoint(const char *mntpnt,
const char *path)
{
@@ -950,8 +1007,7 @@ out:
}
else {
unlink(cachetemp);
if (show_cache)
PERROR("Removed cache attempt: %s\n", cachetemp);
PERROR("Warning failed to create cache: %s\n", basename);
}
free(cachetemp);
}
@@ -1033,6 +1089,7 @@ int main(int argc, char *argv[])
init_base_dir();
process_config_file("/etc/apparmor/parser.conf");
optind = process_args(argc, argv);
setlocale(LC_MESSAGES, "");

View File

@@ -129,6 +129,9 @@ static int get_table_token(const char *name __unused, struct keyword_table *tabl
static struct keyword_table capability_table[] = {
/* capabilities */
#include "cap_names.h"
#ifndef CAP_SYSLOG
{"syslog", 34},
#endif
/* terminate */
{NULL, 0}
};
@@ -430,7 +433,7 @@ char *processquoted(char *string, int len)
*s = '\\';
l++;
break;
case '0' - '3':
case '0': case '1': case '2': case '3':
if ((l < len - 4) &&
strchr("0123456789", string[l + 2]) &&
strchr("0123456789", string[l + 3])) {
@@ -866,6 +869,7 @@ static const char *capnames[] = {
"audit_control",
"setfcap",
"mac_override"
"syslog",
};
const char *capability_to_name(unsigned int cap)
@@ -929,27 +933,6 @@ void debug_cod_list(struct codomain *cod)
}
#ifdef UNIT_TEST
#define MY_TEST(statement, error) \
if (!(statement)) { \
PERROR("FAIL: %s\n", error); \
rc = 1; \
}
/* Guh, fake routine */
void yyerror(char *msg, ...)
{
va_list arg;
char buf[PATH_MAX];
va_start(arg, msg);
vsnprintf(buf, sizeof(buf), msg, arg);
va_end(arg);
PERROR(_("AppArmor parser error: %s\n"), buf);
exit(1);
}
int test_str_to_boolean(void)
{
int rc = 0;
@@ -973,7 +956,7 @@ int test_str_to_boolean(void)
int test_processunquoted(void)
{
int rc = 0;
const char *teststring, *processedstring;
char *teststring, *processedstring;
teststring = "";
MY_TEST(strcmp(teststring, processunquoted(teststring, strlen(teststring))) == 0,
@@ -998,6 +981,106 @@ int test_processunquoted(void)
return rc;
}
int test_processquoted(void)
{
int rc = 0;
char *teststring, *processedstring;
char *out;
teststring = "";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(teststring, out) == 0,
"processquoted on empty string");
free(out);
teststring = "\"abcdefg\"";
processedstring = "abcdefg";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted on simple string");
free(out);
teststring = "\"abcd\\tefg\"";
processedstring = "abcd\tefg";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted on string with tab");
free(out);
teststring = "\"abcdefg\\\"";
processedstring = "abcdefg\\";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted on trailing slash");
free(out);
teststring = "\"a\\\\bcdefg\"";
processedstring = "a\\bcdefg";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted on quoted slash");
free(out);
teststring = "\"a\\\"bcde\\\"fg\"";
processedstring = "a\"bcde\"fg";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted on quoted quotes");
free(out);
teststring = "\"\\rabcdefg\"";
processedstring = "\rabcdefg";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted on quoted \\r");
free(out);
teststring = "\"abcdefg\\n\"";
processedstring = "abcdefg\n";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted on quoted \\n");
free(out);
teststring = "\"\\Uabc\\Ndefg\\x\"";
processedstring = "\\Uabc\\Ndefg\\x";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted passthrough on invalid quoted chars");
free(out);
teststring = "\"abc\\042defg\"";
processedstring = "abc\"defg";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted on quoted octal \\042");
free(out);
teststring = "\"abcdefg\\176\"";
processedstring = "abcdefg~";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted on quoted octal \\176");
free(out);
/* yes, our octal processing is lame; patches accepted */
teststring = "\"abc\\42defg\"";
processedstring = "abc\\42defg";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted passthrough quoted invalid octal \\42");
free(out);
teststring = "\"abcdefg\\04\"";
processedstring = "abcdefg\\04";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted passthrough quoted invalid trailing octal \\04");
free(out);
return rc;
}
int main(void)
{
int rc = 0;
@@ -1010,6 +1093,11 @@ int main(void)
retval = test_processunquoted();
if (retval != 0)
rc = retval;
retval = test_processquoted();
if (retval != 0)
rc = retval;
return rc;
}
#endif /* UNIT_TEST */

View File

@@ -16,6 +16,7 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <libintl.h>
@@ -26,6 +27,7 @@
#include "parser.h"
#include "libapparmor_re/apparmor_re.h"
#include "libapparmor_re/aare_rules.h"
enum error_type {
e_no_error,
@@ -615,29 +617,6 @@ void reset_regex(void)
}
#ifdef UNIT_TEST
#define MY_TEST(statement, error) \
if (!(statement)) { \
PERROR("FAIL: %s\n", error); \
rc = 1; \
}
/* Guh, fake routine */
void yyerror(char *msg, ...)
{
va_list arg;
char buf[PATH_MAX];
va_start(arg, msg);
vsnprintf(buf, sizeof(buf), msg, arg);
va_end(arg);
PERROR(_("AppArmor parser error: %s\n"), buf);
exit(1);
}
/* Guh, fake symbol */
char *progname;
static int test_filter_slashes(void)
{
int rc = 0;

View File

@@ -539,30 +539,6 @@ void free_symtabs(void)
}
#ifdef UNIT_TEST
#define MY_TEST(statement, error) \
if (!(statement)) { \
PERROR("FAIL: %s\n", error); \
rc = 1; \
}
/* Guh, fake symbol */
char *progname;
/* Guh, fake routine */
void yyerror(char *msg, ...)
{
va_list arg;
char buf[PATH_MAX];
va_start(arg, msg);
vsnprintf(buf, sizeof(buf), msg, arg);
va_end(arg);
PERROR(_("AppArmor parser error: %s\n"), buf);
exit(1);
}
int main(void)
{
int rc = 0;
@@ -589,7 +565,7 @@ int main(void)
retval = new_set_var("test", "different value");
MY_TEST(retval != 0, "new set variable 2");
retval = new_set_var("testes", "testes");
retval = new_set_var("testing", "testing");
MY_TEST(retval == 0, "new set variable 3");
retval = new_set_var("monopuff", "Mockingbird");

View File

@@ -36,8 +36,14 @@ static inline char *get_var_end(char *var)
while (*eptr) {
if (*eptr == '}')
return eptr;
if (!(*eptr == '_' || isalpha(*eptr)))
return NULL; /* invalid char */
/* first character must be alpha */
if (eptr == var) {
if (!isalpha(*eptr))
return NULL; /* invalid char */
} else {
if (!(*eptr == '_' || isalnum(*eptr)))
return NULL; /* invalid char */
}
eptr++;
}
return NULL; /* no terminating '}' */
@@ -210,29 +216,6 @@ int process_variables(struct codomain *cod)
}
#ifdef UNIT_TEST
#define MY_TEST(statement, error) \
if (!(statement)) { \
PERROR("FAIL: %s\n", error); \
rc = 1; \
}
/* Guh, fake routine */
void yyerror(char *msg, ...)
{
va_list arg;
char buf[PATH_MAX];
va_start(arg, msg);
vsnprintf(buf, sizeof(buf), msg, arg);
va_end(arg);
PERROR(_("AppArmor parser error: %s\n"), buf);
exit(1);
}
/* Guh, fake symbol */
char *progname;
int test_get_var_end(void)
{
int rc = 0;
@@ -271,7 +254,7 @@ int test_split_string(void)
char *var = "boogie";
char *suffix = "suffixication";
(void) asprintf(&tst_string, "%s@{%s}%s", prefix, var, suffix);
asprintf(&tst_string, "%s@{%s}%s", prefix, var, suffix);
var_start = tst_string + strlen(prefix);
var_end = var_start + strlen(var) + strlen("@\{");
ret_struct = split_string(tst_string, var_start, var_end);
@@ -317,6 +300,8 @@ int test_split_out_var(void)
struct var_string *ret_struct;
char *prefix = "abcdefg";
char *var = "boogie";
char *var2 = "V4rW1thNum5";
char *var3 = "boogie_board";
char *suffix = "suffixication";
/* simple case */
@@ -394,6 +379,34 @@ int test_split_out_var(void)
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split out var 7 suffix");
free_var_string(ret_struct);
/* numeric char in var, should succeed */
asprintf(&tst_string, "%s@{%s}%s", prefix, var2, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct && strcmp(ret_struct->prefix, prefix) == 0, "split out numeric var prefix");
MY_TEST(ret_struct && strcmp(ret_struct->var, var2) == 0, "split numeric var var");
MY_TEST(ret_struct && strcmp(ret_struct->suffix, suffix) == 0, "split out numeric var suffix");
free_var_string(ret_struct);
/* numeric first char in var, should fail */
asprintf(&tst_string, "%s@{6%s}%s", prefix, var2, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct == NULL, "split out var - numeric first char in var");
free_var_string(ret_struct);
/* underscore char in var, should succeed */
asprintf(&tst_string, "%s@{%s}%s", prefix, var3, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct && strcmp(ret_struct->prefix, prefix) == 0, "split out underscore var prefix");
MY_TEST(ret_struct && strcmp(ret_struct->var, var3) == 0, "split out underscore var");
MY_TEST(ret_struct && strcmp(ret_struct->suffix, suffix) == 0, "split out underscore var suffix");
free_var_string(ret_struct);
/* underscore first char in var, should fail */
asprintf(&tst_string, "%s@{_%s%s}%s", prefix, var2, var3, suffix);
ret_struct = split_out_var(tst_string);
MY_TEST(ret_struct == NULL, "split out var - underscore first char in var");
free_var_string(ret_struct);
return rc;
}
int main(void)

View File

@@ -240,7 +240,7 @@ profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE
post_process_nt_entries(cod);
PDEBUG("%s: flags='%s%s'\n",
$3,
$2,
cod->flags.complain ? "complain, " : "",
cod->flags.audit ? "audit" : "");
@@ -280,7 +280,7 @@ hat: hat_start profile_base
{
struct codomain *cod = $2;
if ($2)
PDEBUG("Matched: hat %s { ... }\n", code->name);
PDEBUG("Matched: hat %s { ... }\n", cod->name);
cod->flags.hat = 1;
$$ = cod;
@@ -384,7 +384,7 @@ valuelist: valuelist TOK_VALUE
struct value_list *new = calloc(1, sizeof(struct value_list));
if (!new)
yyerror(_("Memory allocation error."));
PDEBUG("Matched: value (%s)\n", $1);
PDEBUG("Matched: value list\n");
new->value = $2;
new->next = $1;
@@ -711,10 +711,31 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
if (strcmp($6, "infinity") == 0) {
value = RLIM_INFINITY;
} else {
const char *seconds = "seconds";
const char *minutes = "minutes";
const char *hours = "hours";
const char *days = "days";
const char *kb = "KB";
const char *mb = "MB";
const char *gb = "GB";
tmp = strtoll($6, &end, 0);
switch (limit) {
case RLIMIT_CPU:
yyerror("RLIMIT '%s' is currently unsupported\n", $4);
if (!end || $6 == end || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
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);
}
break;
case RLIMIT_NOFILE:
case RLIMIT_NPROC:
@@ -722,14 +743,14 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
case RLIMIT_SIGPENDING:
#ifdef RLIMIT_RTPRIO
case RLIMIT_RTPRIO:
if ($6 == end || *end != '\0' || tmp < 0)
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 ($6 == end || *end != '\0')
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);
@@ -746,11 +767,11 @@ rules: rules TOK_SET TOK_RLIMIT TOK_ID TOK_LE TOK_VALUE TOK_END_OF_RULE
case RLIMIT_MSGQUEUE:
if ($6 == end || tmp < 0)
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);
if (strcmp(end, "K") == 0) {
if (strstr(kb, end) == kb) {
tmp *= 1024;
} else if (strcmp(end, "M") == 0) {
} else if (strstr(mb, end) == mb) {
tmp *= 1024*1024;
} else if (strcmp(end, "G") == 0) {
} else if (strstr(gb, end) == gb) {
tmp *= 1024*1024*1024;
} else if (*end != '\0') {
yyerror("RLIMIT '%s' invalid value %s\n", $4, $6);

View File

@@ -27,7 +27,7 @@
### BEGIN INIT INFO
# Provides: aaeventd
# Required-Start: apparmor
# Required-Stop:
# Required-Stop: $null
# Default-Start: 2 3 5
# Default-Stop:
# Short-Description: AppArmor Notification and Reporting
@@ -78,9 +78,9 @@ usage() {
start_aa_event() {
if [ -x "$AA_EV_BIN" -a "${APPARMOR_ENABLE_AAEVENTD}" = "yes" ] ; then
sd_action "Starting AppArmor Event daemon" startproc -f -p $AA_EV_PIDFILE $AA_EV_BIN -p $AA_EV_PIDFILE
sd_action "Starting AppArmor Event daemon" startproc -p $AA_EV_PIDFILE $AA_EV_BIN -p $AA_EV_PIDFILE
elif [ -x "$SD_EV_BIN" -a "${APPARMOR_ENABLE_AAEVENTD}" = "yes" ] ; then
sd_action "Starting AppArmor Event daemon" startproc -f -p $SD_EV_PIDFILE $SD_EV_BIN -p $SD_EV_PIDFILE
sd_action "Starting AppArmor Event daemon" startproc -p $SD_EV_PIDFILE $SD_EV_BIN -p $SD_EV_PIDFILE
fi
}

View File

@@ -83,15 +83,6 @@ SECURITYFS=/sys/kernel/security
SUBDOMAINFS_MOUNTPOINT=$(grep subdomainfs /etc/fstab | \
sed -e 's|^[[:space:]]*[^[:space:]]\+[[:space:]]\+\(/[^[:space:]]*\)[[:space:]]\+subdomainfs.*$|\1|' 2> /dev/null)
if [ -d "/var/lib/${MODULE}" ] ; then
APPARMOR_TMPDIR="/var/lib/${MODULE}"
elif [ -d "/var/lib/${OLD_MODULE}" ] ; then
APPARMOR_TMPDIR="/var/lib/${OLD_MODULE}"
else
APPARMOR_TMPDIR="/tmp"
fi
# keep exit status from parser during profile load. 0 is good, 1 is bad
STATUS=0
@@ -108,9 +99,7 @@ is_apparmor_present() {
# check for subdomainfs version of module
grep -qE "^($modules)[[:space:]]" /proc/modules
if [ $? -ne 0 ] ; then
ls /sys/module/apparmor 2>/dev/null | grep -qE "^($modules)"
fi
[ $? -ne 0 -a -d /sys/module/apparmor ]
return $?
}
@@ -170,7 +159,7 @@ parse_profiles() {
exit 1
;;
esac
aa_log_action_begin "$PARSER_MSG"
aa_log_action_start "$PARSER_MSG"
# run the parser on all of the apparmor profiles
if [ ! -f "$PARSER" ]; then
aa_log_failure_msg "AppArmor parser not found"
@@ -198,6 +187,7 @@ parse_profiles() {
aa_log_skipped_msg "$profile"
logger -t "AppArmor(init)" -p daemon.warn "Skipping profile $profile"
STATUS=2
continue
elif [ "$skip" -ne 0 ]; then
continue
fi
@@ -222,7 +212,6 @@ parse_profiles() {
profiles_names_list() {
# run the parser on all of the apparmor profiles
TMPFILE=$1
if [ ! -f "$PARSER" ]; then
aa_log_failure_msg "- AppArmor parser not found"
exit 1
@@ -235,9 +224,9 @@ profiles_names_list() {
for profile in $PROFILE_DIR/*; do
if skip_profile "${profile}" && [ -f "${profile}" ] ; then
LIST_ADD=$($PARSER $ABSTRACTIONS -N "$profile" | grep -v '\^')
LIST_ADD=$($PARSER $ABSTRACTIONS -N "$profile" )
if [ $? -eq 0 ]; then
echo "$LIST_ADD" >>$TMPFILE
echo "$LIST_ADD"
fi
fi
done
@@ -297,7 +286,7 @@ is_apparmor_loaded() {
}
is_securityfs_mounted() {
grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts
test -d ${SECURITYFS} -a -d /sys/fs/cgroup/systemd || grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts
return $?
}
@@ -377,10 +366,11 @@ apparmor_start() {
configure_owlsm
# if there is anything in the profiles file don't load
cat "$SFS_MOUNTPOINT/profiles" | if ! read line ; then
if ! read line < "$SFS_MOUNTPOINT/profiles"; then
parse_profiles load
else
aa_log_skipped_msg "AppArmor already loaded with profiles."
aa_log_skipped_msg ": already loaded with profiles."
return 0
fi
aa_log_end_msg 0
return 0
@@ -408,18 +398,16 @@ remove_profiles() {
fi
retval=0
#the list of profiles isn't stable once we start adding or removing
#them so stor to tmp first
MODULE_PLIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX)
sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | sort >"$MODULE_PLIST"
cat "$MODULE_PLIST" | while read profile ; do
# We filter child profiles as removing the parent will remove
# the children
sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | \
LC_COLLATE=C sort | grep -v // | while read profile ; do
echo -n "$profile" > "$SFS_MOUNTPOINT/.remove"
rc=$?
if [ ${rc} -ne 0 ] ; then
retval=${rc}
fi
done
rm "$MODULE_PLIST"
return ${retval}
}
@@ -427,7 +415,7 @@ apparmor_stop() {
aa_log_daemon_msg "Unloading AppArmor profiles "
remove_profiles
rc=$?
log_end_msg $rc
aa_log_end_msg $rc
return $rc
}
@@ -459,20 +447,41 @@ __apparmor_restart() {
return 4
fi
aa_log_daemon_msg "Restarting AppArmor"
configure_owlsm
parse_profiles reload
PNAMES_LIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX)
profiles_names_list ${PNAMES_LIST}
MODULE_PLIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX)
# Clean out running profiles not associated with the current profile
# set, excluding the libvirt dynamically generated profiles.
sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | egrep -v '^libvirt-[0-9a-f\-]+$' | sort >"$MODULE_PLIST"
sort "$PNAMES_LIST" | comm -2 -3 "$MODULE_PLIST" - | while IFS= read profile ; do
# Note that we reverse sort the list of profiles to remove to
# ensure that child profiles (e.g. hats) are removed before the
# parent. We *do* need to remove the child profile and not rely
# on removing the parent profile when the profile has had its
# child profile names changed.
profiles_names_list | awk '
BEGIN {
while (getline < "'${SFS_MOUNTPOINT}'/profiles" ) {
str = sub(/ \((enforce|complain)\)$/, "", $0);
if (match($0, /^libvirt-[0-9a-f\-]+$/) == 0)
arr[$str] = $str
}
}
{ if (length(arr[$0]) > 0) { delete arr[$0] } }
END {
for (key in arr)
if (length(arr[key]) > 0) {
printf("%s\n", arr[key])
}
}
' | LC_COLLATE=C sort -r | while IFS= read profile ; do
echo -n "$profile" > "$SFS_MOUNTPOINT/.remove"
done
rm "$MODULE_PLIST"
rm "$PNAMES_LIST"
return 0
# will not catch all errors, but still better than nothing
rc=$?
aa_log_end_msg $rc
return $rc
}
apparmor_restart() {
@@ -516,11 +525,11 @@ apparmor_status () {
${SD_STATUS} --verbose
return $?
fi
if ! is_apparmor_present apparmor subdomain ; then
if ! is_apparmor_loaded ; then
echo "AppArmor is not loaded."
rc=1
else
echo "AppArmor is enabled,"
echo "AppArmor is enabled."
rc=0
fi
echo "Install the apparmor-utils package to receive more detailed"

View File

@@ -31,6 +31,7 @@
# Required-Start: boot.cleanup
# Required-Stop: $null
# Should-Start: $local_fs
# Should-Stop: $null
# Default-Start: B
# Default-Stop:
# Short-Description: AppArmor initialization
@@ -73,7 +74,19 @@ aa_log_warning_msg() {
}
aa_log_failure_msg() {
log_failure_msg $*
log_failure_msg '\n'$*
}
aa_log_action_start() {
echo -n
}
aa_log_action_end() {
echo -n
}
aa_log_daemon_msg() {
echo -en "$@ "
}
aa_log_skipped_msg() {
@@ -81,6 +94,14 @@ aa_log_skipped_msg() {
echo -e "$rc_skipped"
}
aa_log_end_msg() {
v="-v"
if [ "$1" != '0' ]; then
rc="-v$1"
fi
rc_status $v
}
usage() {
echo "Usage: $0 {start|stop|restart|try-restart|reload|force-reload|status|kill}"
}

View File

@@ -4,9 +4,10 @@ TESTS=simple.pl
PARSER_DIR=..
PARSER_BIN=apparmor_parser
PARSER=$(PARSER_DIR)/$(PARSER_BIN)
PROVE_ARG=-f
ifeq ($(VERBOSE),1)
PROVE_ARG=-v
PROVE_ARG+=-v
endif
all: tests
@@ -14,9 +15,14 @@ all: tests
.PHONY: tests error_output gen_xtrans parser_sanity caching
tests: error_output gen_xtrans parser_sanity caching
gen_xtrans:
GEN_TRANS_DIRS=simple_tests/generated_x/ simple_tests/generated_perms_leading/ simple_tests/generated_perms_safe/
gen_xtrans: $(GEN_TRANS_DIRS)
./gen-xtrans.pl
$(GEN_TRANS_DIRS):
mkdir $@
error_output: $(PARSER)
$(PARSER) -S -I errors >/dev/null errors/okay.sd
LANG=C $(PARSER) -S -I errors 2>&1 >/dev/null errors/single.sd | \
@@ -39,6 +45,4 @@ $(PARSER):
make -C $(PARSER_DIR) $(PARSER_BIN)
clean:
rm -f simple_tests/generated_x/*
rm -f simple_tests/generated_perms_leading/*
rm -f simple_tests/generated_perms_safe/*
find $(GEN_TRANS_DIRS) -type f | xargs rm -f

View File

@@ -118,7 +118,7 @@ sub gen_file($$$$$$$$$$$$) {
print $file "/usr/bin/foo {\n";
print_rule($file, $leading1, $qual1, $rule1, $perm1, $target1);
print_rule($file, $leading2, $qual2, $rule2, $perm2, $target2);
print $file "}";
print $file "}\n";
close($file);
$count++;

View File

@@ -0,0 +1,9 @@
#=DESCRIPTION reference variables in rules that also have alternations
#=EXRESULT PASS
@{FOO}=bar baz
@{BAR}=@{FOO} blort
/does/not/exist {
/does/not/{exist,notexist}/blah/@{BAR} r,
}

View File

@@ -0,0 +1,9 @@
#=DESCRIPTION reference variables in rules that also have alternations
#=EXRESULT PASS
@{FOO}=bar baz
@{BAR}=@{FOO} blort
/does/not/exist {
/does/not/@{BAR}/blah/{exist,notexist} r,
}

View File

@@ -0,0 +1,7 @@
#=DESCRIPTION don't accept variables with leading underscores
#=EXRESULT FAIL
@{_FOO} = /foo /bar /baz /biff
/usr/bin/foo {
/@{_FOO}/.foo/* r,
}

View File

@@ -0,0 +1,7 @@
#=DESCRIPTION don't accept variables with leading numeric
#=EXRESULT FAIL
@{4FOO} = /foo /bar /baz /biff
/usr/bin/foo {
/@{4FOO}/.foo/* r,
}

View File

@@ -0,0 +1,9 @@
#=DESCRIPTION simple expansions within file rules with numeric variable
#=EXRESULT PASS
@{FOO1} = /foo /bar /baz /biff
@{B1A1R1} = @{FOO1}
/usr/bin/foo {
/@{FOO1}/.foo/* r,
/foo/@{B1A1R1}/.foo/* r,
}

View File

@@ -0,0 +1,7 @@
#=DESCRIPTION simple expansions within file rules with underscore variable
#=EXRESULT PASS
@{F_OO} = /foo /bar /baz /biff
/usr/bin/foo {
/@{F_OO}/.foo/* r,
}

View File

@@ -20,7 +20,7 @@
# Makefile for LSM-based AppArmor profiles
NAME=apparmor-profiles
ALL:
ALL: local
COMMONDIR=../common/
include common/Make.rules
@@ -38,7 +38,7 @@ PROFILES_SOURCE=./apparmor.d
EXTRAS_SOURCE=./apparmor/profiles/extras/
SUBDIRS_MUST_BE_SKIPPED=${PROFILES_SOURCE}/abstractions ${PROFILES_SOURCE}/apache2.d ${PROFILES_SOURCE}/program-chunks ${PROFILES_SOURCE}/tunables ${PROFILES_SOURCE}/local
PROFILES_TO_COPY=$(filter-out ${SUBDIRS_MUST_BE_SKIPPED}, $(wildcard ${PROFILES_SOURCE}/*))
TUNABLES_TO_COPY=$(filter-out ${PROFILES_SOURCE}/tunables/home.d, $(wildcard ${PROFILES_SOURCE}/tunables/*))
TUNABLES_TO_COPY=$(filter-out ${PROFILES_SOURCE}/tunables/home.d ${PROFILES_SOURCE}/tunables/multiarch.d, $(wildcard ${PROFILES_SOURCE}/tunables/*))
ABSTRACTIONS_TO_COPY=$(filter-out ${PROFILES_SOURCE}/abstractions/ubuntu-browsers.d, $(wildcard ${PROFILES_SOURCE}/abstractions/*))
local:
@@ -52,6 +52,7 @@ install: local
install -m 755 -d ${PROFILES_DEST}
install -m 755 -d ${PROFILES_DEST}/abstractions \
${PROFILES_DEST}/apache2.d \
${PROFILES_DEST}/disable \
${PROFILES_DEST}/program-chunks \
${PROFILES_DEST}/tunables \
${PROFILES_DEST}/tunables/home.d \
@@ -64,6 +65,7 @@ install: local
install -m 644 ${PROFILES_SOURCE}/program-chunks/* ${PROFILES_DEST}/program-chunks
install -m 644 ${TUNABLES_TO_COPY} ${PROFILES_DEST}/tunables
install -m 644 ${PROFILES_SOURCE}/tunables/home.d/* ${PROFILES_DEST}/tunables/home.d
install -m 644 ${PROFILES_SOURCE}/tunables/multiarch.d/* ${PROFILES_DEST}/tunables/multiarch.d
install -m 755 -d ${EXTRAS_DEST}
install -m 644 ${EXTRAS_SOURCE}/* ${EXTRAS_DEST}
install -m 644 ${PROFILES_SOURCE}/local/* ${PROFILES_DEST}/local
@@ -81,23 +83,22 @@ endif
ifndef PARSER
# use system parser
PARSER=/sbin/apparmor_parser
PARSER=../parser/apparmor_parser
endif
ifndef LOGPROF
# use system logprof
LOGPROF=/usr/sbin/aa-logprof
# use ../utils logprof
LOGPROF=perl -I../utils/ ../utils/aa-logprof
endif
EXTRAS_PATH=${EXTRAS_SOURCE}/profiles/extras
IGNORE_FILES=${EXTRAS_PATH}/README
CHECK_PROFILES=$(filter-out ${IGNORE_FILES}, $(wildcard ${PROFILES_SOURCE}/*) $(wildcard ${EXTRAS_PATH}/*))
IGNORE_FILES=${EXTRAS_SOURCE}/README
CHECK_PROFILES=$(filter-out ${IGNORE_FILES} ${SUBDIRS_MUST_BE_SKIPPED}, $(wildcard ${PROFILES_SOURCE}/*) $(wildcard ${EXTRAS_SOURCE}/*))
.PHONY: check
check:
@echo "*** Checking profiles from ${PROFILES_SOURCE} and ${EXTRAS_PATH} against apparmor_parser"
@echo "*** Checking profiles from ${PROFILES_SOURCE} and ${EXTRAS_SOURCE} against apparmor_parser"
$(Q)for profile in ${CHECK_PROFILES} ; do \
${PARSER} -S -I ${PWD}/apparmor.d $${profile} > /dev/null ; \
${PARSER} -S -b ${PWD}/apparmor.d $${profile} > /dev/null || exit 1; \
done
@echo "*** Checking profiles from ${PROFILES_SOURCE} against logprof"
$(Q)${LOGPROF} -d ${PROFILES_SOURCE} -f /dev/null
$(Q)${LOGPROF} -d ${PROFILES_SOURCE} -f /dev/null || exit 1

View File

@@ -2,7 +2,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2009 Novell/SUSE
# Copyright (C) 2009 Canonical Ltd.
# Copyright (C) 2009-2011 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
@@ -17,7 +17,8 @@
# .Xauthority files required for X connections, per user
@{HOME}/.Xauthority r,
owner /var/run/gdm/*/database r,
owner /{,var/}run/gdm/*/database r,
owner /{,var/}run/lightdm/authority/[0-9]* r,
# the unix socket to use to connect to the display
/tmp/.X11-unix/* w,
@@ -33,6 +34,7 @@
# DRI
/usr/lib{,32,64}/dri/** mr,
/usr/lib/@{multiarch}/dri/** mr,
/dev/dri/** rw,
# mouse themes

View File

@@ -50,8 +50,8 @@
# pulse
/etc/pulse/ r,
/etc/pulse/* r,
/dev/shm/ r,
owner /dev/shm/pulse-shm* rwk,
/{run,dev}/shm/ r,
owner /{run,dev}/shm/pulse-shm* rwk,
owner @{HOME}/.pulse-cookie rwk,
owner @{HOME}/.pulse/ rw,
owner @{HOME}/.pulse/* rwk,
@@ -61,3 +61,7 @@ owner /tmp/pulse-*/* rw,
# libgnome2
/etc/sound/ r,
/etc/sound/** r,
# openal
/etc/openal/alsoft.conf r,
owner @{HOME}/.alsoftrc r,

View File

@@ -1,7 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2009 Novell/SUSE
# Copyright (C) 2009 Canonical Ltd
# Copyright (C) 2009-2011 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
@@ -25,6 +25,9 @@
/lib{,32,64}/security/pam_filter/* mr,
/lib{,32,64}/security/pam_*.so mr,
/lib{,32,64}/security/ r,
/lib/@{multiarch}/security/pam_filter/* mr,
/lib/@{multiarch}/security/pam_*.so mr,
/lib/@{multiarch}/security/ r,
# kerberos
#include <abstractions/kerberosclient>

View File

@@ -2,7 +2,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2009 Novell/SUSE
# Copyright (C) 2009 Canonical Ltd.
# Copyright (C) 2009-2011 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
@@ -36,6 +36,8 @@
/usr/lib{,32,64}/locale/** mr,
/usr/lib{,32,64}/gconv/*.so mr,
/usr/lib{,32,64}/gconv/gconv-modules* mr,
/usr/lib/@{multiarch}/gconv/*.so mr,
/usr/lib/@{multiarch}/gconv/gconv-modules mr,
# used by glibc when binding to ephemeral ports
/etc/bindresvport.blacklist r,
@@ -45,17 +47,26 @@
/etc/ld.so.cache mr,
/lib{,32,64}/ld{,32,64}-*.so mrix,
/lib{,32,64}/**/ld{,32,64}-*.so mrix,
/lib/@{multiarch}/ld{,32,64}-*.so mrix,
/lib/tls/i686/{cmov,nosegneg}/ld-*.so mrix,
/lib/i386-linux-gnu/tls/i686/{cmov,nosegneg}/ld-*.so mrix,
/opt/*-linux-uclibc/lib/ld-uClibc*so* mrix,
# we might as well allow everything to use common libraries
/lib{,32,64}/** r,
/lib{,32,64}/lib*.so* mr,
/lib{,32,64}/**/lib*.so* mr,
/lib/@{multiarch}/** r,
/lib/@{multiarch}/lib*.so* mr,
/lib/@{multiarch}/**/lib*.so* mr,
/usr/lib{,32,64}/** r,
/usr/lib{,32,64}/*.so* mr,
/usr/lib{,32,64}/**/lib*.so* mr,
/usr/lib/@{multiarch}/** r,
/usr/lib/@{multiarch}/lib*.so* mr,
/usr/lib/@{multiarch}/**/lib*.so* mr,
/lib/tls/i686/{cmov,nosegneg}/lib*.so* mr,
/lib/i386-linux-gnu/tls/i686/{cmov,nosegneg}/lib*.so* mr,
# /dev/null is pretty harmless and frequently used
/dev/null rw,

View File

@@ -12,4 +12,4 @@
# discoverable system configuration for non-local cupsd
/etc/cups/client.conf r,
# client should be able to talk the local cupsd
/var/run/cups/cups.sock w,
/{,var/}run/cups/cups.sock w,

View File

@@ -10,4 +10,4 @@
# ------------------------------------------------------------------
# System socket. Be careful when including this abstraction.
/var/run/dbus/system_bus_socket w,
/{,var/}run/dbus/system_bus_socket w,

View File

@@ -1,7 +1,7 @@
# vim:syntax=apparmor
# ------------------------------------------------------------------
#
# Copyright (C) 2010 Canonical Ltd.
# Copyright (C) 2011 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
@@ -9,5 +9,5 @@
#
# ------------------------------------------------------------------
/usr/bin/dbus-launch Pix,
/usr/bin/dbus-launch ix,
/var/lib/dbus/machine-id r,

View File

@@ -2,7 +2,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2009 Novell/SUSE
# Copyright (C) 2009-2010 Canonical Ltd.
# Copyright (C) 2009-2011 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
@@ -19,6 +19,7 @@
/etc/gnome/gtkrc* r,
/etc/gtk/* r,
/usr/lib{,32,64}/gtk/** mr,
/usr/lib/@{multiarch}/gtk/** mr,
/usr/share/themes/** r,
# for gnome 1 applications
@@ -31,6 +32,9 @@
/usr/lib{,32,64}/pango/** mr,
/usr/lib{,32,64}/gtk-*/** mr,
/usr/lib{,32,64}/gdk-pixbuf-*/** mr,
/usr/lib/@{multiarch}/pango/** mr,
/usr/lib/@{multiarch}/gtk-*/** mr,
/usr/lib/@{multiarch}/gdk-pixbuf-*/** mr,
# per-user gtk configuration
@{HOME}/.gnome/Gnome r,
@@ -60,6 +64,7 @@
/etc/gnome-vfs-2.0/modules/ r,
/etc/gnome-vfs-2.0/modules/* r,
/usr/lib/gnome-vfs-2.0/modules/*.so mr,
/usr/lib/@{multiarch}/gnome-vfs-2.0/modules/*.so mr,
# gvfs
/usr/share/gvfs/remote-volume-monitors/ r,
@@ -72,7 +77,8 @@
/usr/share/cups/charmaps/** r,
# holds MIT-MAGIC-COOKIE for gnome
owner /var/run/gdm/auth*/database r,
owner /{,var/}run/gdm/auth*/database r,
# mime-types
/etc/gnome/defaults.list r,
/usr/share/gnome/applications/mimeinfo.cache r,

View File

@@ -1,7 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2006 Novell/SUSE
# Copyright (C) 2009-2010 Canonical Ltd.
# Copyright (C) 2009-2011 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
@@ -38,10 +38,17 @@
/usr/lib*/kde3/plugins/styles/ r,
/usr/lib*/kde3/plugins/styles/* mr,
/usr/lib*/kde3/lib*so* mr,
/usr/lib/@{multiarch}/kde3/plugins/styles/ r,
/usr/lib/@{multiarch}/kde3/plugins/styles/* mr,
/usr/lib/@{multiarch}/kde3/lib*so* mr,
/usr/lib*/qt3/lib*/lib*so* mr,
/usr/lib*/qt3/plugins/** mr,
/usr/lib/@{multiarch}/qt3/lib*/lib*so* mr,
/usr/lib/@{multiarch}/qt3/plugins/** mr,
/usr/lib*/libqt-mt*so* mr,
/usr/lib*/libqui*so* mr,
/usr/lib/@{multiarch}/libqt-mt*so* mr,
/usr/lib/@{multiarch}/libqui*so* mr,
/usr/share/qt3/lib*/libqt-mt*so* mr,
/usr/share/qt3/lib*/libqui*so* mr,
@@ -49,6 +56,11 @@
/usr/lib*/kde4/plugins/*/*.so mr,
/usr/lib*/kde4/plugins/*/ r,
/usr/lib*/kde4/lib*so* mr,
/usr/lib/@{multiarch}/kde4/plugins/*/*.so mr,
/usr/lib/@{multiarch}/kde4/plugins/*/ r,
/usr/lib/@{multiarch}/kde4/lib*so* mr,
/usr/lib*/qt4/lib*/lib*so* mr,
/usr/lib*/qt4/plugins/** mr,
/usr/lib/@{multiarch}/qt4/lib*/lib*so* mr,
/usr/lib/@{multiarch}/qt4/plugins/** mr,
/usr/share/qt4/** r,

View File

@@ -1,7 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2009 Novell/SUSE
# Copyright (C) 2009 Canonical Ltd.
# Copyright (C) 2009-2011 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
@@ -12,9 +12,13 @@
# files required by kerberos client programs
/usr/lib{,32,64}/krb5/plugins/libkrb5/ r,
/usr/lib{,32,64}/krb5/plugins/libkrb5/* mr,
/usr/lib/@{multiarch}/krb5/plugins/libkrb5/ r,
/usr/lib/@{multiarch}/krb5/plugins/libkrb5/* mr,
/usr/lib{,32,64}/krb5/plugins/preauth/ r,
/usr/lib{,32,64}/krb5/plugins/preauth/* mr,
/usr/lib/@{multiarch}/krb5/plugins/preauth/ r,
/usr/lib/@{multiarch}/krb5/plugins/preauth/* mr,
/etc/krb5.keytab r,
/etc/krb5.conf r,

View File

@@ -0,0 +1,21 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2011 Novell/SUSE
#
# 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.
#
# ------------------------------------------------------------------
# files required by LDAP clients (e.g. nss_ldap/pam_ldap)
/etc/ldap.conf r,
/etc/ldap.secret r,
/etc/openldap/* r,
/etc/openldap/cacerts/* r,
# SASL plugins and config
/etc/sasl2/* r,
/usr/lib{,32,64}/sasl2/* r,
#include <abstractions/ssl_certs>

View File

@@ -10,4 +10,4 @@
# mdnsd
/etc/nss_mdns.conf r,
/var/run/mdnsd w,
/{,var/}run/mdnsd w,

View File

@@ -1,7 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2009 Novell/SUSE
# Copyright (C) 2009 Canonical Ltd.
# Copyright (C) 2009-2011 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
@@ -16,8 +16,6 @@
/etc/group r,
/etc/host.conf r,
/etc/hosts r,
/etc/ldap.conf r,
/etc/ldap.secret r,
/etc/nsswitch.conf r,
/etc/gai.conf r,
/etc/passwd r,
@@ -25,39 +23,41 @@
/etc/resolv.conf r,
# on systems using resolvconf, /etc/resolv.conf is a symlink to
# /var/run/resolvconf/resolv.conf and a file sometimes referenced in
# /{,var/}run/resolvconf/resolv.conf and a file sometimes referenced in
# /etc/resolvconf/run/resolv.conf
/var/run/resolvconf/resolv.conf r,
/{,var/}run/resolvconf/resolv.conf r,
/etc/resolvconf/run/resolv.conf r,
/etc/samba/lmhosts r,
/etc/services r,
# all openldap config
/etc/openldap/* r,
/etc/ldap/** r,
# db backend
/var/lib/misc/*.db r,
# The Name Service Cache Daemon can cache lookups, sometimes leading
# to vast speed increases when working with network-based lookups.
/var/run/.nscd_socket rw,
/var/run/nscd/socket rw,
/{,var/}run/.nscd_socket rw,
/{,var/}run/nscd/socket rw,
/var/{db,cache,run}/nscd/{passwd,group,services,host} r,
# nscd renames and unlinks files in it's operation that clients will
# have open
/var/run/nscd/db* rmix,
/{,var/}run/nscd/db* rmix,
# The nss libraries are sometimes used in addition to PAM; make sure
# they are available
/lib{,32,64}/libnss_*.so* mr,
/usr/lib{,32,64}/libnss_*.so* mr,
/lib/@{multiarch}/libnss_*.so* mr,
/usr/lib/@{multiarch}/libnss_*.so* mr,
/etc/default/nss r,
# avahi-daemon is used for mdns4 resolution
/var/run/avahi-daemon/socket w,
/{,var/}run/avahi-daemon/socket w,
# nis
#include <abstractions/nis>
# ldap
#include <abstractions/ldapclient>
# winbind
#include <abstractions/winbind>

View File

@@ -0,0 +1,13 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2011 Novell/SUSE
#
# 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.
#
# ------------------------------------------------------------------
/etc/ssl/openssl.cnf r,
/usr/share/ssl/openssl.cnf r,

View File

@@ -17,12 +17,21 @@
audit deny @{HOME}/.config/autostart/** wl,
audit deny @{HOME}/.kde/Autostart/** wl,
deny @{HOME}/.bash* mrk,
audit deny @{HOME}/.bash* wl,
deny @{HOME}/.profile* mrk,
audit deny @{HOME}/.profile* wl,
# don't allow reading/updating of run control files
deny @{HOME}/.*rc mrk,
audit deny @{HOME}/.*rc wl,
# bash
deny @{HOME}/.bash* mrk,
audit deny @{HOME}/.bash* wl,
deny @{HOME}/.inputrc mrk,
audit deny @{HOME}/.inputrc wl,
# sh/dash/csh/tcsh/pdksh/zsh
deny @{HOME}/.{,z}profile* mrk,
audit deny @{HOME}/.{,z}profile* wl,
deny @{HOME}/.{,z}log{in,out} mrk,
audit deny @{HOME}/.{,z}log{in,out} wl,
deny @{HOME}/.zshenv mrk,
audit deny @{HOME}/.zshenv wl,

View File

@@ -29,3 +29,6 @@
# wx paths
/usr/lib/wx/python/*.pth r,
# python build configuration and headers
/usr/include/python{2,3}.[0-7]*/pyconfig.h

View File

@@ -9,11 +9,11 @@
#
# ------------------------------------------------------------------
/etc/samba/smb.conf r,
/etc/samba/* r,
/usr/share/samba/*.dat r,
/var/lib/samba/**.tdb rwk,
/var/log/samba/cores/ rw,
/var/log/samba/cores/* w,
/var/log/samba/cores/** rw,
/var/log/samba/log.* w,
/var/run/samba/*.tdb rw,
/{,var/}run/samba/*.tdb rw,

View File

@@ -1,7 +1,7 @@
# ------------------------------------------------------------------
#
# Copyright (C) 2002-2005 Novell/SUSE
# Copyright (C) 2010 Canonical Ltd.
# Copyright (C) 2010-2011 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
@@ -14,3 +14,6 @@
/etc/ssl/certs/* r,
/usr/share/ca-certificates/ r,
/usr/share/ca-certificates/** r,
/usr/share/ssl/certs/ca-bundle.crt r,
/usr/local/share/ca-certificates/ r,
/usr/local/share/ca-certificates/** r,

View File

@@ -8,6 +8,10 @@
# Image viewers
/usr/bin/eog PUxr,
/usr/bin/gimp* PUxr,
/usr/bin/shotwell PUxr,
/usr/bin/digikam PUxr,
/usr/bin/f-spot PUxr,
/usr/bin/gwenview PUxr,
#include <abstractions/ubuntu-media-players>
owner @{HOME}/.macromedia/** rw,

View File

@@ -11,6 +11,7 @@
#include <abstractions/private-files>
audit deny @{HOME}/.ssh/** mrwkl,
audit deny @{HOME}/.gnome2_private/** mrwkl,
audit deny @{HOME}/.kde/share/apps/kwallet/** mrwkl,
# Comment this out if using gpg plugin/addons
audit deny @{HOME}/.gnupg/** mrwkl,

View File

@@ -16,5 +16,5 @@
/usr/bin/tkrat PUx,
/usr/lib/thunderbird/thunderbird PUx,
/usr/lib/thunderbird-3*/thunderbird{,.sh} PUx,
/usr/lib/thunderbird-[1-9]*/thunderbird{,.sh} PUx,

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