2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-31 14:25:52 +00:00

Compare commits

..

34 Commits

Author SHA1 Message Date
John Johansen
022a988e4e 2.7.1 Release 2012-01-30 07:30:25 -08:00
Christian Boltz
95f9b1d07c Update abstractios for KDE4
(At least) openSUSE uses ~/.kde4 to store KDE4 settings.
This patch changes ~/.kde/ to ~/.kde{,4} in all abstractions.

The patch is mostly from Velery Valery, I only fixed a merge conflict 
and added the kmail{,2} part in private-files-strict.

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

Acked-By: Steve Beattie <sbeattie@ubuntu.com> for both trunk and 2.7.
2012-01-19 15:21:56 +01:00
Jamie Strandboge
4258749515 update p11-kit to allow mmap of libraries in pkcs directories
Acked-by: Jamie Strandboge <jamie@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-01-18 16:23:12 -06:00
Steve Beattie
62b2a00331 Merge from trunk rev 1930: A bug in Ubuntu reported that the aspell
abstraction does not allow write access to the user customizable
dictionaries, the personal dictionary (~/.aspell.$LANG.pws) and the
personal replacement dictionary (~/.aspell.$LANG.prepl). It also
adjusts the abstraction to add the owner modifier to the personal
dictionaries.

Bug: https://bugs.launchpad.net/bugs/917859

Signed-off-by: Steve Beattie <sbeattie@ubuntu.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2012-01-18 11:06:21 -08:00
Steve Beattie
463415347d Merge from trunk rev 1908: utils/Immunix/AppArmor.pm: fixes the profile
autogeneration code to include read access to the script itself for
interpreted scripts.

Nominated-By: Steve Beattie <sbeattie@ubuntu.com>
Acked-by: Christian Boltz <apparmor@cboltz.de> for the 2.7 branch
2012-01-12 10:39:48 +01:00
Steve Beattie
a52313485f Merge from trunk rev 1907: utils/Immunix/AppArmor.pm: update the
initial profile generation for python and ruby scripts to include
the respective abstractions.

Nominated-By: Steve Beattie <sbeattie@ubuntu.com>
Acked-by: Christian Boltz <apparmor@cboltz.de>
2012-01-12 10:38:41 +01:00
Christian Boltz
67b440a019 when using smbldap-useradd using this smb.conf entry
add machine script = /usr/sbin/smbldap-useradd -t 5 -w "%u"
smbd obviously needs x permissions for smbldap-useradd.

The commit also adds a new profile for usr.sbin.smbldap-useradd (based on 
the audit.log from Alexis Pellicier).

Additionally, I moved the "/etc/samba/* rwk" rule next to the other 
/etc-related rules in the smbd profile.

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

Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-01-10 19:08:48 +01:00
Steve Beattie
da1bb2f219 Trunk revision 1910: Fix from Felix Geyer: block write access to
~/.kde/env because KDE automatically sources scripts in that folder
on startup.

Bug: https://bugs.launchpad.net/bugs/914190

Nominated-By: Steve Beattie <sbeattie@ubuntu.com>
Acked-by: John Johansen <john.johansen@canonical.com>
2012-01-10 12:59:54 +01:00
Steve Beattie
0badfb7816 Trunk revision 1909: Fix from Felix Geyer: in the enchant abstraction,
allow the creation of enchant .config directory.

Bug: https://bugs.launchpad.net/bugs/914184

Nominated-By: Steve Beattie <sbeattie@ubuntu.com>
Acked-By: John Johansen <john.johansen@canonical.com>
2012-01-10 12:58:33 +01:00
Christian Boltz
87bf30b6d0 read-only TFTP access for dnsmasq
This is a backport of r1870 to the 2.7 branch.

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


Original commit message for trunk r1870:
  Merge from Simon Deziel for TFTP read-only access for dnsmasq. Fixes
  LP: #905412

  Acked-by: Jamie Strandboge <jamie@canonical.com>
2012-01-09 21:08:19 +01:00
Christian Boltz
51369a0c3e according to Peter Czanik, the openSUSE syslog-ng maintainer, syslog-ng
needs capability dac_read_search.

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

ACKed-by: Steve Beattie <steve@nxnw.org>
2012-01-09 13:29:43 +01:00
Jamie Strandboge
6ae5a71ea2 merge from trunk:
add p11-kit abstraction (LP: #912754, LP: #912752)
  
  From the README in the toplevel source:
  "[P11-KIT] Provides a way to load and enumerate PKCS#11 modules. Provides a
  standard configuration setup for installing PKCS#11 modules in such a way that
  they're discoverable."
  
  File locatations are described in [1]. There is a global configuration file in
  /etc/pkcs11/pkcs11.conf. Per module configuration happens in
  /etc/pkcs11/<module name>. There is also user configuration in ~/.pkcs11, but
  IMO this should not be allowed in the abstraction. Example configuration can b
e
  seen in the upstream documentation[2].
  
  This will likely need to be refined as more applications use p11-kit.
  
  [1]http://p11-glue.freedesktop.org/doc/p11-kit/config-locations.html
  [2]http://p11-glue.freedesktop.org/doc/p11-kit/config-example.html
  
  Acked-by: Jamie Strandboge <jamie@canonical.com>
  Acked-By: Steve Beattie <sbeattie@ubuntu.com>
  
  
  Also add p11-kit to authentication abstraction
  
  Acked-by: Jamie Strandboge <jamie@canonical.com>
  Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-01-09 10:51:06 +01:00
Jamie Strandboge
850a565dce merge from trunk:
add audacity to the ubuntu-media-players abstraction (LP: #899963)
2012-01-09 10:49:07 +01:00
Jamie Strandboge
a0cf904972 merge from trunk:
allow software-center in the ubuntu-integration abstraction for
  apt: URLs (LP: #662906)
2012-01-09 10:48:36 +01:00
Jamie Strandboge
8760451216 merge from trunk:
allow fireclam plugin to work in Ubuntu multimedia abstraction
  (LP: #562831)
2012-01-09 10:48:11 +01:00
Jamie Strandboge
d096f8f7a5 merge trunk:
Author: James Troup
  Description: fix typo when adding multiarch lines for gconv
  Bug-Ubuntu: https://launchpad.net/bugs/904548
  
  Acked-by: Jamie Strandboge <jamie@canonical.com>
2012-01-09 10:47:42 +01:00
Jamie Strandboge
beb695f7b0 merge from trunk:
Author: Felix Geyer
  Description: allow avahi to do dbus introspection
  Bug-Ubuntu: https://launchpad.net/bugs/769148
  
  Acked-by: Jamie Strandboge <jamie@canonical.com>
2012-01-09 10:47:12 +01:00
Jamie Strandboge
ac80b7ca03 merge from trunk:
Author: Felix Geyer
  Description: abstractions/fonts should allow access to ~/.fonts.conf.d
  Bug-Ubuntu: https://launchpad.net/bugs/870992
  
  Acked-by: Jamie Strandboge <jamie@canonical.com>
2012-01-09 10:46:27 +01:00
Jamie Strandboge
a729e8fd75 merge from trunk:
Nvidia users need access to /dev/nvidia* files for various plugins
  to work right. Since these are all focused around multimedia, add the
  accesses to ubuntu-browsers.d/multimedia
2012-01-09 10:45:40 +01:00
Jamie Strandboge
f4c661e070 merge from trunk:
allow access to Thunar as well as thunar in ubuntu-integration abstraction
  (LP: #890894)
2012-01-09 10:44:45 +01:00
Jamie Strandboge
15e636a329 merge from trunk:
allow ixr access to exo-open in Ubuntu integration abstraction
  (LP: #890894)
2012-01-09 10:44:11 +01:00
Jamie Strandboge
49b9a83d9e Merge from trunk:
update binaries for for transmission in ubuntu-bittorrent-clients
  (LP: #852062)
2012-01-09 10:43:34 +01:00
Jamie Strandboge
069d98d007 merge from trunk:
add kate to Ubuntu text editors browser abstraction
  fix for LP: #884748
2012-01-09 10:42:54 +01:00
Jamie Strandboge
8c82eec301 Cherrypick from trunk:
Description: allow read of @{HOME}/.cups/client.conf and
   @{HOME}/.cups/lpoptions
  Bug-Ubuntu: https://launchpad.net/bugs/887992

  Added owner match per Steve Beattie and lpoptions per Steve and Christian Bolt
z
2012-01-06 10:53:14 -06:00
Jamie Strandboge
455d8a5140 Cherrypick from trunk:
Description: allow read access of /etc/python{2,3}.[0-7]*/sitecustomize.py
   in python abstraction. This script is used by apport aware python application
s
  Bug-Ubuntu: https://launchpad.net/bugs/860856

  Acked-by: Jamie Strandboge <jamie@canonical.com>
  Acked-by: Kees Cook <kees@ubuntu.com>
2012-01-06 10:52:34 -06:00
Jamie Strandboge
efd20f879c Cherrypick from trunk:
Description: update dovecot deliver profile to access various .conf files for
   dovecot
  Bug-Ubuntu: https://launchpad.net/bugs/458922

  Acked-by: Jamie Strandboge <jamie@canonical.com>
  Acked-by: Kees Cook <kees@ubuntu.com>
2012-01-06 10:51:58 -06:00
Jamie Strandboge
977929f558 Cherrypick from trunk:
Description: updates for usr.bin.sshd example profile to work with zsh4, dash
   and systems where /var/run moved to /run. Also allows read of
   /etc/default/locale.
  Bug-Ubuntu: https://launchpad.net/bugs/817956

  Acked-by: Jamie Strandboge <jamie@canonical.com>
  Acked-by: Kees Cook <kees@ubuntu.com>
2012-01-06 10:51:09 -06:00
Jamie Strandboge
cb60e9b3df Cherrypick from upstream:
Description: Disallow writing and linking to @{HOME}/.pki/nssdb/ .so files
  Bug-Ubuntu: https://launchpad.net/bugs/911847

  Acked-by: Jamie Strandboge <jamie@canonical.com>
  Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-01-06 10:50:19 -06:00
Christian Boltz
f57d90d935 lesson of the day: it's not a good idea to edit a file while the
"bzr commit" editor is open because bzr caches the modified file 
and doesn't include last-minute changes in the commit :-/

In other words: the rule for /.htaccess didn't contain the audit
keyword in my last commit.
2012-01-05 23:53:51 +01:00
Christian Boltz
f66a2e2e66 according to Lars Müller (a samba developer) smbd needs access to some
more files in /usr/lib*/samba/ in some cases.

References: https://bugzilla.novell.com/show_bug.cgi?id=725967#c5


Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-01-05 23:50:13 +01:00
Christian Boltz
d1281c4988 Split off various permissions from the httpd2-prefork profile to
abstractions/apache2-common. Additionally, add read permissions
for /**/.htaccess and /dev/urandom to apache2-common.

The patch is based on a profile abstraction from darix. I made some 
things more strict (compared to darix' profile), and OTOH added some 
things that are needed on my servers.

*** BACKWARDS-INCOMPATIBLE CHANGES ***

^HANDLING_UNTRUSTED_INPUT
- don't allow *.htaccess files (the old /**.htaccess rule was too generous)


Note: this is slightly different to trunk r1895 regarding /.htaccess:
/.htaccess is still allowed in the 2.7 branch, but logged ("audit") and 
has a comment saying that it will be disallowed in future versions.
2012-01-05 23:38:48 +01:00
Christian Boltz
c93fc7c758 Create /etc/apparmor.d/tunables/multiarch.d directory in profiles/Makefile
(otherwise it's created as a file, which is wrong)

Acked-by: John Johansen <john.johansen@canonical.com>
2012-01-03 23:45:00 +01:00
Christian Boltz
e2c5ecafce Fix a syntax error in abstractions/python introduced in r1854.
According to https://launchpad.net/bugs/840734 pyconfig.h should have r 
permissions.

Acked-by: John Johansen <john.johansen@canonical.com>
2012-01-03 21:23:30 +01:00
Christian Boltz
888ef7b0e2 smbd needs read access to /etc/netgroup.
References: https://bugzilla.novell.com/show_bug.cgi?id=738041

Acked-by: John Johansen <john.johansen@canonical.com>
2011-12-30 21:55:58 +01:00
137 changed files with 1148 additions and 4229 deletions

View File

@@ -1 +1 @@
2.7.99
2.7.1

View File

@@ -16,5 +16,4 @@ BUILT_SOURCES = $(man_MANS)
--section=2 \
--release="AppArmor $(VERSION)" \
--center="AppArmor" \
--stderr \
$< > $@

View File

@@ -85,9 +85,14 @@ Insufficient kernel memory was available.
The calling application is not confined by apparmor.
=item B<ECHILD>
The application's profile has no hats defined for it.
=item B<EACCES>
The task does not have sufficient permissions to change its domain.
The specified I<profile> does not exist in this profile or the
process tried to change another process's domain.
=back
@@ -170,7 +175,6 @@ The output when run:
If /tmp/change_p is to be confined as well, then the following profile can be
used (in addition to the one for 'i_cant_be_trusted_anymore', above):
# Confine change_p to be able to read /etc/passwd and aa_change_profile()
# to the 'i_cant_be_trusted_anymore' profile.
/tmp/change_p {

View File

@@ -38,9 +38,8 @@ 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(3) to reflect the reason it is not
enabled and returns 0.
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
@@ -58,10 +57,10 @@ appropriately.
=head1 ERRORS
B<aa_is_enabled>
=over 4
B<aa_is_enabled>
=item B<ENOSYS>
AppArmor extensions to the system are not available.
@@ -85,21 +84,18 @@ 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.
+Did not have sufficient permissions to determine if AppArmor is enabled.
=back
B<aa_find_mountpoint>
=over 4
=item B<ENOMEM>
Insufficient memory was available.
=item B<EACCES>
Access to the required paths was denied.
Access to the the required paths was denied.
=item B<ENOENT>

View File

@@ -50,7 +50,7 @@ Link with B<-lapparmor> when compiling.
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().
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.

View File

@@ -76,8 +76,8 @@ EXTRA_CFLAGS+=-DSUBDOMAIN_CONFDIR=\"${CONFDIR}\"
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 mount.c
HDRS = parser.h parser_include.h immunix.h mount.h
parser_alias.c
HDRS = parser.h parser_include.h immunix.h
TOOLS = apparmor_parser
OBJECTS = $(SRCS:.c=.o)
@@ -200,9 +200,6 @@ parser_alias.o: parser_alias.c parser.h
parser_common.o: parser_common.c parser.h
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
mount.o: mount.c mount.h parser.h immunix.h
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
parser_version.h: Makefile
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
@mv -f .ver $@

View File

@@ -3,7 +3,7 @@
# 2008, 2009
# NOVELL (All rights reserved)
#
# Copyright (c) 2010 - 2012
# Copyright (c) 2010
# Canonical Ltd. (All rights reserved)
#
# This program is free software; you can redistribute it and/or

View File

@@ -3,7 +3,7 @@
# 2008, 2009
# NOVELL (All rights reserved)
#
# Copyright (c) 2010 - 2012
# Copyright (c) 2010
# Canonical Ltd. (All rights reserved)
#
# This program is free software; you can redistribute it and/or

View File

@@ -3,7 +3,7 @@
# 2008, 2009
# NOVELL (All rights reserved)
#
# Copyright (c) 2010 - 2012
# Copyright (c) 2010
# Canonical Ltd. (All rights reserved)
#
# This program is free software; you can redistribute it and/or
@@ -127,21 +127,16 @@ Perform no caching at all: disables -W, implies -T.
=item -T, --skip-read-cache
By default, if a profile's cache is found in the location specified by
--cache-loc and the timestamp is newer than the profile, it will be loaded
from the cache. This option disables this cache loading behavior.
By default, if a profile's cache is found in /etc/apparmor.d/cache/ and
the timestamp is newer than the profile, it will be loaded from the cache.
This option disables this cache loading behavior.
=item -W, --write-cache
Write out cached profiles to the location specified in --cache-loc. Off
by default. In cases where abstractions have been changed, and the parser
is running with "--replace", it may make sense to also use
"--skip-read-cache" with the "--write-cache" option.
=item -L, --cache-loc
Set the location of the cache directory. If not specified the cache location
defaults to /etc/apparmor.d/cache
Write out cached profiles to /etc/apparmor.d/cache/. Off by default.
In cases where abstractions have been changed, and the parser is running
with "--replace", it may make sense to also use "--skip-read-cache" with
the "--write-cache" option.
=item -Q, --skip-kernel-load

View File

@@ -96,9 +96,6 @@
#define ALL_AA_EXEC_TYPE (AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE)
#define ALL_USER_EXEC (AA_USER_EXEC | AA_USER_EXEC_TYPE)
#define ALL_OTHER_EXEC (AA_OTHER_EXEC | AA_OTHER_EXEC_TYPE)
#define AA_LINK_BITS ((AA_MAY_LINK << AA_USER_SHIFT) | \
(AA_MAY_LINK << AA_OTHER_SHIFT))

View File

@@ -4,7 +4,7 @@
TARGET=libapparmor_re.a
CFLAGS ?= -g -Wall -O2 ${EXTRA_CFLAGS}
CXXFLAGS := ${CFLAGS} -std=c++0x
CXXFLAGS := ${CFLAGS}
ARFLAGS=-rcs
@@ -12,16 +12,16 @@ BISON := bison
all : ${TARGET}
libapparmor_re.a: parse.o expr-tree.o hfa.o chfa.o aare_rules.o
libapparmor_re.a: parse.o expr-tree.o hfa.o compressed_hfa.o aare_rules.o
ar ${ARFLAGS} $@ $^
expr-tree.o: expr-tree.cc expr-tree.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 chfa.h parse.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
chfa.o: chfa.cc chfa.h ../immunix.h
compressed_hfa.o: compressed_hfa.cc compressed_hfa.h ../immunix.h
parse.o : parse.cc apparmor_re.h expr-tree.h

View File

@@ -1,7 +1,7 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2012 Canonical Ltd.
* 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
@@ -31,7 +31,7 @@
#include "expr-tree.h"
#include "parse.h"
#include "hfa.h"
#include "chfa.h"
#include "compressed_hfa.h"
#include "../immunix.h"
struct aare_ruleset {
@@ -98,7 +98,6 @@ extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny,
{
Node *tree = NULL, *accept;
int exact_match;
uint32_t allow = perms;
assert(perms != 0);
@@ -221,11 +220,7 @@ extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny,
}
cerr << " -> ";
tree->dump(cerr);
if (deny)
cerr << " deny";
cerr << " (0x" << hex << allow <<"/" << audit << dec << ")";
accept->dump(cerr);
cerr << "\n\n";
cerr << "\n\n";
}
if (rules->root)
@@ -275,23 +270,6 @@ extern "C" void *aare_create_dfa(aare_ruleset_t *rules, size_t *size,
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
dfa.dump_uniq_perms("minimized dfa");
}
if (flags & DFA_CONTROL_FILTER_DENY &&
flags & DFA_CONTROL_MINIMIZE &&
dfa.apply_and_clear_deny()) {
/* Do a second minimization pass as removal of deny
* information has moved some states from accepting
* to none accepting partitions
*
* TODO: add this as a tail pass to minimization
* so we don't need to do a full second pass
*/
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);
@@ -313,10 +291,10 @@ extern "C" void *aare_create_dfa(aare_ruleset_t *rules, size_t *size,
} else if (flags & DFA_DUMP_EQUIV)
cerr << "\nDFA did not generate an equivalence class\n";
CHFA chfa(dfa, eq, flags);
TransitionTable transition_table(dfa, eq, flags);
if (flags & DFA_DUMP_TRANS_TABLE)
chfa.dump(cerr);
chfa.flex_table(stream, "");
transition_table.dump(cerr);
transition_table.flex_table(stream, "");
}
catch(int error) {
*size = 0;

View File

@@ -1,7 +1,7 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2012 Canonical Ltd.
* 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

View File

@@ -1,7 +1,7 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2012 Canonical Ltd.
* 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
@@ -26,7 +26,7 @@ typedef enum dfaflags {
DFA_CONTROL_TREE_LEFT = 1 << 3,
DFA_CONTROL_MINIMIZE = 1 << 4,
DFA_CONTROL_MINIMIZE_HASH_TRANS = 1 << 5,
DFA_CONTROL_FILTER_DENY = 1 << 6,
DFA_CONTROL_MINIMIZE_HASH_PERMS = 1 << 6,
DFA_CONTROL_REMOVE_UNREACHABLE = 1 << 7,
DFA_CONTROL_TRANS_HIGH = 1 << 8,

View File

@@ -1,7 +1,7 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2012 Canonical Ltd.
* 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
@@ -30,10 +30,9 @@
#include <string.h>
#include "hfa.h"
#include "chfa.h"
#include "../immunix.h"
#include "compressed_hfa.h"
void CHFA::init_free_list(vector<pair<size_t, size_t> > &free_list,
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++) {
@@ -48,10 +47,11 @@ void CHFA::init_free_list(vector<pair<size_t, size_t> > &free_list,
/**
* new Construct the transition table.
*/
CHFA::CHFA(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags): eq(eq)
TransitionTable::TransitionTable(DFA &dfa, map<uchar, uchar> &eq,
dfaflags_t flags): eq(eq)
{
if (flags & DFA_DUMP_TRANS_PROGRESS)
fprintf(stderr, "Compressing HFA:\r");
fprintf(stderr, "Compressing trans table:\r");
if (eq.empty())
max_eq = 255;
@@ -74,14 +74,14 @@ CHFA::CHFA(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags): eq(eq)
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
if (*i == dfa.start || *i == dfa.nonmatching)
continue;
optimal += (*i)->trans.size();
optimal += (*i)->cases.cases.size();
if (flags & DFA_CONTROL_TRANS_HIGH) {
size_t range = 0;
if ((*i)->trans.size())
if ((*i)->cases.cases.size())
range =
(*i)->trans.rbegin()->first -
(*i)->trans.begin()->first;
size_t ord = ((256 - (*i)->trans.size()) << 8) | (256 - 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));
}
@@ -113,8 +113,8 @@ CHFA::CHFA(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags): eq(eq)
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)->perms.allow;
accept2[num.size()] = PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny);
accept[num.size()] = (*i)->accept;
accept2[num.size()] = (*i)->audit;
num.insert(make_pair(*i, num.size()));
}
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
@@ -130,8 +130,8 @@ CHFA::CHFA(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags): eq(eq)
if (i->second != dfa.nonmatching &&
i->second != dfa.start) {
insert_state(free_list, i->second, dfa);
accept[num.size()] = i->second->perms.allow;
accept2[num.size()] = PACK_AUDIT_CTL(i->second->perms.audit, i->second->perms.quiet & i->second->perms.deny);
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)) {
@@ -154,14 +154,14 @@ CHFA::CHFA(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags): eq(eq)
}
/**
* Does <trans> fit into position <base> of the transition table?
* Does <cases> fit into position <base> of the transition table?
*/
bool CHFA::fits_in(vector<pair<size_t, size_t> > &free_list
bool TransitionTable::fits_in(vector<pair<size_t, size_t> > &free_list
__attribute__ ((unused)), size_t pos,
StateTrans &trans)
Cases &cases)
{
size_t c, base = pos - trans.begin()->first;
for (StateTrans::iterator i = trans.begin(); i != trans.end(); i++) {
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 */
@@ -177,21 +177,21 @@ bool CHFA::fits_in(vector<pair<size_t, size_t> > &free_list
/**
* Insert <state> of <dfa> into the transition table.
*/
void CHFA::insert_state(vector<pair<size_t, size_t> > &free_list,
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;
StateTrans &trans = from->trans;
size_t c = trans.begin()->first;
Cases &cases = from->cases;
size_t c = cases.begin()->first;
size_t prev = 0;
size_t x = first_free;
if (from->otherwise)
default_state = from->otherwise;
if (trans.empty())
if (cases.otherwise)
default_state = cases.otherwise;
if (cases.cases.empty())
goto do_insert;
repeat:
@@ -203,16 +203,16 @@ repeat:
}
/* try inserting until we succeed. */
while (x && !fits_in(free_list, x, trans)) {
while (x && !fits_in(free_list, x, cases)) {
prev = x;
x = free_list[x].second;
}
if (!x) {
resize = 256 - trans.begin()->first;
resize = 256 - cases.begin()->first;
x = free_list.size();
/* set prev to last free */
} else if (x + 255 - trans.begin()->first >= next_check.size()) {
resize = (255 - trans.begin()->first - (next_check.size() - 1 - x));
} 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;
}
@@ -229,7 +229,7 @@ repeat:
}
base = x - c;
for (StateTrans::iterator j = trans.begin(); j != trans.end(); j++) {
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;
@@ -248,7 +248,7 @@ do_insert:
/**
* Text-dump the transition table (for debugging).
*/
void CHFA::dump(ostream &os)
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++) {
@@ -342,7 +342,7 @@ template<class Iter>
os << fill64(sizeof(td) + sizeof(*pos) * size);
}
void CHFA::flex_table(ostream &os, const char *name)
void TransitionTable::flex_table(ostream &os, const char *name)
{
const char th_version[] = "notflex";
struct table_set_header th = { 0, 0, 0, 0 };

View File

@@ -1,7 +1,7 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2012 Canonical Ltd.
* 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
@@ -16,10 +16,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Create a compressed hfa (chfa) from and hfa
* Create a compressed hfa from and hfa
*/
#ifndef __LIBAA_RE_CHFA_H
#define __LIBAA_RE_CHFA_H
#ifndef __LIBAA_RE_COMPRESSED_HFA_H
#define __LIBAA_RE_COMPRESSED_HFA_H
#include <map>
#include <vector>
@@ -28,17 +28,17 @@
using namespace std;
class CHFA {
class TransitionTable {
typedef vector<pair<const State *, size_t> > DefaultBase;
typedef vector<pair<const State *, const State *> > NextCheck;
public:
CHFA(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags);
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,
StateTrans &cases);
Cases &cases);
void insert_state(vector<pair<size_t, size_t> > &free_list,
State *state, DFA &dfa);
@@ -53,4 +53,4 @@ class CHFA {
size_t first_free;
};
#endif /* __LIBAA_RE_CHFA_H */
#endif /* __LIBAA_RE_COMPRESSED_HFA_H */

View File

@@ -1,7 +1,7 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2012 Canonical Ltd.
* 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
@@ -187,22 +187,14 @@ void normalize_tree(Node *t, int dir)
return;
for (;;) {
if (dynamic_cast<TwoChildNode *>(t) &&
(&epsnode == t->child[dir]) &&
(&epsnode != t->child[!dir])) {
if ((&epsnode == t->child[dir]) &&
(&epsnode != t->child[!dir]) &&
dynamic_cast<TwoChildNode *>(t)) {
// (E | a) -> (a | E)
// Ea -> aE
// Test for E | (E | E) and E . (E . E) which will
// result in an infinite loop
Node *c = t->child[!dir];
if (dynamic_cast<TwoChildNode *>(c) &&
&epsnode == c->child[dir] &&
&epsnode == c->child[!dir]) {
c->release();
c = &epsnode;
}
t->child[dir] = c;
t->child[!dir] = &epsnode;
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) &&

View File

@@ -1,7 +1,7 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2012 Canonical Ltd.
* 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
@@ -81,15 +81,17 @@ ostream &operator<<(ostream &os, const NodeSet &state);
* (i.e., following an AnyCharNode or NotCharSetNode). This avoids
* enumerating all the explicit tranitions for default matches.
*/
typedef struct Cases {
typedef struct NodeCases {
typedef map<uchar, NodeSet *>::iterator iterator;
iterator begin() { return cases.begin(); }
iterator end() { return cases.end(); }
Cases(): otherwise(0) { }
NodeCases(): otherwise(0) { }
map<uchar, NodeSet *> cases;
NodeSet *otherwise;
} Cases;
}
NodeCases;
ostream &operator<<(ostream &os, Node &node);
@@ -203,8 +205,7 @@ public:
ImportantNode(): LeafNode() { }
void compute_firstpos() { firstpos.insert(this); }
void compute_lastpos() { lastpos.insert(this); }
virtual void follow(Cases &cases) = 0;
virtual int is_accept(void) = 0;
virtual void follow(NodeCases &cases) = 0;
};
/* common base class for all the different classes that contain
@@ -213,14 +214,13 @@ public:
class CNode: public ImportantNode {
public:
CNode(): ImportantNode() { }
int is_accept(void) { return false; }
};
/* Match one specific character (/c/). */
class CharNode: public CNode {
public:
CharNode(uchar c): c(c) { }
void follow(Cases &cases)
void follow(NodeCases &cases)
{
NodeSet **x = &cases.cases[c];
if (!*x) {
@@ -251,7 +251,7 @@ public:
class CharSetNode: public CNode {
public:
CharSetNode(Chars &chars): chars(chars) { }
void follow(Cases &cases)
void follow(NodeCases &cases)
{
for (Chars::iterator i = chars.begin(); i != chars.end(); i++) {
NodeSet **x = &cases.cases[*i];
@@ -292,7 +292,7 @@ public:
class NotCharSetNode: public CNode {
public:
NotCharSetNode(Chars &chars): chars(chars) { }
void follow(Cases &cases)
void follow(NodeCases & cases)
{
if (!cases.otherwise)
cases.otherwise = new NodeSet;
@@ -305,7 +305,7 @@ public:
* the old otherwise state for the matching characters.
*/
cases.otherwise->insert(followpos.begin(), followpos.end());
for (Cases::iterator i = cases.begin(); i != cases.end();
for (NodeCases::iterator i = cases.begin(); i != cases.end();
i++) {
if (chars.find(i->first) == chars.end())
i->second->insert(followpos.begin(),
@@ -340,12 +340,12 @@ public:
class AnyCharNode: public CNode {
public:
AnyCharNode() { }
void follow(Cases &cases)
void follow(NodeCases &cases)
{
if (!cases.otherwise)
cases.otherwise = new NodeSet;
cases.otherwise->insert(followpos.begin(), followpos.end());
for (Cases::iterator i = cases.begin(); i != cases.end();
for (NodeCases::iterator i = cases.begin(); i != cases.end();
i++)
i->second->insert(followpos.begin(), followpos.end());
}
@@ -365,7 +365,6 @@ public:
class AcceptNode: public ImportantNode {
public:
AcceptNode() { }
int is_accept(void) { return true; }
void release(void)
{
/* don't delete AcceptNode via release as they are shared, and
@@ -373,7 +372,7 @@ public:
*/
}
void follow(Cases &cases __attribute__ ((unused)))
void follow(NodeCases &cases __attribute__ ((unused)))
{
/* Nothing to follow. */
}
@@ -574,6 +573,22 @@ 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:

View File

@@ -1,7 +1,7 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2012 Canonical Ltd.
* 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
@@ -30,40 +30,11 @@
#include <ostream>
#include <iostream>
#include <fstream>
#include <string.h>
#include "expr-tree.h"
#include "hfa.h"
#include "../immunix.h"
ostream &operator<<(ostream &os, const CacheStats &cache)
{
/* dump the state label */
os << "cache: size=";
os << cache.size();
os << " dups=";
os << cache.dup;
os << " longest=";
os << cache.max;
if (cache.size()) {
os << " avg=";
os << cache.sum / cache.size();
}
return os;
}
ostream &operator<<(ostream &os, const ProtoState &proto)
{
/* dump the state label */
os << '{';
os << proto.nnodes;
os << ',';
os << proto.anodes;
os << '}';
return os;
}
ostream &operator<<(ostream &os, const State &state)
{
/* dump the state label */
@@ -73,48 +44,46 @@ ostream &operator<<(ostream &os, const State &state)
return os;
}
static void split_node_types(NodeSet *nodes, NodeSet **anodes, NodeSet **nnodes
)
State *DFA::add_new_state(NodeMap &nodemap,
pair<unsigned long, NodeSet *> index,
NodeSet *nodes, dfa_stats_t &stats)
{
*anodes = *nnodes = NULL;
for (NodeSet::iterator i = nodes->begin(); i != nodes->end(); ) {
if ((*i)->is_accept()) {
if (!*anodes)
*anodes = new NodeSet;
(*anodes)->insert(*i);
NodeSet::iterator k = i++;
nodes->erase(k);
} else
i++;
}
*nnodes = nodes;
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::add_new_state(NodeSet *nodes, State *other)
State *DFA::find_target_state(NodeMap &nodemap, list<State *> &work_queue,
NodeSet *nodes, dfa_stats_t &stats)
{
/* The splitting of nodes should probably get pushed down into
* follow(), ie. put in separate lists from the start
*/
NodeSet *anodes, *nnodes;
hashedNodeVec *nnodev;
split_node_types(nodes, &anodes, &nnodes);
nnodev = nnodes_cache.insert(nnodes);
anodes = anodes_cache.insert(anodes);
State *target;
ProtoState proto(nnodev, anodes);
State *state = new State(node_map.size(), proto, other);
pair<NodeMap::iterator,bool> x = node_map.insert(proto, state);
if (x.second == false) {
delete state;
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 {
states.push_back(state);
work_queue.push_back(state);
/* set of nodes already has a mapping so free this one */
stats.duplicates++;
delete(nodes);
target = x->second;
}
return x.first->second;
return target;
}
void DFA::update_state_transitions(State *state)
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
@@ -122,12 +91,9 @@ void DFA::update_state_transitions(State *state)
*
* The resultant transition set is a mapping of characters to
* sets of nodes.
*
* Note: the follow set for accept nodes is always empty so we don't
* need to compute follow for the accept nodes in a protostate
*/
Cases cases;
for (hashedNodeVec::iterator i = state->proto.nnodes->begin(); i != state->proto.nnodes->end(); i++)
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
@@ -137,22 +103,22 @@ void DFA::update_state_transitions(State *state)
/* check the default transition first */
if (cases.otherwise)
state->otherwise = add_new_state(cases.otherwise, nonmatching);
else
state->otherwise = nonmatching;
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 (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
for (NodeCases::iterator j = cases.begin(); j != cases.end(); j++) {
State *target;
target = add_new_state(j->second, nonmatching);
target = find_target_state(nodemap, work_queue, j->second, stats);
/* Don't insert transition that the otherwise transition
/* Don't insert transition that the default transition
* already covers
*/
if (target != state->otherwise)
state->trans[j->first] = target;
if (target != state->cases.otherwise)
state->cases.cases[j->first] = target;
}
}
@@ -165,7 +131,7 @@ void DFA::dump_node_to_dfa(void)
" State <= Nodes\n"
"-------------------\n";
for (Partition::iterator i = states.begin(); i != states.end(); i++)
cerr << " " << (*i)->label << " <= " << (*i)->proto << "\n";
cerr << " " << (*i)->label << " <= " << *(*i)->nodes << "\n";
}
/**
@@ -173,6 +139,7 @@ void DFA::dump_node_to_dfa(void)
*/
DFA::DFA(Node *root, dfaflags_t flags): root(root)
{
dfa_stats_t stats = { 0, 0, 0 };
int i = 0;
if (flags & DFA_DUMP_PROGRESS)
@@ -190,8 +157,15 @@ DFA::DFA(Node *root, dfaflags_t flags): root(root)
(*i)->compute_followpos();
}
nonmatching = add_new_state(new NodeSet, NULL);
start = add_new_state(new NodeSet(root->firstpos), nonmatching);
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
@@ -203,18 +177,14 @@ DFA::DFA(Node *root, dfaflags_t flags): root(root)
* 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)) {
cerr << "\033[2KCreating dfa: queue "
<< work_queue.size()
<< "\tstates "
<< states.size()
<< "\teliminated duplicates "
<< node_map.dup
<< "\r";
}
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();
@@ -223,7 +193,7 @@ DFA::DFA(Node *root, dfaflags_t flags): root(root)
/* Update 'from's transitions, and if it transitions to any
* unknown State create it and add it to the work_queue
*/
update_state_transitions(from);
update_state_transitions(nodemap, work_queue, from, stats);
} /* while (!work_queue.empty()) */
@@ -239,68 +209,34 @@ DFA::DFA(Node *root, dfaflags_t flags): root(root)
if (flags & DFA_DUMP_NODE_TO_DFA)
dump_node_to_dfa();
if (flags & (DFA_DUMP_STATS)) {
cerr << "\033[2KCreated dfa: states "
<< states.size()
<< " proto { "
<< node_map
<< " }, nnodes { "
<< nnodes_cache
<< " }, anodes { "
<< anodes_cache
<< " }\n";
}
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()));
/* Clear out uniq_nnodes as they are no longer needed.
* Do not clear out uniq_anodes, as we need them for minimizations
* diffs, unions, ...
*/
nnodes_cache.clear();
node_map.clear();
}
DFA::~DFA()
{
anodes_cache.clear();
nnodes_cache.clear();
for (Partition::iterator i = states.begin(); i != states.end(); i++)
delete *i;
}
State *DFA::match_len(State *state, const char *str, size_t len)
{
for (; len > 0; ++str, --len)
state = state->next(*str);
return state;
}
State *DFA::match_until(State *state, const char *str, const char term)
{
while (*str != term)
state = state->next(*str++);
return state;
}
State *DFA::match(const char *str)
{
return match_until(start, str, 0);
}
void DFA::dump_uniq_perms(const char *s)
{
set<perms_t> uniq;
set<pair<uint32_t, uint32_t> > uniq;
for (Partition::iterator i = states.begin(); i != states.end(); i++)
uniq.insert((*i)->perms);
uniq.insert(make_pair((*i)->accept, (*i)->audit));
cerr << "Unique Permission sets: " << s << " (" << uniq.size() << ")\n";
cerr << "----------------------\n";
for (set<perms_t >::iterator i = uniq.begin(); i != uniq.end(); i++) {
cerr << " allow:" << hex << i->allow << " deny:"
<< i->deny << " audit:" << i->audit
<< " quiet:" << i->quiet << dec << "\n";
for (set<pair<uint32_t, uint32_t> >::iterator i = uniq.begin();
i != uniq.end(); i++) {
cerr << " " << hex << i->first << " " << i->second << dec << "\n";
}
}
@@ -308,6 +244,7 @@ void DFA::dump_uniq_perms(const char *s)
void DFA::remove_unreachable(dfaflags_t flags)
{
set<State *> reachable;
list<State *> work_queue;
/* find the set of reachable states */
reachable.insert(nonmatching);
@@ -317,11 +254,11 @@ void DFA::remove_unreachable(dfaflags_t flags)
work_queue.pop_front();
reachable.insert(from);
if (from->otherwise != nonmatching &&
reachable.find(from->otherwise) == reachable.end())
work_queue.push_back(from->otherwise);
if (from->cases.otherwise &&
(reachable.find(from->cases.otherwise) == reachable.end()))
work_queue.push_back(from->cases.otherwise);
for (StateTrans::iterator j = from->trans.begin(); j != from->trans.end(); j++) {
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);
}
@@ -340,12 +277,10 @@ void DFA::remove_unreachable(dfaflags_t flags)
cerr << "unreachable: " << **i;
if (*i == start)
cerr << " <==";
if (!(*i)->perms.is_null()) {
if ((*i)->accept) {
cerr << " (0x" << hex
<< (*i)->perms.allow << " "
<< (*i)->perms.deny << " "
<< (*i)->perms.audit << " "
<< (*i)->perms.quiet << dec
<< (*i)->accept << " "
<< (*i)->audit << dec
<< ')';
}
cerr << "\n";
@@ -366,22 +301,22 @@ void DFA::remove_unreachable(dfaflags_t flags)
/* test if two states have the same transitions under partition_map */
bool DFA::same_mappings(State *s1, State *s2)
{
if (s1->otherwise != nonmatching) {
if (s2->otherwise == nonmatching)
if (s1->cases.otherwise && s1->cases.otherwise != nonmatching) {
if (!s2->cases.otherwise || s2->cases.otherwise == nonmatching)
return false;
Partition *p1 = s1->otherwise->partition;
Partition *p2 = s2->otherwise->partition;
Partition *p1 = s1->cases.otherwise->partition;
Partition *p2 = s2->cases.otherwise->partition;
if (p1 != p2)
return false;
} else if (s2->otherwise != nonmatching) {
} else if (s2->cases.otherwise && s2->cases.otherwise != nonmatching) {
return false;
}
if (s1->trans.size() != s2->trans.size())
if (s1->cases.cases.size() != s2->cases.cases.size())
return false;
for (StateTrans::iterator j1 = s1->trans.begin(); j1 != s1->trans.end(); j1++) {
StateTrans::iterator j2 = s2->trans.find(j1->first);
if (j2 == s2->trans.end())
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;
@@ -395,7 +330,7 @@ bool DFA::same_mappings(State *s1, State *s2)
/* 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
* trans they will never be equivalent.
* 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
*/
@@ -403,31 +338,22 @@ size_t DFA::hash_trans(State *s)
{
unsigned long hash = 5381;
for (StateTrans::iterator j = s->trans.begin(); j != s->trans.end(); j++) {
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->trans.size();
hash = ((hash << 5) + hash) + k->cases.cases.size();
}
if (s->otherwise != nonmatching) {
if (s->cases.otherwise && s->cases.otherwise != nonmatching) {
hash = ((hash << 5) + hash) + 5381;
State *k = s->otherwise;
hash = ((hash << 5) + hash) + k->trans.size();
State *k = s->cases.otherwise;
hash = ((hash << 5) + hash) + k->cases.cases.size();
}
hash = (hash << 8) | s->trans.size();
hash = (hash << 8) | s->cases.cases.size();
return hash;
}
int DFA::apply_and_clear_deny(void)
{
int c = 0;
for (Partition::iterator i = states.begin(); i != states.end(); i++)
c += (*i)->apply_and_clear_deny();
return c;
}
/* minimize the number of dfa states */
void DFA::minimize(dfaflags_t flags)
{
@@ -447,11 +373,19 @@ void DFA::minimize(dfaflags_t flags)
int accept_count = 0;
int final_accept = 0;
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
size_t hash = 0;
uint64_t permtype = ((uint64_t) (PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny)) << 32) | (uint64_t) (*i)->perms.allow;
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)
hash |= hash_trans(*i);
pair<uint64_t, size_t> group = make_pair(permtype, hash);
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();
@@ -459,7 +393,7 @@ void DFA::minimize(dfaflags_t flags)
perm_map.insert(make_pair(group, part));
partitions.push_back(part);
(*i)->partition = part;
if (permtype)
if (perm_hash)
accept_count++;
} else {
(*i)->partition = p->second;
@@ -553,11 +487,11 @@ void DFA::minimize(dfaflags_t flags)
cerr << *rep << " : ";
/* update representative state's transitions */
if (rep->otherwise != nonmatching) {
Partition *partition = rep->otherwise->partition;
rep->otherwise = *partition->begin();
if (rep->cases.otherwise) {
Partition *partition = rep->cases.otherwise->partition;
rep->cases.otherwise = *partition->begin();
}
for (StateTrans::iterator c = rep->trans.begin(); c != rep->trans.end(); c++) {
for (Cases::iterator c = rep->cases.begin(); c != rep->cases.end(); c++) {
Partition *partition = c->second->partition;
c->second = *partition->begin();
}
@@ -571,9 +505,10 @@ void DFA::minimize(dfaflags_t flags)
if (flags & DFA_DUMP_MIN_PARTS)
cerr << **i << ", ";
(*i)->label = -1;
rep->perms.add((*i)->perms);
rep->accept |= (*i)->accept;
rep->audit |= (*i)->audit;
}
if (!rep->perms.is_null())
if (rep->accept || rep->audit)
final_accept++;
//if ((*p)->size() > 1)
//cerr << "\n";
@@ -628,15 +563,13 @@ out:
void DFA::dump(ostream & os)
{
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
if (*i == start || !(*i)->perms.is_null()) {
if (*i == start || (*i)->accept) {
os << **i;
if (*i == start)
os << " <== (allow/deny/audit/quiet)";
if (!(*i)->perms.is_null()) {
os << " (0x " << hex << (*i)->perms.allow << "/"
<< (*i)->perms.deny << "/"
<< (*i)->perms.audit << "/"
<< (*i)->perms.quiet << ')';
os << " <==";
if ((*i)->accept) {
os << " (0x" << hex << (*i)->accept << " "
<< (*i)->audit << dec << ')';
}
os << "\n";
}
@@ -644,34 +577,12 @@ void DFA::dump(ostream & os)
os << "\n";
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
Chars excluded;
for (StateTrans::iterator j = (*i)->trans.begin();
j != (*i)->trans.end(); j++) {
if (j->second == nonmatching) {
excluded.insert(j->first);
} else {
os << **i << " -> " << *(j)->second << ": 0x"
<< hex << (int) j->first;
if (isprint(j->first))
os << " " << j->first;
os << "\n";
}
}
if ((*i)->otherwise != nonmatching) {
os << **i << " -> " << *(*i)->otherwise << ": [";
if (!excluded.empty()) {
os << "^";
for (Chars::iterator k = excluded.begin();
k != excluded.end(); k++) {
if (isprint(*k))
os << *k;
else
os << "\\0x" << hex << (int) *k;
}
}
os << "]\n";
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";
@@ -692,44 +603,35 @@ void DFA::dump_dot_graph(ostream & os)
if (*i == start) {
os << "\t\tstyle=bold" << "\n";
}
if (!(*i)->perms.is_null()) {
os << "\t\tlabel=\"" << **i << "\\n(0x " << hex
<< (*i)->perms.allow << "/"
<< (*i)->perms.deny << "/"
<< (*i)->perms.audit << "/"
<< (*i)->perms.quiet << ")\"\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 (StateTrans::iterator j = (*i)->trans.begin(); j != (*i)->trans.end(); j++) {
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=\"";
if (isprint(j->first))
os << j->first;
else
os << "\\0xhex" << (int) j->first;
os << "\"\n\t]" << "\n";
os << "\t\tlabel=\"" << j->first << "\"\n";
os << "\t]" << "\n";
}
}
if ((*i)->otherwise != nonmatching) {
os << "\t\"" << **i << "\" -> \"" << *(*i)->otherwise
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++) {
if (isprint(*i))
os << *i;
else
os << "\\0x" << hex << (int) *i;
os << *i;
}
os << "]\"" << "\n";
}
@@ -749,9 +651,11 @@ map<uchar, uchar> DFA::equivalence_classes(dfaflags_t flags)
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 (StateTrans::iterator j = (*i)->trans.begin(); j != (*i)->trans.end(); j++)
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();
@@ -838,9 +742,10 @@ void DFA::apply_equivalence_classes(map<uchar, uchar> &eq)
*/
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
map<uchar, State *> tmp;
tmp.swap((*i)->trans);
for (StateTrans::iterator j = tmp.begin(); j != tmp.end(); j++)
(*i)->trans.insert(make_pair(eq[j->first], j->second));
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));
}
}
@@ -884,66 +789,81 @@ static inline int diff_qualifiers(uint32_t perm1, uint32_t perm2)
* have any exact matches, then they override the execute and safe
* execute flags.
*/
int accept_perms(NodeSet *state, perms_t &perms)
uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error)
{
int error = 0;
uint32_t exact_match_allow = 0;
uint32_t exact_audit = 0;
perms.clear();
if (!state)
return 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_allow,
match->flag))
error = 1;;
exact_match_allow |= match->flag;
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)) {
perms.deny |= match->flag;
perms.quiet |= match->audit;
deny |= match->flag;
quiet |= match->audit;
} else {
if (!is_merged_x_consistent(perms.allow, match->flag))
error = 1;
perms.allow |= match->flag;
perms.audit |= match->audit;
if (!is_merged_x_consistent(perms, match->flag)
&& error)
*error = 1;
perms |= match->flag;
audit |= match->audit;
}
}
perms.allow |= exact_match_allow & ~(ALL_AA_EXEC_TYPE);
//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);
if (exact_match_allow & AA_USER_EXEC_TYPE) {
perms.allow = (exact_match_allow & AA_USER_EXEC_TYPE) |
(perms.allow & ~AA_USER_EXEC_TYPE);
perms.audit = (exact_audit & AA_USER_EXEC_TYPE) |
(perms.audit & ~AA_USER_EXEC_TYPE);
perms.exact = AA_USER_EXEC_TYPE;
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_allow & AA_OTHER_EXEC_TYPE) {
perms.allow = (exact_match_allow & AA_OTHER_EXEC_TYPE) |
(perms.allow & ~AA_OTHER_EXEC_TYPE);
perms.audit = (exact_audit & AA_OTHER_EXEC_TYPE) |
(perms.audit & ~AA_OTHER_EXEC_TYPE);
perms.exact |= AA_OTHER_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 (AA_USER_EXEC & perms.deny)
perms.deny |= AA_USER_EXEC_TYPE;
if (perms & AA_USER_EXEC & deny)
perms &= ~AA_USER_EXEC_TYPE;
if (AA_OTHER_EXEC & perms.deny)
perms.deny |= AA_OTHER_EXEC_TYPE;
if (perms & AA_OTHER_EXEC & deny)
perms &= ~AA_OTHER_EXEC_TYPE;
perms.allow &= ~perms.deny;
perms.quiet &= perms.deny;
perms &= ~deny;
if (error)
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 error;
return perms;
}

View File

@@ -1,7 +1,7 @@
/*
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
* Copyright 2009-2012 Canonical Ltd.
* 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
@@ -33,291 +33,28 @@
#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 map<uchar, State *> StateTrans;
typedef list<State *> Partition;
#include "../immunix.h"
class perms_t {
public:
perms_t(void) throw(int): allow(0), deny(0), audit(0), quiet(0), exact(0) { };
bool is_null(void) { return !(allow | deny | audit | quiet); }
void clear(void) { allow = deny = audit = quiet = 0; }
void add(perms_t &rhs)
{
deny |= rhs.deny;
if (!is_merged_x_consistent(allow & ALL_USER_EXEC,
rhs.allow & ALL_USER_EXEC)) {
if ((exact & AA_USER_EXEC_TYPE) &&
!(rhs.exact & AA_USER_EXEC_TYPE)) {
/* do nothing */
} else if ((rhs.exact & AA_USER_EXEC_TYPE) &&
!(exact & AA_USER_EXEC_TYPE)) {
allow = (allow & ~AA_USER_EXEC_TYPE) |
(rhs.allow & AA_USER_EXEC_TYPE);
} else
throw 1;
} else
allow |= rhs.allow & AA_USER_EXEC_TYPE;
if (!is_merged_x_consistent(allow & ALL_OTHER_EXEC,
rhs.allow & ALL_OTHER_EXEC)) {
if ((exact & AA_OTHER_EXEC_TYPE) &&
!(rhs.exact & AA_OTHER_EXEC_TYPE)) {
/* do nothing */
} else if ((rhs.exact & AA_OTHER_EXEC_TYPE) &&
!(exact & AA_OTHER_EXEC_TYPE)) {
allow = (allow & ~AA_OTHER_EXEC_TYPE) |
(rhs.allow & AA_OTHER_EXEC_TYPE);
} else
throw 1;
} else
allow |= rhs.allow & AA_OTHER_EXEC_TYPE;
allow = (allow | (rhs.allow & ~ALL_AA_EXEC_TYPE));
audit |= rhs.audit;
quiet = (quiet | rhs.quiet);
/*
if (exec & AA_USER_EXEC_TYPE &&
(exec & AA_USER_EXEC_TYPE) != (allow & AA_USER_EXEC_TYPE))
throw 1;
if (exec & AA_OTHER_EXEC_TYPE &&
(exec & AA_OTHER_EXEC_TYPE) != (allow & AA_OTHER_EXEC_TYPE))
throw 1;
*/
}
int apply_and_clear_deny(void)
{
if (deny) {
allow &= ~deny;
quiet &= deny;
deny = 0;
return is_null();
}
return 0;
}
bool operator<(perms_t const &rhs)const
{
if (allow < rhs.allow)
return allow < rhs.allow;
if (deny < rhs.deny)
return deny < rhs.deny;
if (audit < rhs.audit)
return audit < rhs.audit;
return quiet < rhs.quiet;
}
uint32_t allow, deny, audit, quiet, exact;
};
int accept_perms(NodeSet *state, perms_t &perms);
/*
* hashedNodes - for efficient set comparison
*/
class hashedNodeSet {
public:
unsigned long hash;
NodeSet *nodes;
hashedNodeSet(NodeSet *n): nodes(n)
{
hash = hash_NodeSet(n);
}
bool operator<(hashedNodeSet const &rhs)const
{
if (hash == rhs.hash) {
if (nodes->size() == rhs.nodes->size())
return *nodes < *(rhs.nodes);
else
return nodes->size() < rhs.nodes->size();
} else {
return hash < rhs.hash;
}
}
};
class hashedNodeVec {
public:
typedef ImportantNode ** iterator;
iterator begin() { return nodes; }
iterator end() { iterator t = nodes ? &nodes[len] : NULL; return t; }
unsigned long hash;
unsigned long len;
ImportantNode **nodes;
hashedNodeVec(NodeSet *n)
{
hash = hash_NodeSet(n);
len = n->size();
nodes = new ImportantNode *[n->size()];
unsigned int j = 0;
for (NodeSet::iterator i = n->begin(); i != n->end(); i++, j++) {
nodes[j] = *i;
}
}
hashedNodeVec(NodeSet *n, unsigned long h): hash(h)
{
len = n->size();
nodes = new ImportantNode *[n->size()];
ImportantNode **j = nodes;
for (NodeSet::iterator i = n->begin(); i != n->end(); i++) {
*(j++) = *i;
}
}
~hashedNodeVec()
{
delete nodes;
}
unsigned long size()const { return len; }
bool operator<(hashedNodeVec const &rhs)const
{
if (hash == rhs.hash) {
if (len == rhs.size()) {
for (unsigned int i = 0; i < len; i++) {
if (nodes[i] != rhs.nodes[i])
return nodes[i] < rhs.nodes[i];
}
return false;
}
return len < rhs.size();
}
return hash < rhs.hash;
}
};
class CacheStats {
public:
unsigned long dup, sum, max;
CacheStats(void): dup(0), sum(0), max(0) { };
void clear(void) { dup = sum = max = 0; }
virtual unsigned long size(void) const = 0;
};
class NodeCache: public CacheStats {
public:
set<hashedNodeSet> cache;
NodeCache(void): cache() { };
~NodeCache() { clear(); };
virtual unsigned long size(void) const { return cache.size(); }
void clear()
{
for (set<hashedNodeSet>::iterator i = cache.begin();
i != cache.end(); i++) {
delete i->nodes;
}
cache.clear();
CacheStats::clear();
}
NodeSet *insert(NodeSet *nodes)
{
if (!nodes)
return NULL;
pair<set<hashedNodeSet>::iterator,bool> uniq;
uniq = cache.insert(hashedNodeSet(nodes));
if (uniq.second == false) {
delete(nodes);
dup++;
} else {
sum += nodes->size();
if (nodes->size() > max)
max = nodes->size();
}
return uniq.first->nodes;
}
};
struct deref_less_than {
bool operator()(hashedNodeVec * const &lhs, hashedNodeVec * const &rhs)const
{
return *lhs < *rhs;
}
};
class NodeVecCache: public CacheStats {
public:
set<hashedNodeVec *, deref_less_than> cache;
NodeVecCache(void): cache() { };
~NodeVecCache() { clear(); };
virtual unsigned long size(void) const { return cache.size(); }
void clear()
{
for (set<hashedNodeVec *>::iterator i = cache.begin();
i != cache.end(); i++) {
delete *i;
}
cache.clear();
CacheStats::clear();
}
hashedNodeVec *insert(NodeSet *nodes)
{
if (!nodes)
return NULL;
pair<set<hashedNodeVec *>::iterator,bool> uniq;
hashedNodeVec *nv = new hashedNodeVec(nodes);
uniq = cache.insert(nv);
if (uniq.second == false) {
delete nv;
dup++;
} else {
sum += nodes->size();
if (nodes->size() > max)
max = nodes->size();
}
delete(nodes);
return (*uniq.first);
}
};
/*
* ProtoState - NodeSet and ancillery information used to create a state
*/
class ProtoState {
public:
hashedNodeVec *nnodes;
NodeSet *anodes;
ProtoState(hashedNodeVec *n, NodeSet *a = NULL): nnodes(n), anodes(a) { };
bool operator<(ProtoState const &rhs)const
{
if (nnodes == rhs.nnodes)
return anodes < rhs.anodes;
return nnodes < rhs.nnodes;
}
unsigned long size(void)
{
if (anodes)
return nnodes->size() + anodes->size();
return nnodes->size();
}
};
uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error);
/*
* State - DFA individual state information
@@ -326,8 +63,7 @@ public:
* the start state is setup to have label == 1
* audit: the audit permission mask for the state
* accept: the accept permissions for the state
* trans: set of transitions from this state
* otherwise: the default state for transitions not in @trans
* 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.
@@ -336,110 +72,62 @@ public:
*/
class State {
public:
State(int l, ProtoState &n, State *other) throw(int):
label(l), perms(), trans()
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;
if (other)
otherwise = other;
else
otherwise = this;
proto = n;
/* Compute permissions associated with the State. */
error = accept_perms(n.anodes, perms);
accept = accept_perms(nodes, &audit, &error);
if (error) {
//cerr << "Failing on accept perms " << error << "\n";
throw error;
}
};
State *next(uchar c) {
StateTrans::iterator i = trans.find(c);
if (i != trans.end())
return i->second;
return otherwise;
};
int apply_and_clear_deny(void) { return perms.apply_and_clear_deny(); }
int label;
perms_t perms;
StateTrans trans;
State *otherwise;
/* temp storage for State construction */
uint32_t audit, accept;
Cases cases;
union {
Partition *partition;
ProtoState proto;
NodeSet *nodes;
};
};
ostream &operator<<(ostream &os, const State &state);
class NodeMap: public CacheStats
{
public:
typedef map<ProtoState, State *>::iterator iterator;
iterator begin() { return cache.begin(); }
iterator end() { return cache.end(); }
map<ProtoState, State *> cache;
NodeMap(void): cache() { };
~NodeMap() { clear(); };
virtual unsigned long size(void) const { return cache.size(); }
void clear()
{
cache.clear();
CacheStats::clear();
}
pair<iterator,bool> insert(ProtoState &proto, State *state)
{
pair<iterator,bool> uniq;
uniq = cache.insert(make_pair(proto, state));
if (uniq.second == false) {
dup++;
} else {
sum += proto.size();
if (proto.size() > max)
max = proto.size();
}
return uniq;
}
};
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(NodeSet *nodes, State *other);
void update_state_transitions(State *state);
/* temporary values used during computations */
NodeCache anodes_cache;
NodeVecCache nnodes_cache;
NodeMap node_map;
list<State *> work_queue;
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();
State *match_len(State *state, const char *str, size_t len);
State *match_until(State *state, const char *str, const char term);
State *match(const char *str);
void remove_unreachable(dfaflags_t flags);
bool same_mappings(State *s1, State *s2);
size_t hash_trans(State *s);
void minimize(dfaflags_t flags);
int apply_and_clear_deny(void);
void dump(ostream &os);
void dump_dot_graph(ostream &os);
void dump_uniq_perms(const char *s);

View File

@@ -169,7 +169,7 @@ int hexdigit(char c)
else if (c >= 'A' && c <= 'F')
return 10 + c - 'A';
else if (c >= 'a' && c <= 'f')
return 10 + c - 'a';
return 10 + c - 'A';
else
return -1;
}

View File

@@ -1,506 +0,0 @@
/*
* Copyright (c) 2010
* 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.
*/
/**
* The mount command, its mix of options and flags, its permissions and
* mapping are a mess.
* mount [-lhV]
*
* mount -a [-fFnrsvw] [-t vfstype] [-O optlist]
*
* mount [-fnrsvw] [-o option[,option]...] device|dir
*
* mount [-fnrsvw] [-t vfstype] [-o options] device dir
*
*----------------------------------------------------------------------
* Mount flags of no interest for apparmor mediation
* -a, --all
* -F fork for simultaneous mount
* -f fake, do everything except that actual system call
* -h --help
* -i, --internal-only
* -n mount without writing in /etc/mtab
* -O <optlist> limits what is auto mounted
* -p, --pass-fd num
* -s Tolerate sloppy mount options
* -U uuid
* -V --version
* --no-canonicalize
*
*----------------------------------------------------------------------
* what do we do with these
* -l list?
* -L <label> label
* -v --verbose deprecated
*
*----------------------------------------------------------------------
* Filesystem type
* -t <vfstype>
* vfstype=<vfstype>
*
*----------------------------------------------------------------------
* Mount Flags/options (-o --options)
* -o option[,option]
*
* The Linux kernel has 32 fs - independent mount flags, that mount command
* is responsible for stripping out and mapping to a 32 bit flags field.
* The mount commands mapping is documented below.
*
* Unfortunately we can not directly use this mapping as we need to be able
* represent, whether none, 1 or both options of a flag can be present for
* example
* ro, and rw information is stored in a single bit. But we need 2 bits
* of information.
* ro - the mount can only be readonly
* rw - the mount can only be rw
* ro/rw - the mount can be either ro/rw
* the fourth state of neither ro/rw does not exist, but still we need
* >1 bit to represent the possible choices
*
* The fs specific mount options are passed into the kernel as a string
* to be interpreted by the filesystem.
*
*
* #define MS_RDONLY 1 Mount read-only
* ro -r --read-only [source] dest
* rw -w
* #define MS_NOSUID 2 Ignore suid and sgid bits
* nosuid
* suid
* #define MS_NODEV 4 Disallow access to device special files
* nodev
* dev
* #define MS_NOEXEC 8 Disallow program execution
* noexec
* exec
* #define MS_SYNCHRONOUS 16 Writes are synced at once
* sync
* async
* #define MS_REMOUNT 32 Alter flags of a mounted FS
* remount source dest
* #define MS_MANDLOCK 64 Allow mandatory locks on an FS
* mand
* nomand
* #define MS_DIRSYNC 128 Directory modifications are synchronous
* dirsync
* #define MS_NOATIME 1024 Do not update access times
* noatime
* atime
* #define MS_NODIRATIME 2048 Do not update directory access times
* nodiratime
* diratime
* #define MS_BIND 4096
* --bind -B source dest
* #define MS_MOVE 8192
* --move -M source dest
* #define MS_REC 16384
* --rbind -R source dest
* --make-rshared dest
* --make-rslave dest
* --make-rprivate dest
* --make-runbindable dest
* #define MS_VERBOSE 32768 MS_VERBOSE is deprecated
* #define MS_SILENT 32768
* silent
* load
* #define MS_POSIXACL (1<<16) VFS does not apply the umask
* acl
* noacl
* #define MS_UNBINDABLE (1<<17) change to unbindable
* --make-unbindable dest
* #define MS_PRIVATE (1<<18) change to private
* --make-private dest
* #define MS_SLAVE (1<<19) change to slave
* --make-slave dest
* #define MS_SHARED (1<<20) change to shared
* --make-shared dest
* #define MS_RELATIME (1<<21) Update atime relative to mtime/ctime
* relatime
* norelatime
* #define MS_KERNMOUNT (1<<22) this is a kern_mount call
* #define MS_I_VERSION (1<<23) Update inode I_version field
* iversion
* noiversion
* #define MS_STRICTATIME (1<<24) Always perform atime updates
* strictatime
* nostrictatime
* #define MS_NOSEC (1<<28)
* #define MS_BORN (1<<29)
* #define MS_ACTIVE (1<<30)
* #define MS_NOUSER (1<<31)
* nouser
* user
*
* other mount options of interest
*
* selinux
* context=<context>
* fscontext=<context>
* defcontext=<context>,
* rootcontext=<context>
*
* defaults -> rw, suid, dev, exec, auto, nouser, async
* owner -> implies nosuid and nodev
* users -> implies noexec, nosuid, and nodev
*
*----------------------------------------------------------------------
* AppArmor mount rules
*
* AppArmor mount rules try to leverage mount syntax within apparmor syntax
* this can not be done entirely but it is largely covered.
*
* The general mount syntax is
* [audit] [deny] [owner] mount [conds]* [source] [ -> [conds] path],
* [audit] [deny] remount [conds]* [path],
* [audit] [deny] umount [conds]* [path],
*
* Note: leading owner option applies owner condition to both sours and dest
* path.
*
* where [conds] can be
* fstype=<expr>
* options=<expr>
* owner[=<expr>]
*
* <expr> := <re> | '(' (<re>[,])+ ')'
*
* If a condition is not specified then it is assumed to match all possible
* entries for it. ie. a missing fstype means all fstypes are matched.
* However if a condition is specified then the rule only grants permission
* for mounts matching the specified pattern.
*
* Examples.
* mount, # allow any mount
* mount /dev/foo, # allow mounting of /dev/foo anywhere
* mount options=ro /dev/foo, #allow mounting /dev/foo as read only
* mount options=(ro,foo) /dev/foo,
* mount options=ro options=foo /dev/foo,
* mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) overlay -> /mnt/
*
*----------------------------------------------------------------------
* pivotroot
* pivotroot [oldroot=<value>] <path> -> <profile>
* pivotroot <path> -> { }
*
*----------------------------------------------------------------------
* chroot
* chroot <path> -> <profile>
* chroot <path> -> { }
*
*----------------------------------------------------------------------
* AppArmor mount rule encoding
*
* TODO:
* add semantic checking of options against specified filesytem types
* to catch mount options that can't be covered.
*
*
*/
#include <stdlib.h>
#include <string.h>
#include "parser.h"
#include "mount.h"
struct mnt_keyword_table {
char *keyword;
unsigned int set;
unsigned int clear;
};
static struct mnt_keyword_table mnt_opts_table[] = {
{"ro", MS_RDONLY, 0},
{"r", MS_RDONLY, 0},
{"read-only", MS_RDONLY, 0},
{"rw", 0, MS_RDONLY},
{"w", 0, MS_RDONLY},
{"suid", 0, MS_NOSUID},
{"nosuid", MS_NOSUID, 0},
{"dev", 0, MS_NODEV},
{"nodev", MS_NODEV, 0},
{"exec", 0, MS_NOEXEC},
{"noexec", MS_NOEXEC, 0},
{"sync", MS_SYNC, 0},
{"async", 0, MS_SYNC},
{"remount", MS_REMOUNT, 0},
{"mand", MS_MAND, 0},
{"nomand", 0, MS_MAND},
{"dirsync", MS_DIRSYNC, 0},
{"atime", 0, MS_NOATIME},
{"noatime", MS_NOATIME, 0},
{"diratime", 0, MS_NODIRATIME},
{"nodiratime", MS_NODIRATIME, 0},
{"bind", MS_BIND, 0},
{"B", MS_BIND, 0},
{"move", MS_MOVE, 0},
{"M", MS_MOVE, 0},
{"rbind", MS_RBIND, 0},
{"R", MS_RBIND, 0},
{"verbose", MS_VERBOSE, 0},
{"silent", MS_SILENT, 0},
{"load", 0, MS_SILENT},
{"acl", MS_ACL, 0},
{"noacl", 0, MS_ACL},
{"make-unbindable", MS_UNBINDABLE, 0},
{"make-runbindable", MS_RUNBINDABLE, 0},
{"make-private", MS_PRIVATE, 0},
{"make-rprivate", MS_RPRIVATE, 0},
{"make-slave", MS_SLAVE, 0},
{"make-rslave", MS_RSLAVE, 0},
{"make-shared", MS_SHARED, 0},
{"make-rshared", MS_RSHARED, 0},
{"relatime", MS_RELATIME, 0},
{"norelatime", 0, MS_NORELATIME},
{"iversion", MS_IVERSION, 0},
{"noiversion", 0, MS_IVERSION},
{"strictatime", MS_STRICTATIME, 0},
{"user", 0, MS_NOUSER},
{"nouser", MS_NOUSER, 0},
{NULL, 0, 0}
};
static struct mnt_keyword_table mnt_conds_table[] = {
{"options", MNT_SRC_OPT, MNT_COND_OPTIONS},
{"option", MNT_SRC_OPT, MNT_COND_OPTIONS},
{"fstype", MNT_SRC_OPT | MNT_DST_OPT, MNT_COND_FSTYPE},
{"vfstype", MNT_SRC_OPT | MNT_DST_OPT, MNT_COND_FSTYPE},
{NULL, 0, 0}
};
static int find_mnt_keyword(struct mnt_keyword_table *table, const char *name)
{
int i;
for (i = 0; table[i].keyword; i++) {
if (strcmp(name, table[i].keyword) == 0)
return i;
}
return -1;
}
int is_valid_mnt_cond(const char *name, int src)
{
int i;
i = find_mnt_keyword(mnt_conds_table, name);
if (i != -1)
return (mnt_conds_table[i].set & src);
return -1;
}
static unsigned int extract_flags(struct value_list **list, unsigned int *inv)
{
unsigned int flags = 0;
*inv = 0;
struct value_list *entry, *tmp, *prev = NULL;
list_for_each_safe(*list, entry, tmp) {
int i;
i = find_mnt_keyword(mnt_opts_table, entry->value);
if (i != -1) {
flags |= mnt_opts_table[i].set;
*inv |= mnt_opts_table[i].clear;
PDEBUG(" extracting mount flag %s req: 0x%x inv: 0x%x"
" => req: 0x%x inv: 0x%x\n",
entry->value, mnt_opts_table[i].set,
mnt_opts_table[i].clear, flags, *inv);
if (prev)
prev->next = tmp;
if (entry == *list)
*list = tmp;
entry->next = NULL;
free_value_list(entry);
} else
prev = entry;
}
return flags;
}
static struct value_list *extract_fstype(struct cond_entry **conds)
{
struct value_list *list = NULL;
struct cond_entry *entry, *tmp, *prev = NULL;
list_for_each_safe(*conds, entry, tmp) {
if (strcmp(entry->name, "fstype") == 0 ||
strcmp(entry->name, "vfstype") == 0) {
PDEBUG(" extracting fstype\n");
if (prev)
prev->next = tmp;
if (entry == *conds)
*conds = tmp;
entry->next = NULL;
list_append(entry->vals, list);
list = entry->vals;
entry->vals = NULL;
free_cond_entry(entry);
} else
prev = entry;
}
return list;
}
static struct value_list *extract_options(struct cond_entry **conds)
{
struct value_list *list = NULL;
struct cond_entry *entry, *tmp, *prev = NULL;
list_for_each_safe(*conds, entry, tmp) {
if (strcmp(entry->name, "options") == 0 ||
strcmp(entry->name, "option") == 0) {
if (prev)
prev->next = tmp;
if (entry == *conds)
*conds = tmp;
entry->next = NULL;
PDEBUG(" extracting option %s\n", entry->name);
list_append(entry->vals, list);
list = entry->vals;
entry->vals = NULL;
free_cond_entry(entry);
} else
prev = entry;
}
return list;
}
struct mnt_entry *new_mnt_entry(struct cond_entry *src_conds, char *device,
struct cond_entry *dst_conds __unused, char *mnt_point,
int allow)
{
/* FIXME: dst_conds are ignored atm */
struct mnt_entry *ent;
ent = (struct mnt_entry *) calloc(1, sizeof(struct mnt_entry));
if (ent) {
ent->mnt_point = mnt_point;
ent->device = device;
ent->dev_type = extract_fstype(&src_conds);
ent->flags = 0;
if (src_conds) {
ent->opts = extract_options(&src_conds);
if (ent->opts)
ent->flags = extract_flags(&ent->opts,
&ent->inv_flags);
}
if (allow & AA_DUMMY_REMOUNT) {
allow = AA_MAY_MOUNT;
ent->flags |= MS_REMOUNT;
ent->inv_flags = 0;
} else if (!(ent->flags | ent->inv_flags)) {
/* no flag options, and not remount, allow everything */
ent->flags = 0xffffffff;
ent->inv_flags = 0xffffffff;
}
ent->allow = allow;
if (src_conds) {
PERROR(" unsupported mount conditions\n");
exit(1);
}
}
return ent;
}
void free_mnt_entry(struct mnt_entry *ent)
{
if (!ent)
return;
free_mnt_entry(ent->next);
free_value_list(ent->opts);
free_value_list(ent->dev_type);
free(ent->device);
free(ent->mnt_point);
free(ent->trans);
free(ent);
}
struct mnt_entry *dup_mnt_entry(struct mnt_entry *orig)
{
struct mnt_entry *entry = NULL;
entry = (struct mnt_entry *) calloc(1, sizeof(struct mnt_entry));
if (!entry)
return NULL;
entry->mnt_point = orig->mnt_point ? strdup(orig->mnt_point) : NULL;
entry->device = orig->device ? strdup(orig->device) : NULL;
entry->trans = orig->trans ? strdup(orig->trans) : NULL;
entry->dev_type = dup_value_list(orig->dev_type);
entry->opts = dup_value_list(orig->opts);
entry->flags = orig->flags;
entry->inv_flags = orig->inv_flags;
entry->allow = orig->allow;
entry->audit = orig->audit;
entry->deny = orig->deny;
entry->next = orig->next;
return entry;
}
void print_mnt_entry(struct mnt_entry *entry)
{
if (entry->allow & AA_MAY_MOUNT)
fprintf(stderr, "mount");
else if (entry->allow & AA_MAY_UMOUNT)
fprintf(stderr, "umount");
else if (entry->allow & AA_MAY_PIVOTROOT)
fprintf(stderr, "pivotroot");
else
fprintf(stderr, "error: unknonwn mount perm");
fprintf(stderr, " (0x%x - 0x%x) ", entry->flags, entry->inv_flags);
if (entry->dev_type) {
fprintf(stderr, " type=");
print_value_list(entry->dev_type);
}
if (entry->opts) {
fprintf(stderr, " options=");
print_value_list(entry->opts);
}
if (entry->device)
fprintf(stderr, " %s", entry->device);
if (entry->mnt_point)
fprintf(stderr, " -> %s", entry->mnt_point);
if (entry->trans)
fprintf(stderr, " -> %s", entry->trans);
fprintf(stderr, " %s (0x%x/0x%x)", entry->deny ? "deny" : "", entry->allow, entry->audit);
fprintf(stderr, ",\n");
}

View File

@@ -1,134 +0,0 @@
/*
* Copyright (c) 2010
* Canonical, Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#ifndef __AA_MOUNT_H
#define __AA_MOUNT_H
#include "parser.h"
#define MS_RDONLY (1 << 0)
#define MS_RW 0
#define MS_NOSUID (1 << 1)
#define MS_SUID 0
#define MS_NODEV (1 << 2)
#define MS_DEV 0
#define MS_NOEXEC (1 << 3)
#define MS_EXEC 0
#define MS_SYNC (1 << 4)
#define MS_ASYNC 0
#define MS_REMOUNT (1 << 5)
#define MS_MAND (1 << 6)
#define MS_NOMAND 0
#define MS_DIRSYNC (1 << 7)
#define MS_NODIRSYNC 0
#define MS_NOATIME (1 << 10)
#define MS_ATIME 0
#define MS_NODIRATIME (1 << 11)
#define MS_DIRATIME 0
#define MS_BIND (1 << 12)
#define MS_MOVE (1 << 13)
#define MS_REC (1 << 14)
#define MS_VERBOSE (1 << 15)
#define MS_SILENT (1 << 15)
#define MS_LOAD 0
#define MS_ACL (1 << 16)
#define MS_NOACL 0
#define MS_UNBINDABLE (1 << 17)
#define MS_PRIVATE (1 << 18)
#define MS_SLAVE (1 << 19)
#define MS_SHARED (1 << 20)
#define MS_RELATIME (1 << 21)
#define MS_NORELATIME 0
#define MS_IVERSION (1 << 23)
#define MS_NOIVERSION 0
#define MS_STRICTATIME (1 << 24)
#define MS_NOUSER (1 << 31)
#define MS_USER 0
#define MS_ALL_FLAGS (MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC | \
MS_SYNC | MS_REMOUNT | MS_MAND | MS_DIRSYNC | \
MS_NOATIME | MS_NODIRATIME | MS_BIND | MS_MOVE | \
MS_REC | MS_VERBOSE | MS_ACL | MS_UNBINDABLE | \
MS_PRIVATE | MS_SLAVE | MS_SHARED | MS_RELATIME | \
MS_IVERSION | MS_STRICTATIME | MS_USER)
#define MS_RBIND (MS_BIND | MS_REC)
#define MS_RUNBINDABLE (MS_UNBINDABLE | MS_REC)
#define MS_RPRIVATE (MS_PRIVATE | MS_REC)
#define MS_RSLAVE (MS_SLAVE | MS_REC)
#define MS_RSHARED (MS_SHARED | MS_REC)
/* set of flags we don't use but define (but not with the kernel values)
* for MNT_FLAGS
*/
#define MS_ACTIVE 0
#define MS_BORN 0
#define MS_KERNMOUNT 0
/* from kernel fs/namespace.c - set of flags masked off */
#define MNT_FLAGS (MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | \
MS_BORN | MS_NOATIME | MS_NODIRATIME | MS_RELATIME| \
MS_KERNMOUNT | MS_STRICTATIME)
#define MS_REMOUNT_FLAGS (MS_REMOUNT | MNT_FLAGS)
#define MS_BIND_FLAGS (MS_BIND | MS_REC)
#define MS_MAKE_FLAGS ((MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED | \
MS_REC) | (MS_ALL_FLAGS & ~(MNT_FLAGS)))
#define MS_MOVE_FLAGS (MS_MOVE)
#define MS_CMDS (MS_MOVE | MS_REMOUNT | MS_BIND | MS_PRIVATE | MS_SLAVE | \
MS_SHARED | MS_UNBINDABLE)
#define MNT_SRC_OPT 1
#define MNT_DST_OPT 2
#define MNT_COND_FSTYPE 1
#define MNT_COND_OPTIONS 2
#define AA_MAY_PIVOTROOT 1
#define AA_MAY_MOUNT 2
#define AA_MAY_UMOUNT 4
#define AA_DUMMY_REMOUNT 32 /* dummy perm for remount rule - is remapped
* to a mount option*/
struct mnt_entry {
char *mnt_point;
char *device;
char *trans;
struct value_list *dev_type;
struct value_list *opts;
unsigned int flags, inv_flags;
int allow, audit;
int deny;
struct mnt_entry *next;
};
void print_mnt_entry(struct mnt_entry *entry);
int is_valid_mnt_cond(const char *name, int src);
struct mnt_entry *new_mnt_entry(struct cond_entry *sconds, char *device,
struct cond_entry *dconds, char *mnt_point,
int mode);
struct mnt_entry *dup_mnt_entry(struct mnt_entry *orig);
void free_mnt_entry(struct mnt_entry *ent);
#endif /* __AA_MOUNT_H */

View File

@@ -2,8 +2,8 @@
* Copyright (c) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
*
* Copyright (c) 2010 - 2012
* Canonical Ltd. (All rights reserved)
* Copyright (c) 2010
* 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
@@ -19,22 +19,12 @@
* Ltd.
*/
#ifndef __AA_PARSER_H
#define __AA_PARSER_H
#include <netinet/in.h>
#include <sys/resource.h>
#include "immunix.h"
#include "libapparmor_re/apparmor_re.h"
#include "libapparmor_re/aare_rules.h"
struct mnt_ent;
/* Global variable to pass token to lexer. Will be replaced by parameter
* when lexer and parser are made reentrant
*/
extern int parser_token;
typedef enum pattern_t pattern_t;
struct flagval {
@@ -54,19 +44,6 @@ struct cod_pattern {
char *regex; // posix regex
};
struct value_list {
char *value;
struct value_list *next;
};
struct cond_entry {
char *name;
struct value_list *vals;
struct cond_entry *next;
};
struct cod_entry {
char *namespace;
char *name;
@@ -130,6 +107,7 @@ struct codomain {
uint64_t audit_caps;
uint64_t deny_caps;
uint64_t quiet_caps;
uint64_t set_caps;
unsigned int *network_allowed; /* array of type masks
* indexed by AF_FAMILY */
@@ -141,8 +119,6 @@ struct codomain {
char *exec_table[AA_EXEC_COUNT];
struct cod_entry *entries;
struct mnt_entry *mnt_ents;
void *hat_table;
//struct codomain *next;
@@ -150,11 +126,6 @@ struct codomain {
int dfarule_count;
void *dfa;
size_t dfa_size;
aare_ruleset_t *policy_rules;
int policy_rule_count;
void *policy_dfa;
size_t policy_dfa_size;
};
struct sd_hat {
@@ -245,19 +216,12 @@ extern int preprocess_only;
#define __unused __attribute__ ((unused))
#endif
#define list_for_each(LIST, ENTRY) \
for ((ENTRY) = (LIST); (ENTRY); (ENTRY) = (ENTRY)->next)
#define list_for_each_safe(LIST, ENTRY, TMP) \
for ((ENTRY) = (LIST), (TMP) = (LIST) ? (LIST)->next : NULL; (ENTRY); (ENTRY) = (TMP), (TMP) = (TMP) ? (TMP)->next : NULL)
#define list_last_entry(LIST, ENTRY) \
for ((ENTRY) = (LIST); (ENTRY) && (ENTRY)->next; (ENTRY) = (ENTRY)->next)
#define list_append(LISTA, LISTB) \
do { \
typeof(LISTA) ___tmp; \
list_last_entry((LISTA), ___tmp);\
___tmp->next = (LISTB); \
} while (0)
/* from parser_common.c */
extern int regex_type;
@@ -265,7 +229,6 @@ extern int perms_create;
extern int net_af_max_override;
extern int kernel_load;
extern int kernel_supports_network;
extern int kernel_supports_mount;
extern int flag_changehat_version;
extern int conf_verbose;
extern int conf_quiet;
@@ -291,7 +254,7 @@ extern void update_mru_tstamp(FILE *file);
extern FILE *yyin;
extern void yyrestart(FILE *fp);
extern int yyparse(void);
extern void yyerror(const char *msg, ...);
extern void yyerror(char *msg, ...);
extern int yylex(void);
/* parser_include.c */
@@ -302,24 +265,12 @@ extern int process_regex(struct codomain *cod);
extern int post_process_entry(struct cod_entry *entry);
extern void reset_regex(void);
extern int process_policydb(struct codomain *cod);
extern int process_policy_ents(struct codomain *cod);
/* parser_variable.c */
extern int process_variables(struct codomain *cod);
extern struct var_string *split_out_var(char *string);
extern void free_var_string(struct var_string *var);
/* parser_misc.c */
extern struct value_list *new_value_list(char *value);
extern struct value_list *dup_value_list(struct value_list *list);
extern void free_value_list(struct value_list *list);
extern void print_value_list(struct value_list *list);
extern struct cond_entry *new_cond_entry(char *name, struct value_list *list);
extern void free_cond_entry(struct cond_entry *ent);
extern void print_cond_entry(struct cond_entry *ent);
extern char *processid(char *string, int len);
extern char *processquoted(char *string, int len);
extern char *processunquoted(char *string, int len);
extern int get_keyword_token(const char *keyword);
@@ -342,7 +293,6 @@ extern void debug_cod_list(struct codomain *list);
extern int str_to_boolean(const char* str);
extern struct cod_entry *copy_cod_entry(struct cod_entry *cod);
extern void free_cod_entries(struct cod_entry *list);
extern void free_mnt_entries(struct mnt_entry *list);
/* parser_symtab.c */
struct set_value {;
@@ -381,11 +331,9 @@ extern void add_to_list(struct codomain *codomain);
extern void add_hat_to_policy(struct codomain *policy, struct codomain *hat);
extern void add_entry_to_policy(struct codomain *policy, struct cod_entry *entry);
extern void post_process_nt_entries(struct codomain *cod);
extern void post_process_mnt_entries(struct codomain *cod);
extern int post_process_policy(int debug_only);
extern int process_hat_regex(struct codomain *cod);
extern int process_hat_variables(struct codomain *cod);
extern int process_hat_policydb(struct codomain *cod);
extern int post_merge_rules(void);
extern int merge_hat_rules(struct codomain *cod);
extern struct codomain *merge_policy(struct codomain *a, struct codomain *b);
@@ -406,7 +354,7 @@ void free_policies(void);
*/
/* parser_yacc.y */
void yyerror(const char *msg, ...)
void yyerror(char *msg, ...)
{
va_list arg;
char buf[PATH_MAX];
@@ -427,5 +375,3 @@ void yyerror(const char *msg, ...)
}
#endif
#endif /** __AA_PARSER_H */

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2010 - 2012
* Canonical Ltd. (All rights reserved)
* 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
@@ -27,7 +27,6 @@ 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 kernel_supports_mount = 0; /* kernel supports mount rules */
int flag_changehat_version = FLAG_CHANGEHAT_1_5;
int conf_verbose = 0;
int conf_quiet = 0;
@@ -35,7 +34,7 @@ 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;
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__;
@@ -55,16 +54,18 @@ void pwarn(char *fmt, ...)
{
va_list arg;
char *newfmt;
int rc;
if (conf_quiet || names_only || option == OPTION_REMOVE)
return;
if (asprintf(&newfmt, _("Warning from %s (%s%sline %d): %s"),
profilename ? profilename : "stdin",
current_filename ? current_filename : "",
current_filename ? " " : "",
current_lineno,
fmt) == -1)
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);

View File

@@ -1,8 +1,8 @@
/*
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
* Copyright (c) 2010 - 2012
* Canonical Ltd.
* Copyright (c) 2010
* Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public

View File

@@ -1,8 +1,8 @@
/*
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
* Copyright (c) 2010 - 2012
* Canonical Ltd.
* Copyright (c) 2010
* Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public

View File

@@ -59,7 +59,6 @@
#define SUBDOMAIN_INTERFACE_VERSION 2
#define SUBDOMAIN_INTERFACE_DFA_VERSION 5
#define SUBDOMAIN_INTERFACE_POLICY_DB 16
int sd_serialize_codomain(int option, struct codomain *cod);
@@ -610,14 +609,15 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
#define low_caps(X) ((u32) ((X) & 0xffffffff))
#define high_caps(X) ((u32) (((X) >> 32) & 0xffffffff))
allowed_caps = (profile->capabilities) & ~profile->deny_caps;
allowed_caps = (profile->capabilities | profile->set_caps) &
~profile->deny_caps;
if (!sd_write32(p, low_caps(allowed_caps)))
return 0;
if (!sd_write32(p, low_caps(allowed_caps & profile->audit_caps)))
return 0;
if (!sd_write32(p, low_caps(profile->deny_caps & profile->quiet_caps)))
return 0;
if (!sd_write32(p, 0))
if (!sd_write32(p, low_caps(profile->set_caps & ~profile->deny_caps)))
return 0;
if (!sd_write_struct(p, "caps64"))
@@ -628,7 +628,7 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
return 0;
if (!sd_write32(p, high_caps(profile->deny_caps & profile->quiet_caps)))
return 0;
if (!sd_write32(p, 0))
if (!sd_write32(p, high_caps(profile->set_caps & ~profile->deny_caps)))
return 0;
if (!sd_write_structend(p))
return 0;
@@ -655,15 +655,6 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
} else if (profile->network_allowed)
pwarn(_("profile %s network rules not enforced\n"), profile->name);
if (profile->policy_dfa && regex_type == AARE_DFA) {
if (!sd_write_struct(p, "policydb"))
return 0;
if (!sd_serialize_dfa(p, profile->policy_dfa, profile->policy_dfa_size))
return 0;
if (!sd_write_structend(p))
return 0;
}
/* either have a single dfa or lists of different entry types */
if (regex_type == AARE_DFA) {
if (!sd_serialize_dfa(p, profile->dfa, profile->dfa_size))
@@ -695,13 +686,9 @@ int sd_serialize_top_profile(sd_serialize *p, struct codomain *profile)
{
int version;
if (regex_type == AARE_DFA) {
/* Not yet
if (profile->policy_dfa)
version = SUBDOMAIN_INTERFACE_POLICYDB;
else */
version = SUBDOMAIN_INTERFACE_DFA_VERSION;
} else
if (regex_type == AARE_DFA)
version = SUBDOMAIN_INTERFACE_DFA_VERSION;
else
version = SUBDOMAIN_INTERFACE_VERSION;
@@ -776,10 +763,10 @@ int sd_serialize_codomain(int option, struct codomain *cod)
int len = 0;
if (profile_namespace) {
len += strlen(profile_namespace) + 2;
len += strlen(profile_namespace) + 1;
ns = profile_namespace;
} else if (cod->namespace) {
len += strlen(cod->namespace) + 2;
len += strlen(cod->namespace) + 1;
ns = cod->namespace;
}
if (cod->parent) {
@@ -791,7 +778,7 @@ int sd_serialize_codomain(int option, struct codomain *cod)
goto exit;
}
if (ns)
sprintf(name, ":%s:%s//%s", ns,
sprintf(name, "%s:%s//%s", ns,
cod->parent->name, cod->name);
else
sprintf(name, "%s//%s", cod->parent->name,
@@ -803,7 +790,7 @@ int sd_serialize_codomain(int option, struct codomain *cod)
error = -errno;
goto exit;
}
sprintf(name, ":%s:%s", ns, cod->name);
sprintf(name, "%s:%s", ns, cod->name);
} else {
name = cod->name;
}

View File

@@ -1,8 +1,8 @@
/*
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
* Copyright (c) 2010 - 2012
* Canonical Ltd.
* Copyright (c) 2010
* Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
@@ -23,7 +23,6 @@
/* eliminates need to link with libfl */
%option noyywrap
%option nounput
%option stack
%{
#include <stdio.h>
@@ -45,7 +44,7 @@
#endif
/* #define DEBUG */
#ifdef DEBUG
#define PDEBUG(fmt, args...) printf("Lexer (state %d): " fmt, YY_START, ## args)
#define PDEBUG(fmt, args...) printf("Lexer: " fmt, ## args)
#else
#define PDEBUG(fmt, args...) /* Do nothing */
#endif
@@ -168,35 +167,25 @@ void include_filename(char *filename, int search)
%}
CARET "^"
UP "^"
OPEN_BRACE \{
CLOSE_BRACE \}
SLASH \/
COLON :
END_OF_RULE [,]
SEPARATOR {UP}
RANGE -
MODE_CHARS ([RrWwaLlMmkXx])|(([Pp]|[Cc])[Xx])|(([Pp]|[Cc])?([IiUu])[Xx])
MODES {MODE_CHARS}+
WS [[:blank:]]
NUMBER [[:digit:]]+
ID_CHARS [^ \t\n"!,]
ID {ID_CHARS}|(,{ID_CHARS})
IDS {ID}+
POST_VAR_ID_CHARS [^ \t\n"!,]{-}[=\+]
POST_VAR_ID {POST_VAR_ID_CHARS}|(,{POST_VAR_ID_CHARS})
LIST_VALUE_ID_CHARS [^ \t\n"!,]{-}[()]
LIST_VALUE_ID {LIST_VALUE_ID_CHARS}+
ID_CHARS_NOEQ [^ \t\n"!,]{-}[=]
ID_NOEQ {ID_CHARS_NOEQ}|(,{ID_CHARS_NOEQ})
IDS_NOEQ {ID_NOEQ}+
ID [^ \t\n"!,]|(,[^ \t\n"!])
POST_VAR_ID [^ =\+\t\n"!,]|(,[^ =\+\t\n"!])
IP {NUMBER}\.{NUMBER}\.{NUMBER}\.{NUMBER}
ALLOWED_QUOTED_ID [^\0"]|\\\"
QUOTED_ID \"{ALLOWED_QUOTED_ID}*\"
IP {NUMBER}\.{NUMBER}\.{NUMBER}\.{NUMBER}
HAT hat{WS}*
PROFILE profile{WS}*
HAT hat[ \t]+
KEYWORD [[:alpha:]_]+
VARIABLE_NAME [[:alpha:]][[:alnum:]_]*
SET_VAR_PREFIX @
@@ -206,36 +195,25 @@ BOOL_VARIABLE $(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
PATHNAME (\/|{SET_VARIABLE}{POST_VAR_ID}){ID}*
QPATHNAME \"(\/|{SET_VAR_PREFIX})([^\0"]|\\\")*\"
OPEN_PAREN \(
CLOSE_PAREN \)
COMMA \,
FLAGOPEN_PAREN \(
FLAGCLOSE_PAREN \)
FLAGSEP \,
EQUALS =
ADD_ASSIGN \+=
ARROW ->
LT_EQUAL <=
%x SUB_ID
%x SUB_VALUE
%x EXTCOND_MODE
%x SUB_NAME
%x SUB_NAME2
%x NETWORK_MODE
%x LIST_VAL_MODE
%x FLAGS_MODE
%x ASSIGN_MODE
%x RLIMIT_MODE
%x MOUNT_MODE
%x CHANGE_PROFILE_MODE
%x INCLUDE
%%
%{
/* Copied directly into yylex function */
if (parser_token) {
int t = parser_token;
parser_token = 0;
return t;
}
%}
<INCLUDE>{
{WS}+ { /* Eat whitespace */ }
\<([^\> \t\n]+)\> { /* <filename> */
@@ -243,7 +221,7 @@ LT_EQUAL <=
filename[strlen(filename) - 1] = '\0';
include_filename(filename + 1, 1);
free(filename);
yy_pop_state();
BEGIN(INITIAL);
}
\"([^\" \t\n]+)\" { /* "filename" */
@@ -251,12 +229,12 @@ LT_EQUAL <=
filename[strlen(filename) - 1] = '\0';
include_filename(filename + 1, 0);
free(filename);
yy_pop_state();
BEGIN(INITIAL);
}
[^\<\>\"{WS}]+ { /* filename */
include_filename(yytext, 0);
yy_pop_state();
BEGIN(INITIAL);
}
}
@@ -267,34 +245,33 @@ LT_EQUAL <=
if ( !YY_CURRENT_BUFFER ) yyterminate();
}
<INITIAL,MOUNT_MODE>{
{VARIABLE_NAME}/{WS}*= {
/* we match to the = in the lexer so that
* can switch scanner state. By the time
* the parser see the = it may be to late
* as bison may have requested the next
* token from the scanner
*/
PDEBUG("conditional %s=\n", yytext);
yylval.id = processid(yytext, yyleng);
yy_push_state(EXTCOND_MODE);
return TOK_CONDID;
}
}
<SUB_ID>{
({IDS}|{QUOTED_ID}) {
<SUB_NAME>{
{ID}+ {
/* Ugh, this is a gross hack. I used to use
* {IDS} to match all TOK_IDs, but that would
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* separate state. */
DUMP_PREPROCESS;
yylval.id = processid(yytext, yyleng);
yylval.id = processunquoted(yytext, yyleng);
PDEBUG("Found sub name: \"%s\"\n", yylval.id);
yy_pop_state();
BEGIN(INITIAL);
return TOK_ID;
}
{QUOTED_ID} {
/* Ugh, this is a gross hack. I used to use
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* separate state. */
DUMP_PREPROCESS;
yylval.id = processquoted(yytext, yyleng);
PDEBUG("Found sub name: \"%s\"\n", yylval.id);
BEGIN(INITIAL);
return TOK_ID;
}
@@ -305,22 +282,37 @@ LT_EQUAL <=
}
}
<SUB_VALUE>{
({IDS}|{QUOTED_ID}) {
<SUB_NAME2>{
{ID}+ {
/* Ugh, this is a gross hack. I used to use
* {IDS} to match all TOK_IDs, but that would
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* separate state. */
DUMP_PREPROCESS;
yylval.id = processid(yytext, yyleng);
PDEBUG("Found sub value: \"%s\"\n", yylval.id);
yy_pop_state();
return TOK_VALUE;
yylval.id = processunquoted(yytext, yyleng);
PDEBUG("Found sub name: \"%s\"\n", yylval.id);
BEGIN(INITIAL);
return TOK_ID;
}
{QUOTED_ID} {
/* Ugh, this is a gross hack. I used to use
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* separate state. */
DUMP_PREPROCESS;
yylval.id = processquoted(yytext, yyleng);
PDEBUG("Found sub name: \"%s\"\n", yylval.id);
BEGIN(INITIAL);
return TOK_ID;
}
{WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ }
[^\n] {
DUMP_PREPROCESS;
/* Something we didn't expect */
@@ -328,27 +320,37 @@ LT_EQUAL <=
}
}
<LIST_VAL_MODE>{
{CLOSE_PAREN} {
<FLAGS_MODE>{
{FLAGOPEN_PAREN} {
DUMP_PREPROCESS;
PDEBUG("listval: )\n");
yy_pop_state();
return TOK_CLOSEPAREN;
PDEBUG("FLag (\n");
return TOK_FLAG_OPENPAREN;
}
{FLAGCLOSE_PAREN} {
DUMP_PREPROCESS;
PDEBUG("Flag )\n");
BEGIN(INITIAL);
return TOK_FLAG_CLOSEPAREN;
}
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
{COMMA} {
{FLAGSEP} {
DUMP_PREPROCESS;
PDEBUG("listval: , \n");
/* East comma, its an optional separator */
PDEBUG("Flag , \n");
return TOK_FLAG_SEP;
}
({LIST_VALUE_ID}|{QUOTED_ID}) {
{EQUALS} {
DUMP_PREPROCESS;
yylval.id = processid(yytext, yyleng);
PDEBUG("listval: \"%s\"\n", yylval.id);
return TOK_VALUE;
PDEBUG("Flag = \n");
return TOK_EQUALS;
}
{KEYWORD} {
DUMP_PREPROCESS;
yylval.flag_id = strdup(yytext);
PDEBUG("Found flag: \"%s\"\n", yylval.flag_id);
return TOK_FLAG_ID;
}
[^\n] {
@@ -358,46 +360,19 @@ LT_EQUAL <=
}
}
<EXTCOND_MODE>{
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
{EQUALS}{WS}*/[^(\n]{-}{WS} {
DUMP_PREPROCESS;
BEGIN(SUB_VALUE);
return TOK_EQUALS;
}
{EQUALS} {
DUMP_PREPROCESS;
return TOK_EQUALS;
}
{OPEN_PAREN} {
DUMP_PREPROCESS;
PDEBUG("extcond listv\n");
/* Don't push state here as this is a transition
* start condition and we want to return to the start
* condition that invoked <EXTCOND_MODE> when
* LIST_VAL_ID is done
*/
BEGIN(LIST_VAL_MODE);
return TOK_OPENPAREN;
}
[^\n] {
DUMP_PREPROCESS;
/* Something we didn't expect */
yyerror(_("Found unexpected character: '%s' %d"), yytext, *yytext);
}
}
<ASSIGN_MODE>{
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
({IDS}|{QUOTED_ID}) {
{ID}+ {
DUMP_PREPROCESS;
yylval.var_val = processid(yytext, yyleng);
yylval.var_val = processunquoted(yytext, yyleng);
PDEBUG("Found assignment value: \"%s\"\n", yylval.var_val);
return TOK_VALUE;
}
{QUOTED_ID} {
DUMP_PREPROCESS;
yylval.var_val = processquoted(yytext, yyleng);
PDEBUG("Found assignment value: \"%s\"\n", yylval.var_val);
return TOK_VALUE;
}
@@ -413,7 +388,7 @@ LT_EQUAL <=
\r?\n {
DUMP_PREPROCESS;
current_lineno++;
yy_pop_state();
BEGIN(INITIAL);
}
[^\n] {
DUMP_PREPROCESS;
@@ -425,14 +400,14 @@ LT_EQUAL <=
<NETWORK_MODE>{
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
{IDS} {
{ID}+ {
DUMP_PREPROCESS;
yylval.id = strdup(yytext);
return TOK_ID;
}
{END_OF_RULE} {
DUMP_PREPROCESS;
yy_pop_state();
BEGIN(INITIAL);
return TOK_END_OF_RULE;
}
[^\n] {
@@ -455,18 +430,32 @@ LT_EQUAL <=
return TOK_ARROW;
}
({IDS}|{QUOTED_ID}) {
{ID}+ {
/* Ugh, this is a gross hack. I used to use
* {IDS} to match all TOK_IDs, but that would
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* separate state. */
DUMP_PREPROCESS;
yylval.id = processid(yytext, yyleng);
yylval.id = processunquoted(yytext, yyleng);
PDEBUG("Found change profile name: \"%s\"\n", yylval.id);
yy_pop_state();
BEGIN(INITIAL);
return TOK_ID;
}
{QUOTED_ID} {
/* Ugh, this is a gross hack. I used to use
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* separate state. */
DUMP_PREPROCESS;
yylval.id = processquoted(yytext, yyleng);
PDEBUG("Found change profile quoted name: \"%s\"\n", yylval.id);
BEGIN(INITIAL);
return TOK_ID;
}
@@ -478,6 +467,43 @@ LT_EQUAL <=
}
}
#include/.*\r?\n { /* include */
PDEBUG("Matched #include\n");
BEGIN(INCLUDE);
}
#.*\r?\n { /* normal comment */
DUMP_PREPROCESS;
PDEBUG("comment(%d): %s\n", current_lineno, yytext);
current_lineno++;
BEGIN(INITIAL);
}
{END_OF_RULE} { DUMP_PREPROCESS; return TOK_END_OF_RULE; }
{SEPARATOR} {
DUMP_PREPROCESS;
PDEBUG("Matched a separator\n");
BEGIN(SUB_NAME);
return TOK_SEP;
}
{ARROW} {
DUMP_PREPROCESS;
PDEBUG("Matched a arrow\n");
return TOK_ARROW;
}
{EQUALS} {
DUMP_PREPROCESS;
PDEBUG("Matched equals for assignment\n");
BEGIN(ASSIGN_MODE);
return TOK_EQUALS;
}
{ADD_ASSIGN} {
DUMP_PREPROCESS;
PDEBUG("Matched additive value assignment\n");
BEGIN(ASSIGN_MODE);
return TOK_ADD_ASSIGN;
}
<RLIMIT_MODE>{
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
@@ -500,94 +526,23 @@ LT_EQUAL <=
{END_OF_RULE} {
DUMP_PREPROCESS;
yy_pop_state();
BEGIN(INITIAL);
return TOK_END_OF_RULE;
}
\\\n {
DUMP_PREPROCESS;
current_lineno++;
yy_pop_state();
BEGIN(INITIAL);
}
\r?\n {
DUMP_PREPROCESS;
current_lineno++;
yy_pop_state();
BEGIN(INITIAL);
}
}
<MOUNT_MODE>{
{WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ }
{ARROW} {
DUMP_PREPROCESS;
PDEBUG("Matched arrow\n");
return TOK_ARROW;
}
({IDS_NOEQ}|{PATHNAME}|{QUOTED_ID}) {
DUMP_PREPROCESS;
yylval.id = processid(yytext, yyleng);
PDEBUG("Found ID: \"%s\"\n", yylval.id);
return TOK_ID;
}
{END_OF_RULE} {
DUMP_PREPROCESS;
yy_pop_state();
return TOK_END_OF_RULE;
}
[^\n] {
DUMP_PREPROCESS;
/* Something we didn't expect */
yyerror(_("Found unexpected character: '%s'"), yytext);
}
\r?\n {
DUMP_PREPROCESS;
current_lineno++;
yy_pop_state();
}
}
#include/.*\r?\n { /* include */
PDEBUG("Matched #include\n");
yy_push_state(INCLUDE);
}
#.*\r?\n { /* normal comment */
DUMP_PREPROCESS;
PDEBUG("comment(%d): %s\n", current_lineno, yytext);
current_lineno++;
}
{END_OF_RULE} { DUMP_PREPROCESS; return TOK_END_OF_RULE; }
{CARET} {
DUMP_PREPROCESS;
PDEBUG("Matched hat ^\n");
yy_push_state(SUB_ID);
return TOK_CARET;
}
{ARROW} {
DUMP_PREPROCESS;
PDEBUG("Matched a arrow\n");
return TOK_ARROW;
}
{EQUALS} {
DUMP_PREPROCESS;
PDEBUG("Matched equals for assignment\n");
yy_push_state(ASSIGN_MODE);
return TOK_EQUALS;
}
{ADD_ASSIGN} {
DUMP_PREPROCESS;
PDEBUG("Matched additive value assignment\n");
yy_push_state(ASSIGN_MODE);
return TOK_ADD_ASSIGN;
}
{SET_VARIABLE} {
DUMP_PREPROCESS;
yylval.set_var = strdup(yytext);
@@ -613,14 +568,21 @@ LT_EQUAL <=
return TOK_CLOSE;
}
({PATHNAME}|{QPATHNAME}) {
{PATHNAME} {
DUMP_PREPROCESS;
yylval.id = processid(yytext, yyleng);
yylval.id = processunquoted(yytext, yyleng);
PDEBUG("Found id: \"%s\"\n", yylval.id);
return TOK_ID;
}
({MODES})/([[:space:],]) {
{QPATHNAME} {
DUMP_PREPROCESS;
yylval.id = processquoted(yytext, yyleng);
PDEBUG("Found id: \"%s\"\n", yylval.id);
return TOK_ID;
}
{MODES} {
DUMP_PREPROCESS;
yylval.mode = strdup(yytext);
PDEBUG("Found modes: %s\n", yylval.mode);
@@ -629,27 +591,21 @@ LT_EQUAL <=
{HAT} {
DUMP_PREPROCESS;
yy_push_state(SUB_ID);
BEGIN(SUB_NAME2);
return TOK_HAT;
}
{PROFILE} {
DUMP_PREPROCESS;
yy_push_state(SUB_ID);
return TOK_PROFILE;
}
{COLON} {
DUMP_PREPROCESS;
PDEBUG("Found a colon\n");
return TOK_COLON;
}
{OPEN_PAREN} {
{FLAGOPEN_PAREN} {
DUMP_PREPROCESS;
PDEBUG("listval (\n");
yy_push_state(LIST_VAL_MODE);
return TOK_OPENPAREN;
PDEBUG("FLag (\n");
BEGIN(FLAGS_MODE);
return TOK_FLAG_OPENPAREN;
}
{VARIABLE_NAME} {
@@ -664,21 +620,20 @@ LT_EQUAL <=
PDEBUG("Found (var) id: \"%s\"\n", yylval.id);
return TOK_ID;
break;
case TOK_PROFILE:
BEGIN(SUB_NAME2);
break;
case TOK_FLAGS:
BEGIN(FLAGS_MODE);
break;
case TOK_RLIMIT:
yy_push_state(RLIMIT_MODE);
BEGIN(RLIMIT_MODE);
break;
case TOK_NETWORK:
yy_push_state(NETWORK_MODE);
BEGIN(NETWORK_MODE);
break;
case TOK_CHANGE_PROFILE:
yy_push_state(CHANGE_PROFILE_MODE);
break;
case TOK_MOUNT:
case TOK_REMOUNT:
case TOK_UMOUNT:
DUMP_PREPROCESS;
PDEBUG("Entering mount\n");
yy_push_state(MOUNT_MODE);
BEGIN(CHANGE_PROFILE_MODE);
break;
default: /* nothing */
break;

View File

@@ -2,8 +2,8 @@
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
*
* Copyright (c) 2010 - 2012
* Canonical Ltd. (All rights reserved)
* 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
@@ -30,7 +30,6 @@
#include <mntent.h>
#include <libintl.h>
#include <locale.h>
#include <dirent.h>
#define _(s) gettext(s)
/* enable the following line to get voluminous debug info */
@@ -60,7 +59,7 @@
#define UNPRIVILEGED_OPS (!(PRIVILEGED_OPS))
const char *parser_title = "AppArmor parser";
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2012 Canonical Ltd.";
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2011 Canonical Ltd.";
char *progname;
int opt_force_complain = 0;
@@ -75,10 +74,8 @@ int preprocess_only = 0;
int skip_mode_force = 0;
struct timespec mru_tstamp;
#define FLAGS_STRING_SIZE 1024
char *match_string = NULL;
char *flags_string = NULL;
char *cacheloc = NULL;
/* per-profile settings */
int force_complain = 0;
@@ -109,7 +106,6 @@ struct option long_options[] = {
{"skip-read-cache", 0, 0, 'T'},
{"write-cache", 0, 0, 'W'},
{"show-cache", 0, 0, 'k'},
{"cache-loc", 1, 0, 'L'},
{"debug", 0, 0, 'd'},
{"dump", 1, 0, 'D'},
{"Dump", 1, 0, 'D'},
@@ -151,7 +147,6 @@ static void display_usage(char *command)
"-K, --skip-cache Do not attempt to load or save cached profiles\n"
"-T, --skip-read-cache Do not attempt to load cached profiles\n"
"-W, --write-cache Save cached profile (force with -T)\n"
"-L, --cache-loc n Set the location of the profile cache\n"
"-q, --quiet Don't emit warnings\n"
"-v, --verbose Show profile names as they load\n"
"-Q, --skip-kernel-load Do everything except loading into kernel\n"
@@ -230,10 +225,10 @@ optflag_table_t optflag_table[] = {
{ 2, "expr-right-simplify", "right simplification first",
DFA_CONTROL_TREE_LEFT },
{ 1, "minimize", "dfa state minimization", DFA_CONTROL_MINIMIZE },
{ 1, "hash-perms", "minimization - hash permissions during setup",
DFA_CONTROL_MINIMIZE_HASH_PERMS },
{ 1, "hash-trans", "minimization - hash transitions during setup",
DFA_CONTROL_MINIMIZE_HASH_TRANS },
{ 1, "filter-deny", "filter out deny information from final dfa",
DFA_CONTROL_FILTER_DENY },
{ 1, "remove-unreachable", "dfa unreachable state removal",
DFA_CONTROL_REMOVE_UNREACHABLE },
{ 0, "compress-small",
@@ -527,9 +522,6 @@ static int process_arg(int c, char *optarg)
case 'T':
skip_read_cache = 1;
break;
case 'L':
cacheloc = strdup(optarg);
break;
case 'Q':
kernel_load = 0;
break;
@@ -675,137 +667,17 @@ int have_enough_privilege(void)
return 0;
}
char *snprintf_buffer(char *buf, char *pos, ssize_t size, const char *fmt, ...)
{
va_list args;
int i, remaining = size - (pos - buf);
va_start(args, fmt);
i = vsnprintf(pos, remaining, fmt, args);
va_end(args);
if (i >= size) {
PERROR(_("Feature buffer full."));
exit(1);
}
return pos + i;
}
static char *handle_features_dir(const char *filename, char **buffer, int size,
char *pos)
{
DIR *dir = NULL;
char *dirent_path = NULL;
struct dirent *dirent;
struct stat my_stat;
int len;
PDEBUG("Opened features directory \"%s\"\n", filename);
if (!(dir = opendir(filename))) {
PDEBUG("opendir failed '%s'", filename);
exit(1);
}
while ((dirent = readdir(dir)) != NULL) {
int name_len;
/* skip dotfiles silently. */
if (dirent->d_name[0] == '.')
continue;
if (dirent_path)
free(dirent_path);
if (asprintf(&dirent_path, "%s/%s", filename, dirent->d_name) < 0)
{
PERROR(_("Memory allocation error."));
exit(1);
}
name_len = strlen(dirent->d_name);
if (!name_len)
continue;
if (stat(dirent_path, &my_stat)) {
PERROR(_("stat failed for '%s'"), dirent_path);
exit(1);
}
pos = snprintf_buffer(*buffer, pos, size, "%s {", dirent->d_name);
if (S_ISREG(my_stat.st_mode)) {
int file;
int remaining = size - (pos - *buffer);
if (!(file = open(dirent_path, O_RDONLY))) {
PDEBUG("Could not open '%s' in '%s'", dirent_path, filename);
exit(1);
break;
}
PDEBUG("Opened features \"%s\" in \"%s\"\n", dirent_path, filename);
if (my_stat.st_size > remaining) {
PERROR(_("Feature buffer full."));
exit(1);
}
do {
len = read(file, pos, remaining);
if (len > 0) {
remaining -= len;
pos += len;
*pos = 0;
}
} while (len > 0);
if (len < 0) {
PDEBUG("Error reading feature file '%s'\n",
dirent_path);
exit(1);
}
close(file);
} else if (S_ISDIR(my_stat.st_mode)) {
pos = handle_features_dir(dirent_path, buffer, size,
pos);
if (!pos)
break;
}
pos = snprintf_buffer(*buffer, pos, size, "}\n");
}
if (dirent_path)
free(dirent_path);
closedir(dir);
return pos;
}
/* match_string == NULL --> no match_string available
match_string != NULL --> either a matching string specified on the
command line, or the kernel supplied a match string */
static void get_match_string(void) {
FILE *ms = NULL;
struct stat stat_file;
/* has process_args() already assigned a match string? */
if (match_string)
goto out;
if (stat(FLAGS_FILE, &stat_file) == -1)
goto out;
if (S_ISDIR(stat_file.st_mode)) {
/* if we have a features directory default to */
regex_type = AARE_DFA;
perms_create = 1;
flags_string = malloc(FLAGS_STRING_SIZE);
handle_features_dir(FLAGS_FILE, &flags_string, FLAGS_STRING_SIZE, flags_string);
if (strstr(flags_string, "network"))
kernel_supports_network = 1;
if (strstr(flags_string, "mount"))
kernel_supports_mount = 1;
return;
}
ms = fopen(MATCH_STRING, "r");
if (!ms)
goto out;
@@ -844,24 +716,20 @@ out:
static void get_flags_string(char **flags, char *flags_file) {
char *pos;
FILE *f = NULL;
size_t size;
/* abort if missing or already set */
if (!flags || *flags)
return;
if (!flags || *flags) return;
f = fopen(flags_file, "r");
if (!f)
return;
*flags = malloc(FLAGS_STRING_SIZE);
*flags = malloc(1024);
if (!*flags)
goto fail;
size = fread(*flags, 1, FLAGS_STRING_SIZE - 1, f);
if (!size || ferror(f))
if (!fgets(*flags, 1024, f))
goto fail;
(*flags)[size] = 0;
fclose(f);
pos = strstr(*flags, "change_hat=");
@@ -1052,15 +920,6 @@ int process_profile(int option, char *profilename)
if (retval != 0)
goto out;
/* Test to see if profile is for another namespace, if so disable
* caching for now
* TODO: Add support for caching profiles in an alternate namespace
* TODO: Add support for embedded namespace defines if they aren't
* removed from the language.
*/
if (profile_namespace)
skip_cache = 1;
/* Do secondary test to see if cached binary profile is good,
* instead of checking against a presupplied list of files
* use the timestamps from the files that were parsed.
@@ -1069,14 +928,8 @@ int process_profile(int option, char *profilename)
*/
if ((profilename && option != OPTION_REMOVE) && !force_complain &&
!skip_cache) {
if (cacheloc) {
cachename = strdup(cacheloc);
if (!cachename) {
PERROR(_("Memory allocation error."));
exit(1);
}
} else if (asprintf(&cachename, "%s/%s/%s", basedir, "cache", basename)<0) {
PERROR(_("Memory allocation error."));
if (asprintf(&cachename, "%s/%s/%s", basedir, "cache", basename)<0) {
perror("asprintf");
exit(1);
}
/* Load a binary cache if it exists and is newest */

View File

@@ -17,7 +17,6 @@
/* assistance routines */
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
@@ -37,7 +36,6 @@
#include "parser.h"
#include "parser_yacc.h"
#include "mount.h"
/* #define DEBUG */
#ifdef DEBUG
@@ -55,6 +53,8 @@ struct keyword_table {
};
static struct keyword_table keyword_table[] = {
/* flags */
{"flags", TOK_FLAGS},
/* network */
{"network", TOK_NETWORK},
/* misc keywords */
@@ -73,17 +73,12 @@ static struct keyword_table keyword_table[] = {
{"subset", TOK_SUBSET},
{"audit", TOK_AUDIT},
{"deny", TOK_DENY},
{"profile", TOK_PROFILE},
{"set", TOK_SET},
{"rlimit", TOK_RLIMIT},
{"alias", TOK_ALIAS},
{"rewrite", TOK_ALIAS},
{"ptrace", TOK_PTRACE},
{"file", TOK_FILE},
{"mount", TOK_MOUNT},
{"remount", TOK_REMOUNT},
{"umount", TOK_UMOUNT},
{"unmount", TOK_UMOUNT},
{"pivotroot", TOK_PIVOTROOT},
/* terminate */
{NULL, 0}
};
@@ -402,16 +397,6 @@ char *processunquoted(char *string, int len)
return tmp;
}
char *processid(char *string, int len)
{
/* lexer should never call this fn if len <= 0 */
assert(len > 0);
if (*string == '"')
return processquoted(string, len);
return processunquoted(string, len);
}
/* rewrite a quoted string substituting escaped characters for the
* real thing. Strip the quotes around the string */
@@ -782,20 +767,6 @@ void free_cod_entries(struct cod_entry *list)
free(list);
}
void free_mnt_entries(struct mnt_entry *list)
{
if (!list)
return;
if (list->next)
free_mnt_entries(list->next);
free(list->mnt_point);
free(list->device);
free_value_list(list->dev_type);
free_value_list(list->opts);
free(list);
}
static void debug_base_perm_mask(int mask)
{
if (HAS_MAY_READ(mask))
@@ -933,6 +904,8 @@ void debug_capabilities(struct codomain *cod)
__debug_capabilities(cod->deny_caps, "Deny Caps");
if (cod->quiet_caps != 0ull)
__debug_capabilities(cod->quiet_caps, "Quiet Caps");
if (cod->set_caps != 0ull)
__debug_capabilities(cod->set_caps, "Set Capabilities");
}
void debug_cod_list(struct codomain *cod)
@@ -959,101 +932,6 @@ void debug_cod_list(struct codomain *cod)
dump_policy_hats(cod);
}
struct value_list *new_value_list(char *value)
{
struct value_list *val = calloc(1, sizeof(struct value_list));
if (val)
val->value = value;
return val;
}
void free_value_list(struct value_list *list)
{
struct value_list *next;
while (list) {
next = list->next;
if (list->value)
free(list->value);
free(list);
list = next;
}
}
struct value_list *dup_value_list(struct value_list *list)
{
struct value_list *entry, *dup, *head = NULL;
char *value;
list_for_each(list, entry) {
value = NULL;
if (list->value) {
value = strdup(list->value);
if (!value)
goto fail2;
}
dup = new_value_list(value);
if (!dup)
goto fail;
if (head)
list_append(head, dup);
else
head = dup;
}
return head;
fail:
free(value);
fail2:
free_value_list(head);
return NULL;
}
void print_value_list(struct value_list *list)
{
struct value_list *entry;
if (!list)
return;
fprintf(stderr, "%s", list->value);
list = list->next;
list_for_each(list, entry) {
fprintf(stderr, ", %s", entry->value);
}
}
struct cond_entry *new_cond_entry(char *name, struct value_list *list)
{
struct cond_entry *ent = calloc(1, sizeof(struct cond_entry));
if (ent) {
ent->name = name;
ent->vals = list;
}
return ent;
}
void free_cond_entry(struct cond_entry *ent)
{
if (ent) {
free(ent->name);
free_value_list(ent->vals);
free(ent);
}
}
void print_cond_entry(struct cond_entry *ent)
{
if (ent) {
fprintf(stderr, "%s=(", ent->name);
print_value_list(ent->vals);
fprintf(stderr, ")\n");
}
}
#ifdef UNIT_TEST
int test_str_to_boolean(void)
{

View File

@@ -2,8 +2,8 @@
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
*
* Copyright (c) 2010 - 2012
* Canonical Ltd. (All rights reserved)
* Copyright (c) 2010
* 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
@@ -29,7 +29,6 @@
#define _(s) gettext(s)
#include "parser.h"
#include "mount.h"
#include "parser_yacc.h"
/* #define DEBUG */
@@ -96,26 +95,10 @@ void add_hat_to_policy(struct codomain *cod, struct codomain *hat)
}
}
static int add_entry_to_x_table(struct codomain *cod, char *name)
{
int i;
for (i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++) {
if (!cod->exec_table[i]) {
cod->exec_table[i] = name;
return i;
} else if (strcmp(cod->exec_table[i], name) == 0) {
/* name already in table */
free(name);
return i;
}
}
free(name);
return 0;
}
static int add_named_transition(struct codomain *cod, struct cod_entry *entry)
{
char *name = NULL;
int i;
/* check to see if it is a local transition */
if (!entry->namespace) {
@@ -163,7 +146,18 @@ static int add_named_transition(struct codomain *cod, struct cod_entry *entry)
name = entry->nt_name;
}
return add_entry_to_x_table(cod, name);
for (i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++) {
if (!cod->exec_table[i]) {
cod->exec_table[i] = name;
return i;
} else if (strcmp(cod->exec_table[i], name) == 0) {
/* name already in table */
free(name);
return i;
}
}
free(name);
return 0;
}
void add_entry_to_policy(struct codomain *cod, struct cod_entry *entry)
@@ -181,7 +175,7 @@ void post_process_nt_entries(struct codomain *cod)
int mode = 0;
int n = add_named_transition(cod, entry);
if (!n) {
PERROR("Profile %s has too many specified profile transitions.\n", cod->name);
PERROR("Profile %s has to many specified profile transitions.\n", cod->name);
exit(1);
}
if (entry->mode & AA_USER_EXEC)
@@ -196,32 +190,6 @@ void post_process_nt_entries(struct codomain *cod)
}
}
void post_process_mnt_entries(struct codomain *cod)
{
struct mnt_entry *entry;
list_for_each(cod->mnt_ents, entry) {
if (entry->trans) {
unsigned int mode = 0;
int n = add_entry_to_x_table(cod, entry->trans);
if (!n) {
PERROR("Profile %s has too many specified profile transitions.\n", cod->name);
exit(1);
}
if (entry->allow & AA_USER_EXEC)
mode |= SHIFT_MODE(n << 10, AA_USER_SHIFT);
if (entry->allow & AA_OTHER_EXEC)
mode |= SHIFT_MODE(n << 10, AA_OTHER_SHIFT);
entry->allow = ((entry->allow & ~AA_ALL_EXEC_MODIFIERS) |
(mode & AA_ALL_EXEC_MODIFIERS));
entry->trans = NULL;
}
}
}
static void __merge_rules(const void *nodep, const VISIT value,
const int __unused depth)
{
@@ -326,33 +294,6 @@ int process_hat_regex(struct codomain *cod)
return 0;
}
static void __process_policydb(const void *nodep, const VISIT value,
const int __unused depth)
{
struct codomain **t = (struct codomain **) nodep;
if (value == preorder || value == endorder)
return;
if (process_policydb(*t) != 0) {
PERROR(_("ERROR processing policydb rules for profile %s, failed to load\n"),
(*t)->name);
exit(1);
}
}
int post_process_policydb(void)
{
twalk(policy_list, __process_policydb);
return 0;
}
int process_hat_policydb(struct codomain *cod)
{
twalk(cod->hat_table, __process_policydb);
return 0;
}
static void __process_variables(const void *nodep, const VISIT value,
const int __unused depth)
{
@@ -704,6 +645,7 @@ struct codomain *merge_policy(struct codomain *a, struct codomain *b)
a->audit_caps |= b->audit_caps;
a->deny_caps |= b->deny_caps;
a->quiet_caps |= b->quiet_caps;
a->set_caps |= b->set_caps;
if (a->network_allowed) {
size_t i;
@@ -765,15 +707,6 @@ int post_process_policy(int debug_only)
}
}
if (!debug_only) {
retval = post_process_policydb();
if (retval != 0) {
PERROR(_("%s: Errors found during policydb postprocess. Aborting.\n"),
progname);
return retval;
}
}
return retval;
}
@@ -795,15 +728,10 @@ void free_policy(struct codomain *cod)
return;
free_hat_table(cod->hat_table);
free_cod_entries(cod->entries);
free_mnt_entries(cod->mnt_ents);
if (cod->dfarules)
aare_delete_ruleset(cod->dfarules);
if (cod->dfa)
free(cod->dfa);
if (cod->policy_rules)
aare_delete_ruleset(cod->policy_rules);
if (cod->policy_dfa)
free(cod->policy_dfa);
if (cod->name)
free(cod->name);
if (cod->attachment)

View File

@@ -28,8 +28,6 @@
#include "parser.h"
#include "libapparmor_re/apparmor_re.h"
#include "libapparmor_re/aare_rules.h"
#include "mount.h"
#include "policydb.h"
enum error_type {
e_no_error,
@@ -613,411 +611,6 @@ out:
return error;
}
static int build_list_val_expr(char *buffer, int size, struct value_list *list)
{
struct value_list *ent;
char tmp[PATH_MAX + 3];
char *p;
int len;
pattern_t ptype;
int pos;
if (!list) {
strncpy(buffer, "[^\\000]*", size);
return TRUE;
}
p = buffer;
strncpy(p, "(", size - (p - buffer));
p++;
if (p > buffer + size)
goto fail;
ptype = convert_aaregex_to_pcre(list->value, 0, tmp, PATH_MAX+3, &pos);
if (ptype == ePatternInvalid)
goto fail;
len = strlen(tmp);
if (len > size - (p - buffer))
goto fail;
strcpy(p, tmp);
p += len;
list_for_each(list->next, ent) {
ptype = convert_aaregex_to_pcre(ent->value, 0, tmp,
PATH_MAX+3, &pos);
if (ptype == ePatternInvalid)
goto fail;
strncpy(p, "|", size - (p - buffer));
p++;
len = strlen(tmp);
if (len > size - (p - buffer))
goto fail;
strcpy(p, tmp);
p += len;
}
strncpy(p, ")", size - (p - buffer));
p++;
if (p > buffer + size)
goto fail;
return TRUE;
fail:
return FALSE;
}
static int convert_entry(char *buffer, int size, char *entry)
{
pattern_t ptype;
int pos;
if (entry) {
ptype = convert_aaregex_to_pcre(entry, 0, buffer, size, &pos);
if (ptype == ePatternInvalid)
return FALSE;
} else {
/* match any char except \000 0 or more times */
if (size < 8)
return FALSE;
strcpy(buffer, "[^\\000]*");
}
return TRUE;
}
static int build_mnt_flags(char *buffer, int size, unsigned int flags,
unsigned int inv_flags)
{
char *p = buffer;
int i, len = 0;
if (flags == 0xffffffff) {
/* all flags are optional */
len = snprintf(p, size, "[^\\000]*");
if (len < 0 || len >= size)
return FALSE;
return TRUE;
}
for (i = 0; i <= 31; ++i) {
if ((flags & inv_flags) & (1 << i))
len = snprintf(p, size, "(\\x%02x|)", i + 1);
else if (flags & (1 << i))
len = snprintf(p, size, "\\x%02x", i + 1);
/* else no entry = not set */
if (len < 0 || len >= size)
return FALSE;
p += len;
size -= len;
}
if (buffer == p) {
/* match nothing - use impossible 254 as regex parser doesn't
* like the empty string
*/
if (size < 9)
return FALSE;
strcpy(p, "(\\0xfe|)");
}
return TRUE;
}
static int build_mnt_opts(char *buffer, int size, struct value_list *opts)
{
struct value_list *ent;
char tmp[PATH_MAX + 3];
char *p;
int len;
pattern_t ptype;
int pos;
if (!opts) {
if (size < 8)
return FALSE;
strncpy(buffer, "[^\\000]*", size);
return TRUE;
}
p = buffer;
list_for_each(opts, ent) {
ptype = convert_aaregex_to_pcre(ent->value, 0, tmp,
PATH_MAX+3, &pos);
if (ptype == ePatternInvalid)
goto fail;
len = strlen(tmp);
if (len > size - (p - buffer))
goto fail;
strcpy(p, tmp);
p += len;
if (ent->next && size - (p - buffer) > 1) {
*p++ = ',';
*p = 0;
}
}
return TRUE;
fail:
return FALSE;
}
static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
{
char mntbuf[PATH_MAX + 3];
char devbuf[PATH_MAX + 3];
char typebuf[PATH_MAX + 3];
char flagsbuf[PATH_MAX + 3];
char optsbuf[PATH_MAX + 3];
char *p, *vec[5];
int count = 0;
/* a single mount rule may result in multiple matching rules being
* created in the backend to cover all the possible choices
*/
if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_REMOUNT)
&& !entry->device && !entry->dev_type) {
/* remount can't be conditional on device and type */
p = mntbuf;
/* rule class single byte header */
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
if (entry->mnt_point) {
/* both device && mnt_point or just mnt_point */
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
goto fail;
vec[0] = mntbuf;
} else {
if (!convert_entry(p, PATH_MAX +3, entry->device))
goto fail;
vec[0] = mntbuf;
}
/* skip device */
if (!convert_entry(devbuf, PATH_MAX +3, NULL))
goto fail;
vec[1] = devbuf;
/* skip type */
vec[2] = devbuf;
if (!build_mnt_flags(flagsbuf, PATH_MAX,
entry->flags & MS_REMOUNT_FLAGS,
entry->inv_flags & MS_REMOUNT_FLAGS))
goto fail;
vec[3] = flagsbuf;
if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
goto fail;
vec[4] = optsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
entry->audit, 5, vec, dfaflags))
goto fail;
count++;
}
if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_BIND)
&& !entry->dev_type && !entry->opts) {
/* bind mount rules can't be conditional on dev_type or data */
p = mntbuf;
/* rule class single byte header */
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
goto fail;
vec[0] = mntbuf;
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
goto fail;
vec[1] = devbuf;
if (!convert_entry(typebuf, PATH_MAX +3, NULL))
goto fail;
vec[2] = typebuf;
if (!build_mnt_flags(flagsbuf, PATH_MAX,
entry->flags & MS_BIND_FLAGS,
entry->inv_flags & MS_BIND_FLAGS))
goto fail;
vec[3] = flagsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
entry->audit, 4, vec, dfaflags))
goto fail;
count++;
}
if ((entry->allow & AA_MAY_MOUNT) &&
(entry->flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED))
&& !entry->device && !entry->dev_type && !entry->opts) {
/* change type base rules can not be conditional on device,
* device type or data
*/
p = mntbuf;
/* rule class single byte header */
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
goto fail;
vec[0] = mntbuf;
/* skip device and type */
if (!convert_entry(devbuf, PATH_MAX +3, NULL))
goto fail;
vec[1] = devbuf;
vec[2] = devbuf;
if (!build_mnt_flags(flagsbuf, PATH_MAX,
entry->flags & MS_MAKE_FLAGS,
entry->inv_flags & MS_MAKE_FLAGS))
goto fail;
vec[3] = flagsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
entry->audit, 4, vec, dfaflags))
goto fail;
count++;
}
if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_MOVE)
&& !entry->dev_type && !entry->opts) {
/* mount move rules can not be conditional on dev_type,
* or data
*/
p = mntbuf;
/* rule class single byte header */
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
goto fail;
vec[0] = mntbuf;
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
goto fail;
vec[1] = devbuf;
/* skip type */
if (!convert_entry(typebuf, PATH_MAX +3, NULL))
goto fail;
vec[2] = typebuf;
if (!build_mnt_flags(flagsbuf, PATH_MAX,
entry->flags & MS_MOVE_FLAGS,
entry->inv_flags & MS_MOVE_FLAGS))
goto fail;
vec[3] = flagsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
entry->audit, 4, vec, dfaflags))
goto fail;
count++;
}
if ((entry->allow & AA_MAY_MOUNT) &&
(entry->flags | entry->inv_flags) & ~MS_CMDS) {
/* generic mount if flags are set that are not covered by
* above commands
*/
p = mntbuf;
/* rule class single byte header */
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
goto fail;
vec[0] = mntbuf;
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
goto fail;
vec[1] = devbuf;
if (!build_list_val_expr(typebuf, PATH_MAX+2, entry->dev_type))
goto fail;
vec[2] = typebuf;
if (!build_mnt_flags(flagsbuf, PATH_MAX,
entry->flags & ~MS_CMDS,
entry->inv_flags & ~MS_CMDS))
goto fail;
vec[3] = flagsbuf;
if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
goto fail;
vec[4] = optsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
entry->audit, 5, vec, dfaflags))
goto fail;
count++;
}
if (entry->allow & AA_MAY_UMOUNT) {
p = mntbuf;
/* rule class single byte header */
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
goto fail;
vec[0] = mntbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
entry->audit, 1, vec, dfaflags))
goto fail;
count++;
}
if (entry->allow & AA_MAY_PIVOTROOT) {
p = mntbuf;
/* rule class single byte header */
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
goto fail;
vec[0] = mntbuf;
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
goto fail;
vec[1] = devbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
entry->audit, 2, vec, dfaflags))
goto fail;
count++;
}
if (!count)
/* didn't actually encode anything */
goto fail;
return TRUE;
fail:
PERROR("Enocoding of mount rule failed\n");
return FALSE;
}
int post_process_policydb_ents(struct codomain *cod)
{
int ret = TRUE;
int count = 0;
/* Add fns for rules that should be added to policydb here */
if (cod->mnt_ents && kernel_supports_mount) {
struct mnt_entry *entry;
list_for_each(cod->mnt_ents, entry) {
if (regex_type == AARE_DFA &&
!process_mnt_entry(cod->policy_rules, entry))
ret = FALSE;
count++;
}
} else if (cod->mnt_ents && !kernel_supports_mount)
pwarn("profile %s mount rules not enforced\n", cod->name);
cod->policy_rule_count = count;
return ret;
}
int process_policydb(struct codomain *cod)
{
int error = -1;
if (regex_type == AARE_DFA) {
cod->policy_rules = aare_new_ruleset(0);
if (!cod->policy_rules)
goto out;
}
if (!post_process_policydb_ents(cod))
goto out;
if (regex_type == AARE_DFA && cod->policy_rule_count > 0) {
cod->policy_dfa = aare_create_dfa(cod->policy_rules,
&cod->policy_dfa_size,
dfaflags);
aare_delete_ruleset(cod->policy_rules);
cod->policy_rules = NULL;
if (!cod->policy_dfa)
goto out;
}
aare_reset_matchflags();
if (process_hat_policydb(cod) != 0)
goto out;
error = 0;
out:
return error;
}
void reset_regex(void)
{
aare_reset_matchflags();

View File

@@ -28,7 +28,6 @@
/* #define DEBUG */
#include "parser.h"
#include "mount.h"
static inline char *get_var_end(char *var)
{
@@ -131,19 +130,17 @@ void free_var_string(struct var_string *var)
free(var);
}
/* doesn't handle variables in options atm */
static int expand_entry_variables(char **name, void *entry,
int (dup_and_chain)(void *))
static int expand_entry_variables(struct cod_entry *entry)
{
struct set_value *valuelist;
int ret = TRUE;
char *value;
struct var_string *split_var;
if (!entry) /* can happen when entry is optional */
if (!entry) /* shouldn't happen */
return ret;
while ((split_var = split_out_var(*name))) {
while ((split_var = split_out_var(entry->name))) {
valuelist = get_set_var(split_var->var);
if (!valuelist) {
int boolean = get_boolean_var(split_var->var);
@@ -162,22 +159,24 @@ static int expand_entry_variables(char **name, void *entry,
split_var->var);
exit(1);
}
free(*name);
if (asprintf(name, "%s%s%s",
free(entry->name);
if (asprintf(&(entry->name), "%s%s%s",
split_var->prefix ? split_var->prefix : "",
value,
split_var->suffix ? split_var->suffix : "") == -1)
return FALSE;
while ((value = get_next_set_value(&valuelist))) {
if (!dup_and_chain(entry)) {
PERROR("Memory allocation error while handling set variable %s\n",
struct cod_entry *dupe = copy_cod_entry(entry);
if (!dupe) {
PERROR("Memory allocaton error while handling set variable %s\n",
split_var->var);
exit(1);
}
entry->next = dupe;
free(*name);
if (asprintf(name, "%s%s%s",
free(entry->name);
if (asprintf(&(entry->name), "%s%s%s",
split_var->prefix ? split_var->prefix : "", value,
split_var->suffix ? split_var->suffix : "") == -1)
return FALSE;
@@ -188,66 +187,15 @@ static int expand_entry_variables(char **name, void *entry,
return ret;
}
int clone_and_chain_cod(void *v)
{
struct cod_entry *entry = v;
struct cod_entry *dup = copy_cod_entry(entry);
if (!dup)
return 0;
entry->next = dup;
return 1;
}
int clone_and_chain_mnt(void *v)
{
struct mnt_entry *entry = v;
struct mnt_entry *dup = dup_mnt_entry(entry);
if (!dup)
return 0;
entry->next = dup;
return 1;
}
static int process_variables_in_entries(struct cod_entry *entry_list)
{
int ret = TRUE, rc;
struct cod_entry *entry;
list_for_each(entry_list, entry) {
rc = expand_entry_variables(&entry->name, entry,
clone_and_chain_cod);
rc = expand_entry_variables(entry);
if (!rc)
return FALSE;
}
return ret;
}
/* does not currently support expansion of vars in options */
static int process_variables_in_mnt_entries(struct mnt_entry *entry_list)
{
int ret = TRUE, rc;
struct mnt_entry *entry;
list_for_each(entry_list, entry) {
rc = expand_entry_variables(&entry->mnt_point, entry,
clone_and_chain_mnt);
if (!rc)
return FALSE;
rc = expand_entry_variables(&entry->device, entry,
clone_and_chain_mnt);
if (!rc)
return FALSE;
rc = expand_entry_variables(&entry->trans, entry,
clone_and_chain_mnt);
if (!rc)
return FALSE;
ret = FALSE;
}
return ret;
@@ -261,10 +209,6 @@ int process_variables(struct codomain *cod)
error = -1;
}
if (!process_variables_in_mnt_entries(cod->mnt_ents)) {
error = -1;
}
if (process_hat_variables(cod) != 0) {
error = -1;
}

View File

@@ -2,8 +2,8 @@
/*
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
* Copyright (c) 2010-2012
* Canonical Ltd.
* Copyright (c) 2010
* Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
@@ -32,7 +32,6 @@
/* #define DEBUG */
#include "parser.h"
#include "mount.h"
#include "parser_include.h"
#include <unistd.h>
#include <netinet/in.h>
@@ -65,23 +64,21 @@
#define CAP_TO_MASK(x) (1ull << (x))
int parser_token = 0;
struct value_list {
char *value;
struct value_list *next;
};
void free_value_list(struct value_list *list);
struct cod_entry *do_file_rule(char *namespace, char *id, int mode,
char *link_id, char *nt);
struct mnt_entry *do_mnt_rule(struct cond_entry *src_conds, char *src,
struct cond_entry *dst_conds, char *dst,
int mode);
struct mnt_entry *do_pivot_rule(struct cond_entry *old, char *root,
char *transition);
void add_local_entry(struct codomain *cod);
%}
%token TOK_ID
%token TOK_CONDID
%token TOK_CARET
%token TOK_SEP
%token TOK_OPEN
%token TOK_CLOSE
%token TOK_MODE
@@ -113,14 +110,6 @@ void add_local_entry(struct codomain *cod);
%token TOK_SET
%token TOK_ALIAS
%token TOK_PTRACE
%token TOK_OPENPAREN
%token TOK_CLOSEPAREN
%token TOK_COMMA
%token TOK_FILE
%token TOK_MOUNT
%token TOK_REMOUNT
%token TOK_UMOUNT
%token TOK_PIVOTROOT
/* rlimits */
%token TOK_RLIMIT
@@ -147,6 +136,10 @@ void add_local_entry(struct codomain *cod);
/* debug flag values */
%token TOK_FLAGS
%token TOK_FLAG_OPENPAREN
%token TOK_FLAG_CLOSEPAREN
%token TOK_FLAG_SEP
%token TOK_FLAG_ID
%union {
char *id;
@@ -156,8 +149,6 @@ void add_local_entry(struct codomain *cod);
struct codomain *cod;
struct cod_net_entry *net_entry;
struct cod_entry *user_entry;
struct mnt_entry *mnt_entry;
struct flagval flags;
int fmode;
uint64_t cap;
@@ -166,13 +157,11 @@ void add_local_entry(struct codomain *cod);
char *bool_var;
char *var_val;
struct value_list *val_list;
struct cond_entry *cond_entry;
int boolean;
struct named_transition transition;
}
%type <id> TOK_ID
%type <id> TOK_CONDID
%type <mode> TOK_MODE
%type <fmode> file_mode
%type <cod> profile_base
@@ -183,19 +172,14 @@ void add_local_entry(struct codomain *cod);
%type <cod> cond_rule
%type <network_entry> network_rule
%type <user_entry> rule
%type <user_entry> file_rule
%type <user_entry> file_rule_tail
%type <user_entry> link_rule
%type <user_entry> ptrace_rule
%type <user_entry> frule
%type <mnt_entry> mnt_rule
%type <cond_entry> opt_conds
%type <cond_entry> cond
%type <flags> flags
%type <flags> flagvals
%type <flags> flagval
%type <flag_id> TOK_FLAG_ID
%type <cap> caps
%type <cap> capability
%type <cap> set_caps
%type <user_entry> change_profile
%type <set_var> TOK_SET_VAR
%type <bool_var> TOK_BOOL_VAR
@@ -207,12 +191,10 @@ void add_local_entry(struct codomain *cod);
%type <boolean> opt_audit_flag
%type <boolean> opt_owner_flag
%type <boolean> opt_profile_flag
%type <boolean> opt_flags
%type <id> opt_namespace
%type <id> opt_id
%type <transition> opt_named_transition
%type <boolean> opt_unsafe
%type <boolean> opt_file
%%
@@ -257,7 +239,6 @@ profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE
cod->flags.complain = 1;
post_process_nt_entries(cod);
post_process_mnt_entries(cod);
PDEBUG("%s: flags='%s%s'\n",
$2,
cod->flags.complain ? "complain, " : "",
@@ -388,23 +369,26 @@ varassign: TOK_BOOL_VAR TOK_EQUALS TOK_VALUE
valuelist: TOK_VALUE
{
struct value_list *val = new_value_list($1);
if (!val)
struct value_list *new = calloc(1, sizeof(struct value_list));
if (!new)
yyerror(_("Memory allocation error."));
PDEBUG("Matched: value (%s)\n", $1);
$$ = val;
new->value = $1;
new->next = NULL;
$$ = new;
}
valuelist: valuelist TOK_VALUE
{
struct value_list *val = new_value_list($2);
if (!val)
struct value_list *new = calloc(1, sizeof(struct value_list));
if (!new)
yyerror(_("Memory allocation error."));
PDEBUG("Matched: value list\n");
list_append($1, val);
$$ = $1;
new->value = $2;
new->next = $1;
$$ = new;
}
flags: { /* nothing */
@@ -413,32 +397,25 @@ flags: { /* nothing */
$$ = fv;
};
opt_flags: { /* nothing */ $$ = 0; }
| TOK_CONDID TOK_EQUALS
flags: TOK_FLAGS TOK_EQUALS TOK_FLAG_OPENPAREN flagvals TOK_FLAG_CLOSEPAREN
{
if (strcmp($1, "flags") != 0)
yyerror("expected flags= got %s=", $1);
$$ = 1;
}
flags: opt_flags TOK_OPENPAREN flagvals TOK_CLOSEPAREN
{
$$ = $3;
$$ = $4;
};
flagvals: flagvals flagval
flags: TOK_FLAG_OPENPAREN flagvals TOK_FLAG_CLOSEPAREN
{
$1.complain = $1.complain || $2.complain;
$1.audit = $1.audit || $2.audit;
$1.path = $1.path | $2.path;
$$ = $2;
}
flagvals: flagvals TOK_FLAG_SEP flagval
{
$1.complain = $1.complain || $3.complain;
$1.audit = $1.audit || $3.audit;
$1.path = $1.path | $3.path;
if (($1.path & (PATH_CHROOT_REL | PATH_NS_REL)) ==
(PATH_CHROOT_REL | PATH_NS_REL))
yyerror(_("Profile flag chroot_relative conflicts with namespace_relative"));
if (!($1.path & PATH_NS_REL))
/* default to chroot relative profiles */
$1.path |= PATH_CHROOT_REL;
if (($1.path & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED)) ==
(PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))
yyerror(_("Profile flag mediate_deleted conflicts with delegate_deleted"));
@@ -457,7 +434,7 @@ flagvals: flagval
$$ = $1;
};
flagval: TOK_VALUE
flagval: TOK_FLAG_ID
{
struct flagval fv = { 0, 0, 0, 0 };
if (strcmp($1, "debug") == 0) {
@@ -664,25 +641,6 @@ rules: rules opt_audit_flag network_rule
$$ = $1;
}
rules: rules opt_audit_flag TOK_DENY mnt_rule
{
$4->deny = $4->allow;
if ($2)
$4->audit = $4->allow;
$4->next = $1->mnt_ents;
$1->mnt_ents = $4;
$$ = $1;
}
rules: rules opt_audit_flag mnt_rule
{
if ($2)
$3->audit = $3->allow;
$3->next = $1->mnt_ents;
$1->mnt_ents = $3;
$$ = $1;
}
rules: rules change_profile
{
PDEBUG("matched: rules change_profile\n");
@@ -709,6 +667,12 @@ rules: rules opt_audit_flag capability
$$ = $1;
};
rules: rules set_caps
{
$1->set_caps |= $2;
$$ = $1;
};
rules: rules hat
{
PDEBUG("Matched: hat rule\n");
@@ -928,16 +892,24 @@ opt_named_transition:
$$.name = $5;
};
rule: file_rule { $$ = $1; }
| link_rule { $$ = $1; }
| ptrace_rule {$$ = $1; }
opt_unsafe: { /* nothing */ $$ = 0; }
| TOK_UNSAFE { $$ = 1; };
| TOK_SAFE { $$ = 2; };
opt_file: { /* nothing */ $$ = 0; }
| TOK_FILE { $$ = 1; }
rule: opt_unsafe frule
{
if ($1) {
if (!($2->mode & AA_EXEC_BITS))
yyerror(_("unsafe rule missing exec permissions"));
if ($1 == 1) {
$2->mode |= (($2->mode & AA_EXEC_BITS) << 8) &
ALL_AA_EXEC_UNSAFE;
}
else if ($1 == 2)
$2->mode &= ~ALL_AA_EXEC_UNSAFE;
}
$$ = $2;
};
frule: id_or_var file_mode opt_named_transition TOK_END_OF_RULE
{
@@ -961,45 +933,16 @@ frule: file_mode opt_subset_flag id_or_var opt_named_transition TOK_END_OF_RULE
}
};
file_rule: TOK_FILE TOK_END_OF_RULE
{
char *path = strdup("/**");
int perms = ((AA_BASE_PERMS & ~AA_EXEC_TYPE) |
(AA_EXEC_INHERIT | AA_MAY_EXEC));
/* duplicate to other permission set */
perms |= perms << AA_OTHER_SHIFT;
if (!path)
yyerror(_("Memory allocation error."));
$$ = do_file_rule(NULL, path, perms, NULL, NULL);
}
| opt_file file_rule_tail { $$ = $2; }
file_rule_tail: opt_unsafe frule
{
if ($1) {
if (!($2->mode & AA_EXEC_BITS))
yyerror(_("unsafe rule missing exec permissions"));
if ($1 == 1) {
$2->mode |= (($2->mode & AA_EXEC_BITS) << 8) &
ALL_AA_EXEC_UNSAFE;
}
else if ($1 == 2)
$2->mode &= ~ALL_AA_EXEC_UNSAFE;
}
$$ = $2;
};
file_rule_tail: opt_unsafe id_or_var file_mode id_or_var
rule: opt_unsafe id_or_var file_mode id_or_var
{
/* Oopsie, we appear to be missing an EOL marker. If we
* were *smart*, we could work around it. Since we're
* obviously not smart, we'll just punt with a more
* sensible error. */
yyerror(_("missing an end of line character? (entry: %s)"), $2);
yyerror(_("missing an end of line character? (entry: %s)"), $1);
};
link_rule: TOK_LINK opt_subset_flag TOK_ID TOK_ARROW TOK_ID TOK_END_OF_RULE
rule: TOK_LINK opt_subset_flag TOK_ID TOK_ARROW TOK_ID TOK_END_OF_RULE
{
struct cod_entry *entry;
PDEBUG("Matched: link tok_id (%s) -> (%s)\n", $3, $5);
@@ -1009,7 +952,7 @@ link_rule: TOK_LINK opt_subset_flag TOK_ID TOK_ARROW TOK_ID TOK_END_OF_RULE
$$ = entry;
};
ptrace_rule: TOK_PTRACE TOK_ID TOK_END_OF_RULE
rule: TOK_PTRACE TOK_ID TOK_END_OF_RULE
{
struct cod_entry *entry;
entry = new_entry(NULL, $2, AA_USER_PTRACE | AA_OTHER_PTRACE, NULL);
@@ -1018,7 +961,7 @@ ptrace_rule: TOK_PTRACE TOK_ID TOK_END_OF_RULE
$$ = entry;
};
ptrace_rule: TOK_PTRACE TOK_COLON TOK_ID TOK_COLON TOK_ID TOK_END_OF_RULE
rule: TOK_PTRACE TOK_COLON TOK_ID TOK_COLON TOK_ID TOK_END_OF_RULE
{
struct cod_entry *entry;
entry = new_entry($3, $5, AA_USER_PTRACE | AA_OTHER_PTRACE, NULL);
@@ -1066,67 +1009,7 @@ network_rule: TOK_NETWORK TOK_ID TOK_ID TOK_END_OF_RULE
$$ = entry;
}
cond: TOK_CONDID TOK_EQUALS TOK_VALUE
{
struct cond_entry *ent;
struct value_list *value = new_value_list($3);
if (!value)
yyerror(_("Memory allocation error."));
ent = new_cond_entry($1, value);
if (!ent) {
free_value_list(value);
yyerror(_("Memory allocation error."));
}
$$ = ent;
}
cond: TOK_CONDID TOK_EQUALS TOK_OPENPAREN valuelist TOK_CLOSEPAREN
{
struct cond_entry *ent = new_cond_entry($1, $4);
if (!ent)
yyerror(_("Memory allocation error."));
$$ = ent;
}
opt_conds: { /* nothing */ $$ = NULL; }
| opt_conds cond
{
$2->next = $1;
$$ = $2;
}
mnt_rule: TOK_MOUNT opt_conds opt_id TOK_END_OF_RULE
{
$$ = do_mnt_rule($2, $3, NULL, NULL, AA_MAY_MOUNT);
}
mnt_rule: TOK_MOUNT opt_conds opt_id TOK_ARROW opt_conds TOK_ID TOK_END_OF_RULE
{
$$ = do_mnt_rule($2, $3, $5, $6, AA_MAY_MOUNT);
}
mnt_rule: TOK_REMOUNT opt_conds opt_id TOK_END_OF_RULE
{
$$ = do_mnt_rule($2, NULL, NULL, $3, AA_DUMMY_REMOUNT);
}
mnt_rule: TOK_UMOUNT opt_conds opt_id TOK_END_OF_RULE
{
$$ = do_mnt_rule($2, NULL, NULL, $3, AA_MAY_UMOUNT);
}
mnt_rule: TOK_PIVOTROOT opt_conds opt_id TOK_END_OF_RULE
{
$$ = do_pivot_rule($2, $3, NULL);
}
mnt_rule: TOK_PIVOTROOT opt_conds opt_id TOK_ARROW TOK_ID TOK_END_OF_RULE
{
$$ = do_pivot_rule($2, $3, $5);
}
hat_start: TOK_CARET {}
hat_start: TOK_SEP {}
| TOK_HAT {}
file_mode: TOK_MODE
@@ -1160,17 +1043,17 @@ change_profile: TOK_CHANGE_PROFILE TOK_ARROW TOK_COLON TOK_ID TOK_COLON TOK_ID T
};
capability: TOK_CAPABILITY caps TOK_END_OF_RULE
set_caps: TOK_SET TOK_CAPABILITY caps TOK_END_OF_RULE
{
if ($2 == 0) {
/* bare capability keyword - set all caps */
$$ = 0xffffffffffffffff;
} else
$$ = $2;
$$ = $3;
};
caps: { /* nothing */ $$ = 0; }
| caps TOK_ID
capability: TOK_CAPABILITY caps TOK_END_OF_RULE
{
$$ = $2;
};
caps: caps TOK_ID
{
int cap = name_to_capability($2);
if (cap == -1)
@@ -1179,14 +1062,26 @@ caps: { /* nothing */ $$ = 0; }
$$ = $1 | CAP_TO_MASK(cap);
}
caps: TOK_ID
{
int cap = name_to_capability($1);
if (cap == -1)
yyerror(_("Invalid capability %s."), $1);
free($1);
$$ = CAP_TO_MASK(cap);
};
%%
#define MAXBUFSIZE 4096
void vprintyyerror(const char *msg, va_list argptr)
void yyerror(char *msg, ...)
{
va_list arg;
char buf[MAXBUFSIZE];
vsnprintf(buf, sizeof(buf), msg, argptr);
va_start(arg, msg);
vsnprintf(buf, sizeof(buf), msg, arg);
va_end(arg);
if (profilename) {
PERROR(_("AppArmor parser error for %s%s%s at line %d: %s\n"),
@@ -1200,28 +1095,23 @@ void vprintyyerror(const char *msg, va_list argptr)
current_filename ? current_filename : "",
current_lineno, buf);
}
}
void printyyerror(const char *msg, ...)
{
va_list arg;
va_start(arg, msg);
vprintyyerror(msg, arg);
va_end(arg);
}
void yyerror(const char *msg, ...)
{
va_list arg;
va_start(arg, msg);
vprintyyerror(msg, arg);
va_end(arg);
exit(1);
}
void free_value_list(struct value_list *list)
{
struct value_list *next;
while (list) {
next = list->next;
if (list->value)
free(list->value);
free(list);
list = next;
}
}
struct cod_entry *do_file_rule(char *namespace, char *id, int mode,
char *link_id, char *nt)
{
@@ -1259,74 +1149,3 @@ void add_local_entry(struct codomain *cod)
add_entry_to_policy(cod, entry);
}
}
static char *mnt_cond_msg[] = {"",
" not allowed as source conditional",
" not allowed as target conditional",
"",
NULL};
int verify_mnt_conds(struct cond_entry *conds, int src)
{
struct cond_entry *entry;
int error = 0;
if (!conds)
return 0;
list_for_each(conds, entry) {
int res = is_valid_mnt_cond(entry->name, src);
if (res <= 0) {
printyyerror(_("invalid mount conditional %s%s"),
entry->name,
res == -1 ? "" : mnt_cond_msg[src]);
error++;
}
}
return error;
}
struct mnt_entry *do_mnt_rule(struct cond_entry *src_conds, char *src,
struct cond_entry *dst_conds, char *dst,
int mode)
{
struct mnt_entry *ent;
if (verify_mnt_conds(src_conds, MNT_SRC_OPT) != 0)
yyerror(_("bad mount rule"));
/* FIXME: atm conditions are not supported on dst
if (verify_conds(dst_conds, DST_OPT) != 0)
yyerror(_("bad mount rule"));
*/
if (dst_conds)
yyerror(_("mount point conditions not currently supported"));
ent = new_mnt_entry(src_conds, src, dst_conds, dst, mode);
if (!ent) {
yyerror(_("Memory allocation error."));
}
return ent;
}
struct mnt_entry *do_pivot_rule(struct cond_entry *old, char *root,
char *transition)
{
struct mnt_entry *ent = NULL;
if (old) {
if (strcmp(old->name, "oldroot") != 0)
yyerror(_("invalid pivotroot conditional '%s'"), old->name);
}
ent = new_mnt_entry(NULL, old->vals->value, NULL, root,
AA_MAY_PIVOTROOT);
ent->trans = transition;
old->vals->value = NULL;
free_cond_entry(old);
return ent;
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2012 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 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.
*
*/
#ifndef __AA_POLICYDB_H
#define __AA_POLICYDB_H
/*
* Class of mediation types in the AppArmor policy db
*/
#define AA_CLASS_COND 0
#define AA_CLASS_UNKNOWN 1
#define AA_CLASS_FILE 2
#define AA_CLASS_CAP 3
#define AA_CLASS_NET 4
#define AA_CLASS_RLIMITS 5
#define AA_CLASS_DOMAIN 6
#define AA_CLASS_MOUNT 7
#define AA_CLASS_NS_DOMAIN 8
#define AA_CLASS_PTRACE 9
#define AA_CLASS_ENV 16
#define AA_CLASS_DBUS 32
#define AA_CLASS_X 33
#endif /* __AA_POLICYDB_H */

View File

@@ -1,7 +1,7 @@
#!/bin/sh
# ----------------------------------------------------------------------
# Copyright (c) 1999-2008 NOVELL (All rights reserved)
# Copyright (c) 2009-2012 Canonical Ltd. (All rights reserved)
# Copyright (c) 2009-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

View File

@@ -3,7 +3,7 @@
# 2008, 2009
# NOVELL (All rights reserved)
#
# Copyright (c) 2010 - 2012
# Copyright (c) 2010
# Canonical Ltd. (All rights reserved)
#
# This program is free software; you can redistribute it and/or

View File

@@ -12,8 +12,8 @@ endif
all: tests
.PHONY: tests error_output gen_xtrans parser_sanity caching minimize
tests: error_output gen_xtrans parser_sanity caching minimize
.PHONY: tests error_output gen_xtrans parser_sanity caching
tests: error_output gen_xtrans parser_sanity caching
GEN_TRANS_DIRS=simple_tests/generated_x/ simple_tests/generated_perms_leading/ simple_tests/generated_perms_safe/
@@ -41,9 +41,6 @@ parser_sanity: $(PARSER)
caching: $(PARSER)
LANG=C ./caching.sh
minimize: $(PARSER)
LANG=C ./minimize.sh
$(PARSER):
make -C $(PARSER_DIR) $(PARSER_BIN)

View File

@@ -49,34 +49,11 @@ echo -n "Profiles are cached when requested: "
[ ! -f $basedir/cache/$profile ] && echo "FAIL ($basedir/cache/$profile does not exist)" && exit 1
echo "ok"
read_features_dir()
{
directory="$1"
if [ ! -d "$directory" ] ; then
return
fi
for f in `ls -AU "$directory"` ; do
if [ -f "$directory/$f" ] ; then
read -r -d "" KF < "$directory/$f" || true
echo -e "$f {$KF\n}"
elif [ -d "$directory/$f" ] ; then
echo -n "$f {"
KF=`read_features_dir "$directory/$f" "$KF"` || true
echo "$KF"
echo -e "}"
fi
done
}
echo -n "Kernel features are written to cache: "
[ ! -f $basedir/cache/.features ] && echo "FAIL ($basedir/cache/.features missing)" && exit 1
read -r -d "" CF < $basedir/cache/.features || true
if [ -d /sys/kernel/security/apparmor/features ] ; then
KF=`read_features_dir /sys/kernel/security/apparmor/features`
else
read -r -d "" KF < /sys/kernel/security/apparmor/features || true
fi
[ "$CF" != "$KF" ] && echo -e "FAIL (feature text mismatch:\n cache '$CF'\nvs\n kernel '$KF')" && exit 1
read CF < $basedir/cache/.features || true
read KF < /sys/kernel/security/apparmor/features || true
[ "$CF" != "$KF" ] && echo "FAIL (feature text mismatch: cache '$CF' vs kernel '$KF')" && exit 1
echo "ok"
echo -n "Cache is loaded when it exists and features match: "

View File

@@ -1,199 +0,0 @@
#!/bin/bash
# Format of -D dfa-states
# dfa-states output is split into 2 parts:
# the accept state infomation
# {state} (allow deny audit XXX) ignore XXX for now
# followed by the transition table information
# {Y} -> {Z}: 0xXX Char #0xXX is the hex dump of Char
# where the start state is always shown as
# {1} <==
#
# Eg. echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, deny /** w, }" | ./apparmor_parser -QT -O minimize -D dfa-states --quiet
#
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 0/2800a/0/2800a)
# {4} (0x 10004/2800a/0/2800a)
# {7} (0x 40010/2800a/0/2800a)
# {8} (0x 80020/2800a/0/2800a)
# {9} (0x 100040/2800a/0/2800a)
# {c} (0x 40030/0/0/0)
#
# {1} -> {2}: 0x2f /
# {2} -> {4}: 0x61 a
# {2} -> {3}: 0x62 b
# {2} -> {3}: 0x63 c
# {2} -> {7}: 0x64 d
# {2} -> {8}: 0x65 e
# {2} -> {9}: 0x66 f
# {2} -> {3}: [^\0x0/]
# {3} -> {3}: [^\0x0]
# {4} -> {3}: [^\0x0]
# {7} -> {a}: 0x0
# {7} -> {3}: []
# {8} -> {3}: [^\0x0]
# {9} -> {3}: [^\0x0]
# {a} -> {b}: 0x2f /
# {b} -> {c}: [^/]
# {c} -> {c}: []
#
# These tests currently only look at the accept state permissions
#
# To view any of these DFAs as graphs replace --D dfa-states with -D dfa-graph
# strip of the test stuff around the parser command and use the the dot
# command to convert
# Eg.
# echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, deny /** w, }" | ./apparmor_parser -QT -O minimize -D dfa-graph --quiet 2>min.graph
# dot -T png -o min.png min.graph
# and then view min.png in your favorite viewer
#
#------------------------------------------------------------------------
# test to see if minimization is eliminating non-redundant accept state
# Test xtrans and regular perms separately. The are the same basic failure
# but can xtrans has an extra code path.
#
# The permission test is setup to have all the none xtrans permissions show
# up once on unique paths and have a global write permission that adds to
# it.
# This should result in a un-minimized dump looking like. Notice it has 6
# states with accept information, 1 for each rule except for the 'w'
# permission which is combined into a single state for /b and /**
#
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 2800a/0/0/0)
# {4} (0x 3800e/0/0/0)
# {5} (0x 6801a/0/0/0)
# {6} (0x a802a/0/0/0)
# {7} (0x 12804a/0/0/0)
# {a} (0x 40030/0/0/0)
# A dump of minimization that is not respecting the uniqueness of the
# permissions on the states looks like below. Notice it has only 3 states
# with accept information
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 1b806e/0/0/0)
# {5} (0x 6801a/0/0/0)
# {a} (0x 40030/0/0/0)
echo -n "Minimize profiles basic perms "
if [ `echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, /** w, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '(.*)$' | wc -l` -ne 6 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# same test as above except with audit perms added
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 2800a/0/2800a/0)
# {4} (0x 3800e/0/2800a/0)
# {7} (0x 6801a/0/2800a/0)
# {8} (0x a802a/0/2800a/0)
# {9} (0x 12804a/0/2800a/0)
# {c} (0x 40030/0/0/0)
echo -n "Minimize profiles audit perms "
if [ `echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit /** w, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '(.*)$' | wc -l` -ne 6 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# same test as above except with deny 'w' perm added to /**, this does not
# elimnates the states with 'w' and 'a' because the quiet information is
# being carried
#
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 0/2800a/0/2800a)
# {4} (0x 10004/2800a/0/2800a)
# {7} (0x 40010/2800a/0/2800a)
# {8} (0x 80020/2800a/0/2800a)
# {9} (0x 100040/2800a/0/2800a)
# {c} (0x 40030/0/0/0)
echo -n "Minimize profiles deny perms "
if [ `echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, deny /** w, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '(.*)$' | wc -l` -ne 6 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# same test as above except with audit deny 'w' perm added to /**, with the
# parameter this elimnates the states with 'w' and 'a' because
# the quiet information is NOT being carried
#
# {1} <== (allow/deny/audit/quiet)
# {4} (0x 10004/0/0/0)
# {7} (0x 40010/0/0/0)
# {8} (0x 80020/0/0/0)
# {9} (0x 100040/0/0/0)
# {c} (0x 40030/0/0/0)
echo -n "Minimize profiles audit deny perms "
if [ `echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '(.*)$' | wc -l` -ne 6 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# The x transition test profile is setup so that there are 3 conflicting x
# permissions, two are on paths that won't collide during dfa creation. The
# 3rd is a generic permission that should be overriden during dfa creation.
#
# This should result in a dfa that specifies transitions on 'a' and 'b' to
# unique states that store the alternate accept information. However
# minimization can remove the unique accept permission states if x permissions
# are treated as a single accept state.
#
# The minimized dump should retain the 'a' and 'b' transitions accept states.
# notice the below dump has 3 states with accept information {3}, {4}, {5}
#
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 2914a45/0/0/0)
# {4} (0x 4115045/0/0/0)
# {5} (0x 2514945/0/0/0)
#
# A dump of minimization that is not respecting the uniqueness of the
# permissions on the states transitioned to by 'a' and 'b' looks like
# below. Notice that only state {3} has accept information
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 2514945/0/0/0)
#
echo -n "Minimize profiles xtrans "
if [ `echo "/t { /b px, /* Pixr, /a Cx -> foo, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '(.*)$' | wc -l` -ne 3 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# same test as above + audit
echo -n "Minimize profiles audit xtrans "
if [ `echo "/t { /b px, audit /* Pixr, /a Cx -> foo, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '(.*)$' | wc -l` -ne 3 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# now try denying x and make sure perms are cleared
# notice that only deny and quiet information is being carried
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 0/fe17f85/0/14005)
echo -n "Minimize profiles deny xtrans "
if [ `echo "/t { /b px, deny /* xr, /a Cx -> foo, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '(.*)$' | wc -l` -ne 1 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# now try audit + denying x and make sure perms are cleared
# notice that the deny info is being carried, by an artifical trap state
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 0/fe17f85/0/0)
echo -n "Minimize profiles audit deny xtrans "
if [ `echo "/t { /b px, audit deny /* xr, /a Cx -> foo, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '(.*)$' | wc -l` -ne 1 ] ; then
echo "failed"
exit 1;
fi
echo "ok"

View File

@@ -1,9 +0,0 @@
#
#=DESCRIPTION fail CAP_XXX syntax.
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist {
capability chown CAP_CHOWN,
}

View File

@@ -1,9 +0,0 @@
#
#=DESCRIPTION fail unknown keyword
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist {
capability chown foobar,
}

View File

@@ -1,9 +0,0 @@
#
#=DESCRIPTION validate some uses of capabilties.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist {
capability,
}

View File

@@ -1,6 +1,6 @@
#
#=DESCRIPTION validate some uses of capabilties.
#=EXRESULT FAIL
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#

View File

@@ -1,7 +0,0 @@
#
#=Description basic file rule
#=EXRESULT FAIL
#
/usr/bin/foo {
file rw,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION w and a conflict
#=EXRESULT FAIL
#
/usr/bin/foo {
file /a wa,
}

View File

@@ -1,7 +0,0 @@
k#
#=DESCRIPTION comma in pathname
#=EXRESULT FAIL
#
/usr/bin/foo {
file /foobar, r,
}

View File

@@ -1,6 +0,0 @@
#=DESCRIPTION Simple test case for embedded spaces
#=EXRESULT FAIL
/bin/foo {
file /abc\ def r,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION k to be lower case
#=EXRESULT FAIL
#
/usr/bin/foo {
file /bin/ls K,
}

View File

@@ -1,41 +0,0 @@
# vim:syntax=subdomain
# Last Modified: Wed Aug 31 11:14:09 2005
#=DESCRIPTION dos line endings
#=EXRESULT PASS
/usr/lib/RealPlayer10/realplay {
#include <includes/base>
#include <includes/fonts>
file /bin/bash ix,
file /bin/sed ixr,
file /bin/true ixr,
file /etc/opt/gnome/pango/pango.modules r,
file /opt/gnome/lib/gtk-2.0/2.4.0/loaders/* r,
file /opt/gnome/lib/lib*so* r,
file /opt/gnome/lib/pango/1.4.0/modules/* r,
file /opt/gnome/share/icons r,
file /opt/gnome/share/icons/** r,
file /opt/gnome/bin/nautilus rux,
file /root r,
file /root/.Xauthority r,
file /root/.fonts.cache-1 r,
file /root/.realplayerrc rw,
file /home/*/ r,
file /home/*/.Xauthority r,
file /home/*/.fonts.cache-1 r,
file /home/*/.realplayerrc rw,
file /usr/X11R6/lib/Acrobat7/Resource/Font/* r,
file /usr/X11R6/lib/Acrobat7/Resource/Font/PFM/* r,
file /usr/lib/RealPlayer10/** r,
file /usr/lib/RealPlayer10/realplay.bin ixr,
file /usr/lib/jvm/java-1.4.2-sun-1.4.2.06/jre/lib/fonts/** r,
file /usr/lib/ooo-2.0/share/fonts/** r,
file /opt/MozillaFirefox/bin/firefox.sh pxr,
file /opt/MozillaFirefox/lib/firefox-bin pxr,
file /opt/MozillaFirefox/lib/init.d r,
file /usr/bin/opera pxr,
file /usr/share/icons r,
file /usr/share/icons/** r,
file /opt/gnome/share/pixmaps r,
file /opt/gnome/share/pixmaps/** r,
}

View File

@@ -1,24 +0,0 @@
#
#=DESCRIPTION perms before pathname
#=EXRESULT PASS
#
/usr/bin/foo {
file r /foo1,
file w /foo1,
file a /foo1,
file k /foo1,
file m /foo1,
file l /foo1,
file px /foo1,
file Px /foo2,
file ux /foo3,
file Ux /foo4,
file ix /foo5,
file unsafe px /foo6,
file unsafe Px /foo7,
file unsafe ux /foo8,
file unsafe Ux /foo9,
file unsafe ix /foo10,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic file rule
#=EXRESULT PASS
#
/usr/bin/foo {
file /usr/bin/foo r,
}

View File

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

View File

@@ -1,9 +0,0 @@
#
#=DESCRIPTION A simple successful profile
#=EXRESULT PASS
#
/usr/bin/foo {
file /usr/bin/foo r,
file /usr/bin/blah rix,
}

View File

@@ -1,13 +0,0 @@
#
#=DESCRIPTION test append
#=EXRESULT PASS
#
/usr/bin/foo {
file /bin/cat a,
file /bin/true ra,
file /bin/false ma,
file /lib/libc.so la,
file /bin/less ixa,
file /bin/more pxa,
file /a uxa,
}

View File

@@ -1,7 +0,0 @@
#
#=DESCRIPTION carat in pathname
#=EXRESULT PASS
#
/usr/bin/foo {
file /foo^bar r,
}

View File

@@ -1,7 +0,0 @@
#
#=DESCRIPTION trailing carat in pathname
#=EXRESULT PASS
#
/usr/bin/foo {
file /foo/bar^ r,
}

View File

@@ -1,7 +0,0 @@
#
#=DESCRIPTION comma in pathname
#=EXRESULT PASS
#
/usr/bin/foo {
file /foo,bar r,
}

View File

@@ -1,7 +0,0 @@
#
#=DESCRIPTION comma at end of pathname
#=EXRESULT PASS
#
/usr/bin/foo {
file "/foobar," r,
}

View File

@@ -1,6 +0,0 @@
#=DESCRIPTION Simple test case for embedded spaces
#=EXRESULT PASS
/bin/foo {
file "/abc\ def" r,
}

View File

@@ -1,6 +0,0 @@
#=DESCRIPTION Simple test case for embedded spaces
#=EXRESULT PASS
/bin/foo {
file "/abc def" r,
}

View File

@@ -1,6 +0,0 @@
#=DESCRIPTION Simple test case for embedded spaces
#=EXRESULT PASS
"/bin/fo o" {
file "/abc def" r,
}

View File

@@ -1,7 +0,0 @@
#
#=DESCRIPTION carat in pathname
#=EXRESULT PASS
#
/usr/bin/foo {
file /foo[^me]bar r,
}

View File

@@ -1,17 +0,0 @@
#
#=DESCRIPTION k and other perms do not conflict
#=EXRESULT PASS
#
/usr/bin/foo {
file /bin/a k,
file /bin/b rk,
file /bin/c wk,
file /bin/d ak,
file /bin/e lk,
file /bin/e mk,
file /bin/f pxk,
file /bin/g Pxk,
file /bin/h ixk,
file /bin/i uxk,
file /bin/j Uxk,
}

View File

@@ -1,12 +0,0 @@
#
#=DESCRIPTION m and [uUpPi]x do not conflict
#=EXRESULT PASS
#
/usr/bin/foo {
file /bin/cat mix,
file /bin/true mpx,
file /bin/false mux,
file /lib/libc.so rwlm,
file /bin/less mUx,
file /bin/more mPx,
}

View File

@@ -1,14 +0,0 @@
#
#=DESCRIPTION m and [upi]x do not conflict, seperate rules
#=EXRESULT PASS
#
/usr/bin/foo {
file /bin/cat rm,
file /bin/cat ix,
file /bin/true px,
file /bin/true m,
file /bin/false m,
file /bin/false ux,
file /lib/libc.so rwl,
file /lib/libc.so m,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION owner can not follow path name
#=EXRESULT FAIL
#
/usr/bin/foo {
file /foo owner rw,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION owner cannot follow permission
#=EXRESULT FAIL
#
/usr/bin/foo {
file /foo rw owner,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION owner rules must have comma termination
#=EXRESULT FAIL
#
/usr/bin/foo {
owner file /foo rw
file /bar rw,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION owner not allowed after forward perm
#=EXRESULT FAIL
#
/usr/bin/foo {
file rw owner /foo,
}

View File

@@ -1,8 +0,0 @@
#
#=DESCRIPTION owner not allowed after pathname in forward rule
#=EXRESULT FAIL
#
/usr/bin/foo {
file rw /foo owner,
}

View File

@@ -1,9 +0,0 @@
#
#=DESCRIPTION owner block needs } termination
#=EXRESULT FAIL
#
/usr/bin/foo {
owner {
file rw foo,
}

View File

@@ -1,25 +0,0 @@
#
#=DESCRIPTION test owner flag for file rules
#=EXRESULT PASS
#
/usr/bin/foo {
owner file /foo rw,
owner file /foo/** rw,
owner file rw /bar,
owner file rw /bar/**,
owner {
file /one rw,
file /one/** rw,
file rw /two,
file rw /two/**,
}
owner {
}
}

View File

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

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount fstype=(procfs) -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount fstype=(procfs, sysfs) -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount fstype={procfs,sysfs} -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount fstype=(procfs sysfs) none -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount options=(rw) -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount options=(rw, ro) -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount options=(rw ro) -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount options=(rw ro) fstype=procfs -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount options=(rw ro) fstype=(procfs) none -> /foo,
}

View File

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

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount options=(bind) /bar -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount /bar -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
remount /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
umount /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount /**,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount /{foo,bar},
}

View File

@@ -1,10 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
@{mntpnt}=/foo/bar
/usr/bin/foo {
mount @{mntpnt},
}

View File

@@ -1,9 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
@{dev}=/one /two /three /four
/usr/bin/foo {
mount @{dev} -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount none -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount fstype=(procfs sysfs) -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount /dev/bar -> /foo,
}

View File

@@ -1,7 +0,0 @@
#
#=Description basic mount rule
#=EXRESULT PASS
#
/usr/bin/foo {
mount /dev/** -> /foo,
}

View File

@@ -1,13 +1,11 @@
#
#=DESCRIPTION test for conflict resolution in minimization phase of dfa gen
#=EXRESULT PASS
#=EXRESULT FAIL
#=TODO
#
/usr/bin/foo {
/b px,
/* Pixr,
/a Cx -> foo,
# need to build minimal test for this yet
}

View File

@@ -1,11 +0,0 @@
#
#=DESCRIPTION test for conflict resolution in minimization phase of dfa gen
#=EXRESULT FAIL
#=TODO
#
/usr/bin/foo {
/b* px,
/* Pixr,
/a* Cx -> foo,
}

View File

@@ -36,8 +36,6 @@
/usr/lib{,32,64}/dri/** mr,
/usr/lib/@{multiarch}/dri/** mr,
/dev/dri/** rw,
/etc/drirc r,
owner @{HOME}/.drirc r,
# mouse themes
/etc/X11/cursors/ r,

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